From 92ee76d4a926843d0e135aa0c2d9f57504c6876c Mon Sep 17 00:00:00 2001 From: John Crispin Date: Fri, 6 Dec 2019 15:31:03 +0100 Subject: trivial: mac80211: fix indentation Signed-off-by: John Crispin Link: https://lore.kernel.org/r/20191206143103.3645-1-john@phrozen.org Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 7d3f2ced92d1..e172d0c7bf74 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2047,7 +2047,7 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info) #define IEEE80211_HE_OPERATION_ER_SU_DISABLE 0x00010000 #define IEEE80211_HE_OPERATION_6GHZ_OP_INFO 0x00020000 #define IEEE80211_HE_OPERATION_BSS_COLOR_MASK 0x3f000000 -#define IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET 24 +#define IEEE80211_HE_OPERATION_BSS_COLOR_OFFSET 24 #define IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR 0x40000000 #define IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED 0x80000000 -- cgit v1.2.3 From 6989310f5d4327e8595664954edd40a7f99ddd0d Mon Sep 17 00:00:00 2001 From: Pi-Hsun Shih Date: Wed, 4 Dec 2019 16:13:07 +0800 Subject: wireless: Use offsetof instead of custom macro. Use offsetof to calculate offset of a field to take advantage of compiler built-in version when possible, and avoid UBSAN warning when compiling with Clang: ================================================================== UBSAN: Undefined behaviour in net/wireless/wext-core.c:525:14 member access within null pointer of type 'struct iw_point' CPU: 3 PID: 165 Comm: kworker/u16:3 Tainted: G S W 4.19.23 #43 Workqueue: cfg80211 __cfg80211_scan_done [cfg80211] Call trace: dump_backtrace+0x0/0x194 show_stack+0x20/0x2c __dump_stack+0x20/0x28 dump_stack+0x70/0x94 ubsan_epilogue+0x14/0x44 ubsan_type_mismatch_common+0xf4/0xfc __ubsan_handle_type_mismatch_v1+0x34/0x54 wireless_send_event+0x3cc/0x470 ___cfg80211_scan_done+0x13c/0x220 [cfg80211] __cfg80211_scan_done+0x28/0x34 [cfg80211] process_one_work+0x170/0x35c worker_thread+0x254/0x380 kthread+0x13c/0x158 ret_from_fork+0x10/0x18 =================================================================== Signed-off-by: Pi-Hsun Shih Reviewed-by: Nick Desaulniers Link: https://lore.kernel.org/r/20191204081307.138765-1-pihsun@chromium.org Signed-off-by: Johannes Berg --- include/uapi/linux/wireless.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/wireless.h b/include/uapi/linux/wireless.h index 86eca3208b6b..a2c006a364e0 100644 --- a/include/uapi/linux/wireless.h +++ b/include/uapi/linux/wireless.h @@ -74,6 +74,8 @@ #include /* for "struct sockaddr" et al */ #include /* for IFNAMSIZ and co... */ +#include /* for offsetof */ + /***************************** VERSION *****************************/ /* * This constant is used to know the availability of the wireless @@ -1090,8 +1092,7 @@ struct iw_event { /* iw_point events are special. First, the payload (extra data) come at * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second, * we omit the pointer, so start at an offset. */ -#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \ - (char *) NULL) +#define IW_EV_POINT_OFF offsetof(struct iw_point, length) #define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point) - \ IW_EV_POINT_OFF) -- cgit v1.2.3 From 50ff477a8639fa1fbbeecb5a6f2f8b6c5557ecec Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 25 Nov 2019 11:04:37 +0100 Subject: mac80211: add 802.11 encapsulation offloading support This patch adds a new transmit path for hardware that supports 802.11 encapsulation offloading. In those cases 802.3 frames get passed directly to the driver allowing the hardware to handle the encapsulation. Some features such as monitor mode and TKIP would break when encapsulation offloading is enabled. If any of these get enabled, the code will alwyas fallback to the normal sw encapsulation data path. The patch defines a secondary netdev_ops struct that the device gets assigned if 802.11 encap support is available and enabled. The driver needs to enable the support on a per vif basis if it finds that all pre-reqs are meet. Signed-off-by: Vasanthakumar Thiagarajan Signed-off-by: John Crispin Link: https://lore.kernel.org/r/20191125100438.16539-1-john@phrozen.org [reword comments, remove SUPPORTS_80211_ENCAP HW flag, minor cleanups] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 32 +++++++++ net/mac80211/ieee80211_i.h | 9 +++ net/mac80211/iface.c | 67 ++++++++++++++++++ net/mac80211/key.c | 11 +++ net/mac80211/status.c | 71 +++++++++++++++++++ net/mac80211/tx.c | 170 +++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 356 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index aa145808e57a..682fd2f4431b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -826,6 +826,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTRL_AMSDU = BIT(3), IEEE80211_TX_CTRL_FAST_XMIT = BIT(4), IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP = BIT(5), + IEEE80211_TX_CTRL_HW_80211_ENCAP = BIT(6), }; /* @@ -4660,6 +4661,26 @@ static inline void ieee80211_tx_status_ni(struct ieee80211_hw *hw, void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb); +/** + * ieee80211_tx_status_8023 - transmit status callback for 802.3 frame format + * + * Call this function for all transmitted data frames after their transmit + * completion. This callback should only be called for data frames which + * are are using driver's (or hardware's) offload capability of encap/decap + * 802.11 frames. + * + * This function may not be called in IRQ context. Calls to this function + * for a single hardware must be synchronized against each other and all + * calls in the same tx status family. + * + * @hw: the hardware the frame was transmitted by + * @vif: the interface for which the frame was transmitted + * @skb: the frame that was transmitted, owned by mac80211 after this call + */ +void ieee80211_tx_status_8023(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct sk_buff *skb); + /** * ieee80211_report_low_ack - report non-responding station * @@ -6480,5 +6501,16 @@ u32 ieee80211_calc_rx_airtime(struct ieee80211_hw *hw, u32 ieee80211_calc_tx_airtime(struct ieee80211_hw *hw, struct ieee80211_tx_info *info, int len); +/** + * ieee80211_set_hw_80211_encap - enable hardware encapsulation offloading. + * + * This function is used to notify mac80211 that a vif can be passed raw 802.3 + * frames. The driver needs to then handle the 802.11 encapsulation inside the + * hardware or firmware. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @enable: indicate if the feature should be turned on or off + */ +bool ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable); #endif /* MAC80211_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ad15b3be8bb3..e3cf24cb4615 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -984,6 +984,8 @@ struct ieee80211_sub_if_data { } debugfs; #endif + bool hw_80211_encap; + /* must be last, dynamically sized area in this! */ struct ieee80211_vif vif; }; @@ -1762,6 +1764,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev); netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev); void __ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev, u32 info_flags, @@ -1948,6 +1952,11 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid, enum nl80211_band band, u32 txdata_flags); +/* sta_out needs to be checked for ERR_PTR() before using */ +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, + struct sta_info **sta_out); + static inline void ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index af8b09214786..9b833e170c20 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1205,6 +1205,72 @@ static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_get_stats64 = ieee80211_get_stats64, }; +static const struct net_device_ops ieee80211_dataif_8023_ops = { + .ndo_open = ieee80211_open, + .ndo_stop = ieee80211_stop, + .ndo_uninit = ieee80211_uninit, + .ndo_start_xmit = ieee80211_subif_start_xmit_8023, + .ndo_set_rx_mode = ieee80211_set_multicast_list, + .ndo_set_mac_address = ieee80211_change_mac, + .ndo_select_queue = ieee80211_netdev_select_queue, + .ndo_get_stats64 = ieee80211_get_stats64, +}; + +static void __ieee80211_set_hw_80211_encap(struct ieee80211_sub_if_data *sdata, + bool enable) +{ + sdata->dev->netdev_ops = enable ? &ieee80211_dataif_8023_ops : + &ieee80211_dataif_ops; + sdata->hw_80211_encap = enable; +} + +bool ieee80211_set_hw_80211_encap(struct ieee80211_vif *vif, bool enable) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *iter; + struct ieee80211_key *key; + + mutex_lock(&local->iflist_mtx); + list_for_each_entry(iter, &local->interfaces, list) { + struct ieee80211_sub_if_data *disable = NULL; + + if (vif->type == NL80211_IFTYPE_MONITOR) { + disable = iter; + __ieee80211_set_hw_80211_encap(iter, false); + } else if (iter->vif.type == NL80211_IFTYPE_MONITOR) { + disable = sdata; + enable = false; + } + if (disable) + sdata_dbg(disable, + "disable hw 80211 encap due to mon co-exist\n"); + } + mutex_unlock(&local->iflist_mtx); + + if (enable == sdata->hw_80211_encap) + return enable; + + if (!sdata->dev) + return false; + + if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_FRAG) && + (local->hw.wiphy->frag_threshold != (u32)-1)) + enable = false; + + mutex_lock(&sdata->local->key_mtx); + list_for_each_entry(key, &sdata->key_list, list) { + if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP) + enable = false; + } + mutex_unlock(&sdata->local->key_mtx); + + __ieee80211_set_hw_80211_encap(sdata, enable); + + return enable; +} +EXPORT_SYMBOL(ieee80211_set_hw_80211_encap); + static void ieee80211_if_free(struct net_device *dev) { free_percpu(dev->tstats); @@ -1404,6 +1470,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.idle = true; sdata->noack_map = 0; + sdata->hw_80211_encap = false; /* only monitor/p2p-device differ */ if (sdata->dev) { diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 0f889b919b06..9a3a6b95fa27 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -177,6 +177,13 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) } } + /* TKIP countermeasures don't work in encap offload mode */ + if (key->conf.cipher == WLAN_CIPHER_SUITE_TKIP && + sdata->hw_80211_encap) { + sdata_dbg(sdata, "TKIP is not allowed in hw 80211 encap mode\n"); + return -EINVAL; + } + ret = drv_set_key(key->local, SET_KEY, sdata, sta ? &sta->sta : NULL, &key->conf); @@ -203,6 +210,10 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key) key->conf.keyidx, sta ? sta->sta.addr : bcast_addr, ret); + /* cannot do software crypto with encapsulation offload */ + if (sdata->hw_80211_encap) + return -EINVAL; + out_unsupported: switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_WEP40: diff --git a/net/mac80211/status.c b/net/mac80211/status.c index b720feaf9a74..0344b82a34f5 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -1198,6 +1198,77 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_tx_rate_update); +void ieee80211_tx_status_8023(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sta_info *sta; + int retry_count; + int rates_idx; + bool acked; + + sdata = vif_to_sdata(vif); + + acked = info->flags & IEEE80211_TX_STAT_ACK; + rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); + + rcu_read_lock(); + + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) + goto counters_update; + + if (IS_ERR(sta)) + goto counters_update; + + if (!acked) + sta->status_stats.retry_failed++; + + if (rates_idx != -1) + sta->tx_stats.last_rate = info->status.rates[rates_idx]; + + sta->status_stats.retry_count += retry_count; + + if (ieee80211_hw_check(hw, REPORTS_TX_ACK_STATUS)) { + if (acked && vif->type == NL80211_IFTYPE_STATION) + ieee80211_sta_reset_conn_monitor(sdata); + + sta->status_stats.last_ack = jiffies; + if (info->flags & IEEE80211_TX_STAT_ACK) { + if (sta->status_stats.lost_packets) + sta->status_stats.lost_packets = 0; + + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) + sta->status_stats.last_tdls_pkt_time = jiffies; + } else { + ieee80211_lost_packet(sta, info); + } + } + +counters_update: + rcu_read_unlock(); + ieee80211_led_tx(local); + + if (!(info->flags & IEEE80211_TX_STAT_ACK) && + !(info->flags & IEEE80211_TX_STAT_NOACK_TRANSMITTED)) + goto skip_stats_update; + + I802_DEBUG_INC(local->dot11TransmittedFrameCount); + if (is_multicast_ether_addr(skb->data)) + I802_DEBUG_INC(local->dot11MulticastTransmittedFrameCount); + if (retry_count > 0) + I802_DEBUG_INC(local->dot11RetryCount); + if (retry_count > 1) + I802_DEBUG_INC(local->dot11MultipleRetryCount); + +skip_stats_update: + ieee80211_report_used_skb(local, skb, false); + dev_kfree_skb(skb); +} +EXPORT_SYMBOL(ieee80211_tx_status_8023); + void ieee80211_report_low_ack(struct ieee80211_sta *pubsta, u32 num_packets) { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index b696b9136f4c..b31b5078f656 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1250,7 +1250,8 @@ static struct txq_info *ieee80211_get_txq(struct ieee80211_local *local, (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE)) return NULL; - if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) { + if (!(info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) && + unlikely(!ieee80211_is_data_present(hdr->frame_control))) { if ((!ieee80211_is_mgmt(hdr->frame_control) || ieee80211_is_bufferable_mmpdu(hdr->frame_control) || vif->type == NL80211_IFTYPE_STATION) && @@ -2351,9 +2352,9 @@ static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb) skb->data[14] == WLAN_TDLS_SNAP_RFTYPE; } -static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, - struct sta_info **sta_out) +int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, + struct sta_info **sta_out) { struct sta_info *sta; @@ -3607,6 +3608,9 @@ begin: else info->flags &= ~IEEE80211_TX_CTL_AMPDU; + if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) + goto encap_out; + if (info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) { struct sta_info *sta = container_of(txq->sta, struct sta_info, sta); @@ -3666,6 +3670,7 @@ begin: break; } +encap_out: IEEE80211_SKB_CB(skb)->control.vif = vif; if (local->airtime_flags & AIRTIME_USE_AQL) { @@ -4097,6 +4102,153 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, return NETDEV_TX_OK; } +static bool ieee80211_tx_8023(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int led_len, + struct sta_info *sta, + bool txpending) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_tx_control control = {}; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *pubsta = NULL; + unsigned long flags; + int q = info->hw_queue; + + if (ieee80211_queue_skb(local, sdata, sta, skb)) + return true; + + spin_lock_irqsave(&local->queue_stop_reason_lock, flags); + + if (local->queue_stop_reasons[q] || + (!txpending && !skb_queue_empty(&local->pending[q]))) { + if (txpending) + skb_queue_head(&local->pending[q], skb); + else + skb_queue_tail(&local->pending[q], skb); + + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + + return false; + } + + spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + + if (sta && sta->uploaded) + pubsta = &sta->sta; + + control.sta = pubsta; + + drv_tx(local, &control, skb); + + return true; +} + +static void ieee80211_8023_xmit(struct ieee80211_sub_if_data *sdata, + struct net_device *dev, struct sta_info *sta, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ethhdr *ehdr = (struct ethhdr *)skb->data; + struct ieee80211_local *local = sdata->local; + bool authorized = false; + bool multicast; + unsigned char *ra = ehdr->h_dest; + + if (IS_ERR(sta) || (sta && !sta->uploaded)) + sta = NULL; + + if (sdata->vif.type == NL80211_IFTYPE_STATION && + (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER))) + ra = sdata->u.mgd.bssid; + + if (!is_valid_ether_addr(ra)) + goto out_free; + + multicast = is_multicast_ether_addr(ra); + + if (sta) + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + + if (!multicast && !authorized && + (ehdr->h_proto != sdata->control_port_protocol || + !ether_addr_equal(sdata->vif.addr, ehdr->h_source))) + goto out_free; + + if (multicast && sdata->vif.type == NL80211_IFTYPE_AP && + !atomic_read(&sdata->u.ap.num_mcast_sta)) + goto out_free; + + if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning)) && + test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) + goto out_free; + + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS)) + ieee80211_store_ack_skb(local, skb, &info->flags); + + memset(info, 0, sizeof(*info)); + + if (unlikely(sdata->control_port_protocol == ehdr->h_proto)) { + if (sdata->control_port_no_encrypt) + info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO; + } + + if (multicast) + info->flags |= IEEE80211_TX_CTL_NO_ACK; + + info->hw_queue = sdata->vif.hw_queue[skb_get_queue_mapping(skb)]; + + ieee80211_tx_stats(dev, skb->len); + + if (sta) { + sta->tx_stats.bytes[skb_get_queue_mapping(skb)] += skb->len; + sta->tx_stats.packets[skb_get_queue_mapping(skb)]++; + } + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + sdata = container_of(sdata->bss, + struct ieee80211_sub_if_data, u.ap); + + info->control.flags |= IEEE80211_TX_CTRL_HW_80211_ENCAP; + info->control.vif = &sdata->vif; + + ieee80211_tx_8023(sdata, skb, skb->len, sta, false); + + return; + +out_free: + kfree_skb(skb); +} + +netdev_tx_t ieee80211_subif_start_xmit_8023(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + + if (WARN_ON(!sdata->hw_80211_encap)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (unlikely(skb->len < ETH_HLEN)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + + rcu_read_lock(); + + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) + kfree_skb(skb); + else + ieee80211_8023_xmit(sdata, dev, sta, skb); + + rcu_read_unlock(); + + return NETDEV_TX_OK; +} + struct sk_buff * ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags) @@ -4175,6 +4327,16 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, } info->band = chanctx_conf->def.chan->band; result = ieee80211_tx(sdata, NULL, skb, true, 0); + } else if (info->control.flags & IEEE80211_TX_CTRL_HW_80211_ENCAP) { + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { + dev_kfree_skb(skb); + return true; + } + + if (IS_ERR(sta) || (sta && !sta->uploaded)) + sta = NULL; + + result = ieee80211_tx_8023(sdata, skb, skb->len, sta, true); } else { struct sk_buff_head skbs; -- cgit v1.2.3 From 5c5e52d1bb962510fecdc1ebecdde89694d1b223 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 17 Dec 2019 15:19:18 +0100 Subject: nl80211: add handling for BSS color This patch adds the attributes, policy and parsing code to allow userland to send the info about the BSS coloring settings to the kernel. Signed-off-by: John Crispin Link: https://lore.kernel.org/r/20191217141921.8114-1-john@phrozen.org [johannes: remove the strict policy parsing, that was a misunderstanding] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 15 +++++++++++++++ include/uapi/linux/nl80211.h | 26 ++++++++++++++++++++++++++ net/wireless/nl80211.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 059524b87c4c..5c0b1b5ec77b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -259,6 +259,19 @@ struct ieee80211_he_obss_pd { u8 max_offset; }; +/** + * struct cfg80211_he_bss_color - AP settings for BSS coloring + * + * @color: the current color. + * @disabled: is the feature disabled. + * @partial: define the AID equation. + */ +struct cfg80211_he_bss_color { + u8 color; + bool disabled; + bool partial; +}; + /** * struct ieee80211_sta_ht_cap - STA's HT capabilities * @@ -990,6 +1003,7 @@ enum cfg80211_ap_settings_flags { * @twt_responder: Enable Target Wait Time * @flags: flags, as defined in enum cfg80211_ap_settings_flags * @he_obss_pd: OBSS Packet Detection settings + * @he_bss_color: BSS Color settings */ struct cfg80211_ap_settings { struct cfg80211_chan_def chandef; @@ -1018,6 +1032,7 @@ struct cfg80211_ap_settings { bool twt_responder; u32 flags; struct ieee80211_he_obss_pd he_obss_pd; + struct cfg80211_he_bss_color he_bss_color; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 5eab191607f8..809ef9165684 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2400,6 +2400,8 @@ enum nl80211_commands { * @NL80211_ATTR_VLAN_ID: VLAN ID (1..4094) for the station and VLAN group key * (u16). * + * @NL80211_ATTR_HE_BSS_COLOR: nested attribute for BSS Color Settings. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2864,6 +2866,8 @@ enum nl80211_attrs { NL80211_ATTR_VLAN_ID, + NL80211_ATTR_HE_BSS_COLOR, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -6587,5 +6591,27 @@ enum nl80211_obss_pd_attributes { NL80211_HE_OBSS_PD_ATTR_MAX = __NL80211_HE_OBSS_PD_ATTR_LAST - 1, }; +/** + * enum nl80211_bss_color_attributes - BSS Color attributes + * @__NL80211_HE_BSS_COLOR_ATTR_INVALID: Invalid + * + * @NL80211_HE_BSS_COLOR_ATTR_COLOR: the current BSS Color. + * @NL80211_HE_BSS_COLOR_ATTR_DISABLED: is BSS coloring disabled. + * @NL80211_HE_BSS_COLOR_ATTR_PARTIAL: the AID equation to be used.. + * + * @__NL80211_HE_BSS_COLOR_ATTR_LAST: Internal + * @NL80211_HE_BSS_COLOR_ATTR_MAX: highest BSS Color attribute. + */ +enum nl80211_bss_color_attributes { + __NL80211_HE_BSS_COLOR_ATTR_INVALID, + + NL80211_HE_BSS_COLOR_ATTR_COLOR, + NL80211_HE_BSS_COLOR_ATTR_DISABLED, + NL80211_HE_BSS_COLOR_ATTR_PARTIAL, + + /* keep last */ + __NL80211_HE_BSS_COLOR_ATTR_LAST, + NL80211_HE_BSS_COLOR_ATTR_MAX = __NL80211_HE_BSS_COLOR_ATTR_LAST - 1, +}; #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index fa3526592c51..00f24d4c623e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -321,6 +321,13 @@ he_obss_pd_policy[NL80211_HE_OBSS_PD_ATTR_MAX + 1] = { NLA_POLICY_RANGE(NLA_U8, 1, 20), }; +static const struct nla_policy +he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = { + [NL80211_HE_BSS_COLOR_ATTR_COLOR] = NLA_POLICY_RANGE(NLA_U8, 1, 63), + [NL80211_HE_BSS_COLOR_ATTR_DISABLED] = { .type = NLA_FLAG }, + [NL80211_HE_BSS_COLOR_ATTR_PARTIAL] = { .type = NLA_FLAG }, +}; + const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD }, [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, @@ -625,6 +632,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_TWT_RESPONDER] = { .type = NLA_FLAG }, [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), [NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), + [NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy), }; /* policy for the key attributes */ @@ -4511,6 +4519,30 @@ static int nl80211_parse_he_obss_pd(struct nlattr *attrs, return 0; } +static int nl80211_parse_he_bss_color(struct nlattr *attrs, + struct cfg80211_he_bss_color *he_bss_color) +{ + struct nlattr *tb[NL80211_HE_BSS_COLOR_ATTR_MAX + 1]; + int err; + + err = nla_parse_nested(tb, NL80211_HE_BSS_COLOR_ATTR_MAX, attrs, + he_bss_color_policy, NULL); + if (err) + return err; + + if (!tb[NL80211_HE_BSS_COLOR_ATTR_COLOR]) + return -EINVAL; + + he_bss_color->color = + nla_get_u8(tb[NL80211_HE_BSS_COLOR_ATTR_COLOR]); + he_bss_color->disabled = + nla_get_flag(tb[NL80211_HE_BSS_COLOR_ATTR_DISABLED]); + he_bss_color->partial = + nla_get_flag(tb[NL80211_HE_BSS_COLOR_ATTR_PARTIAL]); + + return 0; +} + static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params, const u8 *rates) { @@ -4803,6 +4835,14 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) return err; } + if (info->attrs[NL80211_ATTR_HE_BSS_COLOR]) { + err = nl80211_parse_he_bss_color( + info->attrs[NL80211_ATTR_HE_BSS_COLOR], + ¶ms.he_bss_color); + if (err) + return err; + } + nl80211_calculate_ap_params(¶ms); if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT]) -- cgit v1.2.3 From dd56e90230334752221473c06ff40cac44563a70 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 17 Dec 2019 15:19:19 +0100 Subject: mac80211: add handling for BSS color It is now possible to propagate BSS color settings into the subsystem. Lets make mac80211 also handle them so that we can send them further down the stack into the drivers. We drop the old bss_color field and change iwlwifi to use the new he_bss_color struct. Signed-off-by: John Crispin Link: https://lore.kernel.org/r/20191217141921.8114-2-john@phrozen.org Signed-off-by: Johannes Berg --- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- include/net/cfg80211.h | 13 +++++++++++++ include/net/mac80211.h | 6 ++++-- net/mac80211/cfg.c | 5 ++++- net/mac80211/mlme.c | 9 ++++++++- 5 files changed, 30 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 32dc9d6f0fb6..01ef054e9176 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2016,7 +2016,7 @@ static void iwl_mvm_cfg_he_sta(struct iwl_mvm *mvm, struct iwl_he_sta_context_cmd sta_ctxt_cmd = { .sta_id = sta_id, .tid_limit = IWL_MAX_TID_COUNT, - .bss_color = vif->bss_conf.bss_color, + .bss_color = vif->bss_conf.he_bss_color.color, .htc_trig_based_pkt_ext = vif->bss_conf.htc_trig_based_pkt_ext, .frame_time_rts_th = cpu_to_le16(vif->bss_conf.frame_time_rts_th), diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 5c0b1b5ec77b..fa027d0d031b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -272,6 +272,19 @@ struct cfg80211_he_bss_color { bool partial; }; +/** + * struct ieee80211_he_bss_color - AP settings for BSS coloring + * + * @color: the current color. + * @disabled: is the feature disabled. + * @partial: define the AID equation. + */ +struct ieee80211_he_bss_color { + u8 color; + bool disabled; + bool partial; +}; + /** * struct ieee80211_sta_ht_cap - STA's HT capabilities * diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 682fd2f4431b..6d4ea71523d2 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -316,6 +316,7 @@ struct ieee80211_vif_chanctx_switch { * functionality changed for this BSS (AP mode). * @BSS_CHANGED_TWT: TWT status changed * @BSS_CHANGED_HE_OBSS_PD: OBSS Packet Detection status changed. + * @BSS_CHANGED_HE_BSS_COLOR: BSS Color has changed * */ enum ieee80211_bss_change { @@ -348,6 +349,7 @@ enum ieee80211_bss_change { BSS_CHANGED_FTM_RESPONDER = 1<<26, BSS_CHANGED_TWT = 1<<27, BSS_CHANGED_HE_OBSS_PD = 1<<28, + BSS_CHANGED_HE_BSS_COLOR = 1<<29, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -494,7 +496,6 @@ struct ieee80211_ftm_responder_params { * This structure keeps information about a BSS (and an association * to that BSS) that can change during the lifetime of the BSS. * - * @bss_color: 6-bit value to mark inter-BSS frame, if BSS supports HE * @htc_trig_based_pkt_ext: default PE in 4us units, if BSS supports HE * @multi_sta_back_32bit: supports BA bitmap of 32-bits in Multi-STA BACK * @uora_exists: is the UORA element advertised by AP @@ -604,10 +605,10 @@ struct ieee80211_ftm_responder_params { * in order to discover all the nontransmitted BSSIDs in the set. * @he_operation: HE operation information of the AP we are connected to * @he_obss_pd: OBSS Packet Detection parameters. + * @he_bss_color: BSS coloring settings, if BSS supports HE */ struct ieee80211_bss_conf { const u8 *bssid; - u8 bss_color; u8 htc_trig_based_pkt_ext; bool multi_sta_back_32bit; bool uora_exists; @@ -667,6 +668,7 @@ struct ieee80211_bss_conf { u8 profile_periodicity; struct ieee80211_he_operation he_operation; struct ieee80211_he_obss_pd he_obss_pd; + struct cfg80211_he_bss_color he_bss_color; }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 4fb7f1f12109..a11bd1669c13 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -981,7 +981,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, BSS_CHANGED_P2P_PS | BSS_CHANGED_TXPOWER | BSS_CHANGED_TWT | - BSS_CHANGED_HE_OBSS_PD; + BSS_CHANGED_HE_OBSS_PD | + BSS_CHANGED_HE_BSS_COLOR; int err; int prev_beacon_int; @@ -1054,6 +1055,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->vif.bss_conf.twt_responder = params->twt_responder; memcpy(&sdata->vif.bss_conf.he_obss_pd, ¶ms->he_obss_pd, sizeof(struct ieee80211_he_obss_pd)); + memcpy(&sdata->vif.bss_conf.he_bss_color, ¶ms->he_bss_color, + sizeof(struct ieee80211_he_bss_color)); sdata->vif.bss_conf.ssid_len = params->ssid_len; if (params->ssid_len) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 5fa13176036f..6e4099009eab 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3368,9 +3368,16 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, } if (bss_conf->he_support) { - bss_conf->bss_color = + bss_conf->he_bss_color.color = le32_get_bits(elems->he_operation->he_oper_params, IEEE80211_HE_OPERATION_BSS_COLOR_MASK); + bss_conf->he_bss_color.partial = + le32_get_bits(elems->he_operation->he_oper_params, + IEEE80211_HE_OPERATION_PARTIAL_BSS_COLOR); + bss_conf->he_bss_color.disabled = + le32_get_bits(elems->he_operation->he_oper_params, + IEEE80211_HE_OPERATION_BSS_COLOR_DISABLED); + changed |= BSS_CHANGED_HE_BSS_COLOR; bss_conf->htc_trig_based_pkt_ext = le32_get_bits(elems->he_operation->he_oper_params, -- cgit v1.2.3 From 30b2f0be23fb40e58d0ad2caf8702c2a44cda2e1 Mon Sep 17 00:00:00 2001 From: Thomas Pedersen Date: Mon, 13 Jan 2020 21:59:40 -0800 Subject: mac80211: add ieee80211_is_any_nullfunc() commit 08a5bdde3812 ("mac80211: consider QoS Null frames for STA_NULLFUNC_ACKED") Fixed a bug where we failed to take into account a nullfunc frame can be either non-QoS or QoS. It turns out there is at least one more bug in ieee80211_sta_tx_notify(), introduced in commit 7b6ddeaf27ec ("mac80211: use QoS NDP for AP probing"), where we forgot to check for the QoS variant and so assumed the QoS nullfunc frame never went out Fix this by adding a helper ieee80211_is_any_nullfunc() which consolidates the check for non-QoS and QoS nullfunc frames. Replace existing compound conditionals and add a couple more missing checks for QoS variant. Signed-off-by: Thomas Pedersen Link: https://lore.kernel.org/r/20200114055940.18502-3-thomas@adapt-ip.com Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 9 +++++++++ net/mac80211/mlme.c | 2 +- net/mac80211/rx.c | 8 +++----- net/mac80211/status.c | 5 ++--- net/mac80211/tx.c | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index e172d0c7bf74..1c4409b4c012 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -619,6 +619,15 @@ static inline bool ieee80211_is_qos_nullfunc(__le16 fc) cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_NULLFUNC); } +/** + * ieee80211_is_any_nullfunc - check if frame is regular or QoS nullfunc frame + * @fc: frame control bytes in little-endian byteorder + */ +static inline bool ieee80211_is_any_nullfunc(__le16 fc) +{ + return (ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)); +} + /** * ieee80211_is_bufferable_mmpdu - check if frame is bufferable MMPDU * @fc: frame control field in little-endian byteorder diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 6e4099009eab..cb6fd0a09e07 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2460,7 +2460,7 @@ void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata, if (!ieee80211_is_data(hdr->frame_control)) return; - if (ieee80211_is_nullfunc(hdr->frame_control) && + if (ieee80211_is_any_nullfunc(hdr->frame_control) && sdata->u.mgd.probe_send_count > 0) { if (ack) ieee80211_sta_reset_conn_monitor(sdata); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0e05ff037672..619c223f1cde 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1450,8 +1450,7 @@ ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx) return RX_CONTINUE; if (ieee80211_is_ctl(hdr->frame_control) || - ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control) || + ieee80211_is_any_nullfunc(hdr->frame_control) || is_multicast_ether_addr(hdr->addr1)) return RX_CONTINUE; @@ -1838,8 +1837,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) * Drop (qos-)data::nullfunc frames silently, since they * are used only to control station power saving mode. */ - if (ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) { + if (ieee80211_is_any_nullfunc(hdr->frame_control)) { I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); /* @@ -2319,7 +2317,7 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && - !ieee80211_is_nullfunc(fc) && + !ieee80211_is_any_nullfunc(fc) && ieee80211_is_data(fc) && rx->key)) return -EACCES; diff --git a/net/mac80211/status.c b/net/mac80211/status.c index c9b90d38c54d..22512805eafb 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -643,8 +643,7 @@ static void ieee80211_report_ack_skb(struct ieee80211_local *local, rcu_read_lock(); sdata = ieee80211_sdata_from_skb(local, skb); if (sdata) { - if (ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) + if (ieee80211_is_any_nullfunc(hdr->frame_control)) cfg80211_probe_status(sdata->dev, hdr->addr1, cookie, acked, info->status.ack_signal, @@ -1061,7 +1060,7 @@ static void __ieee80211_tx_status(struct ieee80211_hw *hw, I802_DEBUG_INC(local->dot11FailedCount); } - if ((ieee80211_is_nullfunc(fc) || ieee80211_is_qos_nullfunc(fc)) && + if (ieee80211_is_any_nullfunc(fc) && ieee80211_has_pm(fc) && ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS) && !(info->flags & IEEE80211_TX_CTL_INJECTED) && diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index c56d801e708f..4296d9d71311 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -297,7 +297,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) && test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) && !ieee80211_is_probe_req(hdr->frame_control) && - !ieee80211_is_nullfunc(hdr->frame_control)) + !ieee80211_is_any_nullfunc(hdr->frame_control)) /* * When software scanning only nullfunc frames (to notify * the sleep state to the AP) and probe requests (for the -- cgit v1.2.3 From 24a448b165253b6f2ab1e0bcdba9a733007681d6 Mon Sep 17 00:00:00 2001 From: Chengguang Xu Date: Fri, 6 Dec 2019 18:29:03 +0800 Subject: XArray: Fix incorrect comment in header file 256 means Retry entry and 257 means Zero entry, so fix it. Signed-off-by: Chengguang Xu Signed-off-by: Matthew Wilcox (Oracle) --- include/linux/xarray.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/xarray.h b/include/linux/xarray.h index f73e1775ded0..a491653d8882 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -32,8 +32,8 @@ * The following internal entries have a special meaning: * * 0-62: Sibling entries - * 256: Zero entry - * 257: Retry entry + * 256: Retry entry + * 257: Zero entry * * Errors are also represented as internal entries, but use the negative * space (-4094 to -2). They're never stored in the slots array; only -- cgit v1.2.3 From 1e61d82cca170465902003bfe445f0461e28208b Mon Sep 17 00:00:00 2001 From: Haim Dreyfuss Date: Tue, 21 Jan 2020 10:12:13 +0200 Subject: cfg80211: add no HE indication to the channel flag The regulatory domain might forbid HE operation. Certain regulatory domains may restrict it for specific channels whereas others may do it for the whole regulatory domain. Add an option to indicate it in the channel flag. Signed-off-by: Haim Dreyfuss Signed-off-by: Luca Coelho Link: https://lore.kernel.org/r/20200121081213.733757-1-luca@coelho.fi Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 5 +++++ net/wireless/nl80211.c | 3 +++ net/wireless/reg.c | 2 ++ 4 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index fa027d0d031b..40f2a3a30e3d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -95,6 +95,7 @@ struct wiphy; * on this channel. * @IEEE80211_CHAN_NO_10MHZ: 10 MHz bandwidth is not permitted * on this channel. + * @IEEE80211_CHAN_NO_HE: HE operation is not permitted on this channel. * */ enum ieee80211_channel_flags { @@ -111,6 +112,7 @@ enum ieee80211_channel_flags { IEEE80211_CHAN_IR_CONCURRENT = 1<<10, IEEE80211_CHAN_NO_20MHZ = 1<<11, IEEE80211_CHAN_NO_10MHZ = 1<<12, + IEEE80211_CHAN_NO_HE = 1<<13, }; #define IEEE80211_CHAN_NO_HT40 \ diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 809ef9165684..d996bac97e9d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -3587,6 +3587,8 @@ enum nl80211_wmm_rule { * @NL80211_FREQUENCY_ATTR_WMM: this channel has wmm limitations. * This is a nested attribute that contains the wmm limitation per AC. * (see &enum nl80211_wmm_rule) + * @NL80211_FREQUENCY_ATTR_NO_HE: HE operation is not allowed on this channel + * in current regulatory domain. * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number * currently defined * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use @@ -3616,6 +3618,7 @@ enum nl80211_frequency_attr { NL80211_FREQUENCY_ATTR_NO_20MHZ, NL80211_FREQUENCY_ATTR_NO_10MHZ, NL80211_FREQUENCY_ATTR_WMM, + NL80211_FREQUENCY_ATTR_NO_HE, /* keep last */ __NL80211_FREQUENCY_ATTR_AFTER_LAST, @@ -3813,6 +3816,7 @@ enum nl80211_sched_scan_match_attr { * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed + * @NL80211_RRF_NO_HE: HE operation not allowed */ enum nl80211_reg_rule_flags { NL80211_RRF_NO_OFDM = 1<<0, @@ -3830,6 +3834,7 @@ enum nl80211_reg_rule_flags { NL80211_RRF_NO_HT40PLUS = 1<<14, NL80211_RRF_NO_80MHZ = 1<<15, NL80211_RRF_NO_160MHZ = 1<<16, + NL80211_RRF_NO_HE = 1<<17, }; #define NL80211_RRF_PASSIVE_SCAN NL80211_RRF_NO_IR diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 00f24d4c623e..d8cdbf07aeec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -972,6 +972,9 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) && nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_10MHZ)) goto nla_put_failure; + if ((chan->flags & IEEE80211_CHAN_NO_HE) && + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_HE)) + goto nla_put_failure; } if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 446c76d44e65..ea7bc5652a41 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1569,6 +1569,8 @@ static u32 map_regdom_flags(u32 rd_flags) channel_flags |= IEEE80211_CHAN_NO_80MHZ; if (rd_flags & NL80211_RRF_NO_160MHZ) channel_flags |= IEEE80211_CHAN_NO_160MHZ; + if (rd_flags & NL80211_RRF_NO_HE) + channel_flags |= IEEE80211_CHAN_NO_HE; return channel_flags; } -- cgit v1.2.3 From d6039a3416f7af37c04f22c411f120ad46f51663 Mon Sep 17 00:00:00 2001 From: Veerendranath Jakkam Date: Mon, 27 Jan 2020 02:00:32 +0530 Subject: cfg80211: Enhance the AKM advertizement to support per interface. Commit ab4dfa20534e ("cfg80211: Allow drivers to advertise supported AKM suites") introduces the support to advertize supported AKMs to userspace. This needs an enhancement to advertize the AKM support per interface type, specifically for the cfg80211-based drivers that implement SME and use different mechanisms to support the AKM's for each interface type (e.g., the support for SAE, OWE AKM's take different paths for such drivers on STA/AP mode). This commit aims the same and enhances the earlier mechanism of advertizing the AKMs per wiphy. Add new nl80211 attributes and data structure to provide supported AKMs per interface type to userspace. the AKMs advertized in akm_suites are default capabilities if not advertized for a specific interface type in iftype_akm_suites. Signed-off-by: Veerendranath Jakkam Link: https://lore.kernel.org/r/20200126203032.21934-1-vjakkam@codeaurora.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 28 +++++++++++++++++++++++++++- include/uapi/linux/nl80211.h | 33 +++++++++++++++++++++++++++++++++ net/wireless/nl80211.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 40f2a3a30e3d..c3a9b375d3ca 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4400,6 +4400,21 @@ struct cfg80211_pmsr_capabilities { } ftm; }; +/** + * struct wiphy_iftype_akm_suites - This structure encapsulates supported akm + * suites for interface types defined in @iftypes_mask. Each type in the + * @iftypes_mask must be unique across all instances of iftype_akm_suites. + * + * @iftypes_mask: bitmask of interfaces types + * @akm_suites: points to an array of supported akm suites + * @n_akm_suites: number of supported AKM suites + */ +struct wiphy_iftype_akm_suites { + u16 iftypes_mask; + const u32 *akm_suites; + int n_akm_suites; +}; + /** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, @@ -4412,8 +4427,16 @@ struct cfg80211_pmsr_capabilities { * @signal_type: signal type reported in &struct cfg80211_bss. * @cipher_suites: supported cipher suites * @n_cipher_suites: number of supported cipher suites - * @akm_suites: supported AKM suites + * @akm_suites: supported AKM suites. These are the default AKMs supported if + * the supported AKMs not advertized for a specific interface type in + * iftype_akm_suites. * @n_akm_suites: number of supported AKM suites + * @iftype_akm_suites: array of supported akm suites info per interface type. + * Note that the bits in @iftypes_mask inside this structure cannot + * overlap (i.e. only one occurrence of each type is allowed across all + * instances of iftype_akm_suites). + * @num_iftype_akm_suites: number of interface types for which supported akm + * suites are specified separately. * @retry_short: Retry limit for short frames (dot11ShortRetryLimit) * @retry_long: Retry limit for long frames (dot11LongRetryLimit) * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold); @@ -4620,6 +4643,9 @@ struct wiphy { int n_akm_suites; const u32 *akm_suites; + const struct wiphy_iftype_akm_suites *iftype_akm_suites; + unsigned int num_iftype_akm_suites; + u8 retry_short; u8 retry_long; u32 frag_threshold; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index d996bac97e9d..350ab86fe20e 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -2402,6 +2402,13 @@ enum nl80211_commands { * * @NL80211_ATTR_HE_BSS_COLOR: nested attribute for BSS Color Settings. * + * @NL80211_ATTR_IFTYPE_AKM_SUITES: nested array attribute, with each entry + * using attributes from &enum nl80211_iftype_akm_attributes. This + * attribute is sent in a response to %NL80211_CMD_GET_WIPHY indicating + * supported AKM suites capability per interface. AKMs advertised in + * %NL80211_ATTR_AKM_SUITES are default capabilities if AKM suites not + * advertised for a specific interface type. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2868,6 +2875,8 @@ enum nl80211_attrs { NL80211_ATTR_HE_BSS_COLOR, + NL80211_ATTR_IFTYPE_AKM_SUITES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -6619,4 +6628,28 @@ enum nl80211_bss_color_attributes { NL80211_HE_BSS_COLOR_ATTR_MAX = __NL80211_HE_BSS_COLOR_ATTR_LAST - 1, }; +/** + * enum nl80211_iftype_akm_attributes - interface type AKM attributes + * @__NL80211_IFTYPE_AKM_ATTR_INVALID: Invalid + * + * @NL80211_IFTYPE_AKM_ATTR_IFTYPES: nested attribute containing a flag + * attribute for each interface type that supports AKM suites specified in + * %NL80211_IFTYPE_AKM_ATTR_SUITES + * @NL80211_IFTYPE_AKM_ATTR_SUITES: an array of u32. Used to indicate supported + * AKM suites for the specified interface types. + * + * @__NL80211_IFTYPE_AKM_ATTR_LAST: Internal + * @NL80211_IFTYPE_AKM_ATTR_MAX: highest interface type AKM attribute. + */ +enum nl80211_iftype_akm_attributes { + __NL80211_IFTYPE_AKM_ATTR_INVALID, + + NL80211_IFTYPE_AKM_ATTR_IFTYPES, + NL80211_IFTYPE_AKM_ATTR_SUITES, + + /* keep last */ + __NL80211_IFTYPE_AKM_ATTR_LAST, + NL80211_IFTYPE_AKM_ATTR_MAX = __NL80211_IFTYPE_AKM_ATTR_LAST - 1, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d8cdbf07aeec..3ad937d0a256 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1896,6 +1896,46 @@ static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev, return 0; } +static int +nl80211_put_iftype_akm_suites(struct cfg80211_registered_device *rdev, + struct sk_buff *msg) +{ + int i; + struct nlattr *nested, *nested_akms; + const struct wiphy_iftype_akm_suites *iftype_akms; + + if (!rdev->wiphy.num_iftype_akm_suites || + !rdev->wiphy.iftype_akm_suites) + return 0; + + nested = nla_nest_start(msg, NL80211_ATTR_IFTYPE_AKM_SUITES); + if (!nested) + return -ENOBUFS; + + for (i = 0; i < rdev->wiphy.num_iftype_akm_suites; i++) { + nested_akms = nla_nest_start(msg, i + 1); + if (!nested_akms) + return -ENOBUFS; + + iftype_akms = &rdev->wiphy.iftype_akm_suites[i]; + + if (nl80211_put_iftypes(msg, NL80211_IFTYPE_AKM_ATTR_IFTYPES, + iftype_akms->iftypes_mask)) + return -ENOBUFS; + + if (nla_put(msg, NL80211_IFTYPE_AKM_ATTR_SUITES, + sizeof(u32) * iftype_akms->n_akm_suites, + iftype_akms->akm_suites)) { + return -ENOBUFS; + } + nla_nest_end(msg, nested_akms); + } + + nla_nest_end(msg, nested); + + return 0; +} + struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; @@ -2454,6 +2494,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, rdev->wiphy.akm_suites)) goto nla_put_failure; + if (nl80211_put_iftype_akm_suites(rdev, msg)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; -- cgit v1.2.3 From f93d6b21a93ceb02140eafd84e4fd77f5d00180a Mon Sep 17 00:00:00 2001 From: Zvika Yehudai Date: Mon, 3 Feb 2020 10:08:23 +0200 Subject: ieee80211: fix 'the' doubling in comments Remove redundant 'the' where 'the the' was written. Signed-off-by: Zvika Yehudai Link: https://lore.kernel.org/r/20200203080823.24949-1-zvikayeh@gmail.com Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 1c4409b4c012..095a7108c394 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2062,7 +2062,7 @@ ieee80211_he_ppe_size(u8 ppe_thres_hdr, const u8 *phy_cap_info) /* * ieee80211_he_oper_size - calculate 802.11ax HE Operations IE size - * @he_oper_ie: byte data of the He Operations IE, stating from the the byte + * @he_oper_ie: byte data of the He Operations IE, stating from the byte * after the ext ID byte. It is assumed that he_oper_ie has at least * sizeof(struct ieee80211_he_operation) bytes, the caller must have * validated this. @@ -2100,7 +2100,7 @@ ieee80211_he_oper_size(const u8 *he_oper_ie) /* * ieee80211_he_spr_size - calculate 802.11ax HE Spatial Reuse IE size - * @he_spr_ie: byte data of the He Spatial Reuse IE, stating from the the byte + * @he_spr_ie: byte data of the He Spatial Reuse IE, stating from the byte * after the ext ID byte. It is assumed that he_spr_ie has at least * sizeof(struct ieee80211_he_spr) bytes, the caller must have validated * this @@ -2743,7 +2743,7 @@ enum ieee80211_tdls_actioncode { */ #define WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT BIT(6) -/* TDLS capabilities in the the 4th byte of @WLAN_EID_EXT_CAPABILITY */ +/* TDLS capabilities in the 4th byte of @WLAN_EID_EXT_CAPABILITY */ #define WLAN_EXT_CAPA4_TDLS_BUFFER_STA BIT(4) #define WLAN_EXT_CAPA4_TDLS_PEER_PSM BIT(5) #define WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH BIT(6) -- cgit v1.2.3 From c0058df73309906ef4d5383fbaa10c43ebddc48a Mon Sep 17 00:00:00 2001 From: Shaul Triebitz Date: Fri, 31 Jan 2020 13:12:57 +0200 Subject: mac80211: parse also the RSNXE IE Parse also the RSN Extension IE when parsing the rest of the IEs. It will be used in a later patch. Signed-off-by: Shaul Triebitz Signed-off-by: Luca Coelho Link: https://lore.kernel.org/r/20200131111300.891737-21-luca@coelho.fi Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 8 ++++++++ net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/util.c | 7 ++++++- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 095a7108c394..6f3e7c5c600a 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2532,6 +2532,7 @@ enum ieee80211_eid { WLAN_EID_FILS_INDICATION = 240, WLAN_EID_DILS = 241, WLAN_EID_FRAGMENT = 242, + WLAN_EID_RSNX = 244, WLAN_EID_EXTENSION = 255 }; @@ -3421,4 +3422,11 @@ static inline bool for_each_element_completed(const struct element *element, return (const u8 *)element == (const u8 *)data + datalen; } +/** + * RSNX Capabilities: + * bits 0-3: Field length (n-1) + */ +#define WLAN_RSNX_CAPA_PROTECTED_TWT BIT(4) +#define WLAN_RSNX_CAPA_SAE_H2E BIT(5) + #endif /* LINUX_IEEE80211_H */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 7074af92b536..8a49d78ad7c9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1472,6 +1472,7 @@ struct ieee802_11_elems { const struct ieee80211_tim_ie *tim; const u8 *challenge; const u8 *rsn; + const u8 *rsnx; const u8 *erp_info; const u8 *ext_supp_rates; const u8 *wmm_info; @@ -1519,6 +1520,7 @@ struct ieee802_11_elems { u8 tim_len; u8 challenge_len; u8 rsn_len; + u8 rsnx_len; u8 ext_supp_rates_len; u8 wmm_info_len; u8 wmm_param_len; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 72039c8dbc38..7ddf0508779f 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -6,7 +6,7 @@ * Copyright 2007 Johannes Berg * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2019 Intel Corporation + * Copyright (C) 2018-2020 Intel Corporation * * utilities for mac80211 */ @@ -994,6 +994,7 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, case WLAN_EID_CHAN_SWITCH_TIMING: case WLAN_EID_LINK_ID: case WLAN_EID_BSS_MAX_IDLE_PERIOD: + case WLAN_EID_RSNX: /* * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible * that if the content gets bigger it might be needed more than once @@ -1264,6 +1265,10 @@ _ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, if (elen >= sizeof(*elems->max_idle_period_ie)) elems->max_idle_period_ie = (void *)pos; break; + case WLAN_EID_RSNX: + elems->rsnx = pos; + elems->rsnx_len = elen; + break; case WLAN_EID_EXTENSION: ieee80211_parse_extension_element(calc_crc ? &crc : NULL, -- cgit v1.2.3 From 8c3ed7aa2b9ef666195b789e9b02e28383243fa8 Mon Sep 17 00:00:00 2001 From: Markus Theil Date: Wed, 15 Jan 2020 13:55:22 +0100 Subject: nl80211: add src and dst addr attributes for control port tx/rx When using control port over nl80211 in AP mode with pre-authentication, APs need to forward frames to other APs defined by their MAC address. Before this patch, pre-auth frames reaching user space over nl80211 control port have no longer any information about the dest attached, which can be used for forwarding to a controller or injecting the frame back to a ethernet interface over a AF_PACKET socket. Analog problems exist, when forwarding pre-auth frames from AP -> STA. This patch therefore adds the NL80211_ATTR_DST_MAC and NL80211_ATTR_SRC_MAC attributes to provide more context information when forwarding. The respective arguments are optional on tx and included on rx. Therefore unaware existing software is not affected. Software which wants to detect this feature, can do so by checking against: NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_MAC_ADDRS Signed-off-by: Markus Theil Link: https://lore.kernel.org/r/20200115125522.3755-1-markus.theil@tu-ilmenau.de [split into separate cfg80211/mac80211 patches] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 ++- include/uapi/linux/nl80211.h | 16 +++++++++++++++- net/mac80211/ieee80211_i.h | 3 ++- net/mac80211/tx.c | 3 ++- net/wireless/nl80211.c | 18 +++++++++++++++--- net/wireless/rdev-ops.h | 8 ++++---- net/wireless/trace.h | 27 +++++++++++++++++---------- 7 files changed, 57 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c3a9b375d3ca..ec0de3c43c61 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3969,7 +3969,8 @@ struct cfg80211_ops { int (*tx_control_port)(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, - const u8 *dest, const __be16 proto, + const u8 *dest, const u8 *src, + const __be16 proto, const bool noencrypt); int (*get_ftm_responder_stats)(struct wiphy *wiphy, diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 350ab86fe20e..158bccb4a47b 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1039,11 +1039,14 @@ * a control port frame and as a notification that a control port frame * has been received. %NL80211_ATTR_FRAME is used to specify the * frame contents. The frame is the raw EAPoL data, without ethernet or - * 802.11 headers. + * 802.11 headers. An optional %NL80211_ATTR_SRC_MAC can be used to send + * pre-auth frames to STAs on behalf of other APs. * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added * indicating the protocol type of the received frame; whether the frame * was received unencrypted and the MAC address of the peer respectively. + * %NL80211_ATTR_DST_MAC can be used to forward pre-auth frames in + * userspace while using AP mode. * * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. * @@ -2409,6 +2412,9 @@ enum nl80211_commands { * %NL80211_ATTR_AKM_SUITES are default capabilities if AKM suites not * advertised for a specific interface type. * + * @NL80211_ATTR_SRC_MAC: MAC address used in control port over nl80211 transmit + * @NL80211_ATTR_DST_MAC: MAC address used in control port over nl80211 receive + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2877,6 +2883,9 @@ enum nl80211_attrs { NL80211_ATTR_IFTYPE_AKM_SUITES, + NL80211_ATTR_SRC_MAC, + NL80211_ATTR_DST_MAC, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -5539,6 +5548,10 @@ enum nl80211_feature_flags { * feature, which prevents bufferbloat by using the expected transmission * time to limit the amount of data buffered in the hardware. * + * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_MAC_ADDRS: The driver + * can use src and dst MAC addresses with control port over nl80211 rx + * and tx operations. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -5586,6 +5599,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SAE_OFFLOAD, NL80211_EXT_FEATURE_VLAN_OFFLOAD, NL80211_EXT_FEATURE_AQL, + NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_MAC_ADDRS, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 8a49d78ad7c9..da9eaa9ee37e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1792,7 +1792,8 @@ void ieee80211_check_fast_xmit_iface(struct ieee80211_sub_if_data *sdata); void ieee80211_clear_fast_xmit(struct sta_info *sta); int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, - const u8 *dest, __be16 proto, bool unencrypted); + const u8 *dest, const u8 *src, __be16 proto, + bool unencrypted); int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 4296d9d71311..059c66b6bb5c 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5285,7 +5285,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, - const u8 *dest, __be16 proto, bool unencrypted) + const u8 *dest, const u8 *src, __be16 proto, + bool unencrypted) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4c0ea54e0f59..33fe6ac1c242 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -633,6 +633,8 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), [NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), [NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy), + [NL80211_ATTR_SRC_MAC] = NLA_POLICY_ETH_ADDR, + [NL80211_ATTR_DST_MAC] = NLA_POLICY_ETH_ADDR, }; /* policy for the key attributes */ @@ -13694,6 +13696,7 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) const u8 *buf; size_t len; u8 *dest; + u8 src[ETH_ALEN]; u16 proto; bool noencrypt; int err; @@ -13731,6 +13734,13 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) goto out; } + /* copy src address under wdev_lock, as we may copy wdev_address */ + if (info->attrs[NL80211_ATTR_SRC_MAC]) + ether_addr_copy(src, + nla_data(info->attrs[NL80211_ATTR_SRC_MAC])); + else + ether_addr_copy(src, wdev_address(wdev)); + wdev_unlock(wdev); buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); @@ -13741,7 +13751,7 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]); return rdev_tx_control_port(rdev, dev, buf, len, - dest, cpu_to_be16(proto), noencrypt); + dest, src, cpu_to_be16(proto), noencrypt); out: wdev_unlock(wdev); @@ -15996,7 +16006,8 @@ static int __nl80211_rx_control_port(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct ethhdr *ehdr = eth_hdr(skb); - const u8 *addr = ehdr->h_source; + const u8 *daddr = ehdr->h_dest; + const u8 *saddr = ehdr->h_source; u16 proto = be16_to_cpu(skb->protocol); struct sk_buff *msg; void *hdr; @@ -16021,7 +16032,8 @@ static int __nl80211_rx_control_port(struct net_device *dev, nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), NL80211_ATTR_PAD) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, saddr) || + nla_put(msg, NL80211_ATTR_DST_MAC, ETH_ALEN, daddr) || nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) || (unencrypted && nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index e853a4fe6f97..39e6c1db3092 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -730,14 +730,14 @@ static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev, struct net_device *dev, const void *buf, size_t len, - const u8 *dest, __be16 proto, - const bool noencrypt) + const u8 *dest, const u8 *src, + __be16 proto, const bool noencrypt) { int ret; trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len, - dest, proto, noencrypt); + dest, src, proto, noencrypt); ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len, - dest, proto, noencrypt); + dest, src, proto, noencrypt); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index d98ad2b3143b..fefa255fd062 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1923,27 +1923,31 @@ TRACE_EVENT(rdev_mgmt_tx, TRACE_EVENT(rdev_tx_control_port, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - const u8 *buf, size_t len, const u8 *dest, __be16 proto, + const u8 *buf, size_t len, + const u8 *dest, const u8 *src, __be16 proto, bool unencrypted), - TP_ARGS(wiphy, netdev, buf, len, dest, proto, unencrypted), + TP_ARGS(wiphy, netdev, buf, len, dest, src, proto, unencrypted), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY MAC_ENTRY(dest) - __field(__be16, proto) + MAC_ENTRY(src) + __field(u16, proto) __field(bool, unencrypted) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; MAC_ASSIGN(dest, dest); - __entry->proto = proto; + MAC_ASSIGN(src, src); + __entry->proto = be16_to_cpu(proto); __entry->unencrypted = unencrypted; ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT "," - " proto: 0x%x, unencrypted: %s", - WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dest), - be16_to_cpu(__entry->proto), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", dest: " MAC_PR_FMT + ", src: " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, + MAC_PR_ARG(dest), MAC_PR_ARG(src), + __entry->proto, BOOL_TO_STR(__entry->unencrypted)) ); @@ -2835,6 +2839,7 @@ TRACE_EVENT(cfg80211_rx_control_port, TP_STRUCT__entry( NETDEV_ENTRY __field(int, len) + MAC_ENTRY(to) MAC_ENTRY(from) __field(u16, proto) __field(bool, unencrypted) @@ -2842,12 +2847,14 @@ TRACE_EVENT(cfg80211_rx_control_port, TP_fast_assign( NETDEV_ASSIGN; __entry->len = skb->len; + MAC_ASSIGN(to, eth_hdr(skb)->h_dest); MAC_ASSIGN(from, eth_hdr(skb)->h_source); __entry->proto = be16_to_cpu(skb->protocol); __entry->unencrypted = unencrypted; ), - TP_printk(NETDEV_PR_FMT ", len=%d, " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s", - NETDEV_PR_ARG, __entry->len, MAC_PR_ARG(from), + TP_printk(NETDEV_PR_FMT ", len=%d, dest: " MAC_PR_FMT + ", src: " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s", + NETDEV_PR_ARG, __entry->len, MAC_PR_ARG(to), MAC_PR_ARG(from), __entry->proto, BOOL_TO_STR(__entry->unencrypted)) ); -- cgit v1.2.3 From b9c7d41087bc1755e01f9584f0bdbce0bb8b195d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Feb 2020 17:39:39 +0100 Subject: ALSA: pcm: More helper macros for reducing snd_pcm_format_t cast snd_pcm_format_t is a strong-typed integer and requires the explicit cast with __force if converted or compared with a normal integer value. Since most of use cases do iterate over all formats and test / set the mask, provide a couple of new helper macros that do the explicit cast. Link: https://lore.kernel.org/r/20200206163945.6797-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/sound/pcm.h | 9 +++++++++ include/sound/pcm_params.h | 7 +++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index f657ff08f317..31a4b300e4c9 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1415,6 +1415,15 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format) return 1ULL << (__force int) pcm_format; } +/** + * pcm_for_each_format - helper to iterate for each format type + * @f: the iterator variable in snd_pcm_format_t type + */ +#define pcm_for_each_format(f) \ + for ((f) = SNDRV_PCM_FORMAT_FIRST; \ + (__force int)(f) <= (__force int)SNDRV_PCM_FORMAT_LAST; \ + (f) = (__force snd_pcm_format_t)((__force int)(f) + 1)) + /* printk helpers */ #define pcm_err(pcm, fmt, args...) \ dev_err((pcm)->card->dev, fmt, ##args) diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 661450a2095b..36f94735d23d 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -133,6 +133,13 @@ static inline int snd_mask_test(const struct snd_mask *mask, unsigned int val) return mask->bits[MASK_OFS(val)] & MASK_BIT(val); } +/* Most of drivers need only this one */ +static inline int snd_mask_test_format(const struct snd_mask *mask, + snd_pcm_format_t format) +{ + return snd_mask_test(mask, (__force unsigned int)format); +} + static inline int snd_mask_single(const struct snd_mask *mask) { int i, c = 0; -- cgit v1.2.3 From 046e14afb3561523efd0047c35c20793ae5f8848 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Feb 2020 15:43:36 +0200 Subject: gpio: Avoid kernel.h inclusion where it's possible Inclusion of kernel.h increases the mess with the header dependencies. Avoid kernel.h inclusion where it's possible. Besides that, clean up a bit other inclusions inside GPIO subsystem headers. It includes: - removal pin control bits (forward declaration and header) from linux/gpio.h - removal of.h from asm-generic/gpio.h - use of explicit headers in gpio/consumer.h - add FIXME note with regard to gpio.h inclusion in of_gpio,h Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200205134336.20197-1-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- include/asm-generic/gpio.h | 4 ++-- include/linux/gpio.h | 2 -- include/linux/gpio/consumer.h | 5 ++++- include/linux/of_gpio.h | 9 ++++++--- 4 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 19eadac415c4..aea9aee1f3e9 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -2,10 +2,8 @@ #ifndef _ASM_GENERIC_GPIO_H #define _ASM_GENERIC_GPIO_H -#include #include #include -#include #ifdef CONFIG_GPIOLIB @@ -140,6 +138,8 @@ static inline void gpio_unexport(unsigned gpio) #else /* !CONFIG_GPIOLIB */ +#include + static inline bool gpio_is_valid(int number) { /* only non-negative numbers are valid */ diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 2157717c2136..008ad3ee56b7 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -102,11 +102,9 @@ void devm_gpio_free(struct device *dev, unsigned int gpio); #include #include #include -#include struct device; struct gpio_chip; -struct pinctrl_dev; static inline bool gpio_is_valid(int number) { diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index bf2d017dd7b7..0a72fccf60ff 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -2,9 +2,10 @@ #ifndef __LINUX_GPIO_CONSUMER_H #define __LINUX_GPIO_CONSUMER_H +#include #include +#include #include -#include struct device; @@ -189,6 +190,8 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, #else /* CONFIG_GPIOLIB */ +#include + static inline int gpiod_count(struct device *dev, const char *con_id) { return 0; diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 16967390a3fe..f821095218b0 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -11,9 +11,8 @@ #define __LINUX_OF_GPIO_H #include -#include -#include -#include +#include +#include /* FIXME: Shouldn't be here */ #include struct device_node; @@ -34,6 +33,8 @@ enum of_gpio_flags { #ifdef CONFIG_OF_GPIO +#include + /* * OF GPIO chip for memory mapped banks */ @@ -63,6 +64,8 @@ extern void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc); #else /* CONFIG_OF_GPIO */ +#include + /* Drivers may not strictly depend on the GPIO support, so let them link. */ static inline int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags) -- cgit v1.2.3 From ec2daf6e33f9f9113ba085b6ff88592907b6f1ce Mon Sep 17 00:00:00 2001 From: Jon Flatley Date: Fri, 24 Jan 2020 15:18:32 -0800 Subject: platform: chrome: Add cros-usbpd-notify driver ChromiumOS uses ACPI device with HID "GOOG0003" for power delivery related events. The existing cros-usbpd-charger driver relies on these events without ever actually receiving them on ACPI platforms. This is because in the ChromeOS kernel trees, the GOOG0003 device is owned by an ACPI driver that offers firmware updates to USB-C chargers. Introduce a new platform driver under cros-ec, the ChromeOS embedded controller, that handles these PD events and dispatches them appropriately over a notifier chain to all drivers that use them. On platforms that don't have the ACPI device defined, the driver gets instantiated for ECs which support the EC_FEATURE_USB_PD feature bit, and the notification events will get delivered using the MKBP event handling mechanism. Co-Developed-by: Prashant Malani Reviewed-by: Gwendal Grignou Reviewed-by: Benson Leung Signed-off-by: Jon Flatley Signed-off-by: Prashant Malani Acked-By: Enric Balletbo i Serra Signed-off-by: Benson Leung --- drivers/platform/chrome/Kconfig | 14 ++ drivers/platform/chrome/Makefile | 1 + drivers/platform/chrome/cros_usbpd_notify.c | 169 ++++++++++++++++++++++++ include/linux/platform_data/cros_usbpd_notify.h | 17 +++ 4 files changed, 201 insertions(+) create mode 100644 drivers/platform/chrome/cros_usbpd_notify.c create mode 100644 include/linux/platform_data/cros_usbpd_notify.h (limited to 'include') diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 5f57282a28da..15fc8b8a2db8 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -226,6 +226,20 @@ config CROS_USBPD_LOGGER To compile this driver as a module, choose M here: the module will be called cros_usbpd_logger. +config CROS_USBPD_NOTIFY + tristate "ChromeOS Type-C power delivery event notifier" + depends on MFD_CROS_EC_DEV + default MFD_CROS_EC_DEV + help + If you say Y here, you get support for Type-C PD event notifications + from the ChromeOS EC. On ACPI platorms this driver will bind to the + GOOG0003 ACPI device, and on platforms which don't have this device it + will get initialized on ECs which support the feature + EC_FEATURE_USB_PD. + + To compile this driver as a module, choose M here: the + module will be called cros_usbpd_notify. + source "drivers/platform/chrome/wilco_ec/Kconfig" endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index aacd5920d8a1..f6465f8ef0b5 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -22,5 +22,6 @@ obj-$(CONFIG_CROS_EC_DEBUGFS) += cros_ec_debugfs.o obj-$(CONFIG_CROS_EC_SENSORHUB) += cros_ec_sensorhub.o obj-$(CONFIG_CROS_EC_SYSFS) += cros_ec_sysfs.o obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o +obj-$(CONFIG_CROS_USBPD_NOTIFY) += cros_usbpd_notify.o obj-$(CONFIG_WILCO_EC) += wilco_ec/ diff --git a/drivers/platform/chrome/cros_usbpd_notify.c b/drivers/platform/chrome/cros_usbpd_notify.c new file mode 100644 index 000000000000..3851bbd6e9a3 --- /dev/null +++ b/drivers/platform/chrome/cros_usbpd_notify.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Google LLC + * + * This driver serves as the receiver of cros_ec PD host events. + */ + +#include +#include +#include +#include +#include + +#define DRV_NAME "cros-usbpd-notify" +#define ACPI_DRV_NAME "GOOG0003" + +static BLOCKING_NOTIFIER_HEAD(cros_usbpd_notifier_list); + +/** + * cros_usbpd_register_notify - Register a notifier callback for PD events. + * @nb: Notifier block pointer to register + * + * On ACPI platforms this corresponds to host events on the ECPD + * "GOOG0003" ACPI device. On non-ACPI platforms this will filter mkbp events + * for USB PD events. + * + * Return: 0 on success or negative error code. + */ +int cros_usbpd_register_notify(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&cros_usbpd_notifier_list, + nb); +} +EXPORT_SYMBOL_GPL(cros_usbpd_register_notify); + +/** + * cros_usbpd_unregister_notify - Unregister notifier callback for PD events. + * @nb: Notifier block pointer to unregister + * + * Unregister a notifier callback that was previously registered with + * cros_usbpd_register_notify(). + */ +void cros_usbpd_unregister_notify(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&cros_usbpd_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(cros_usbpd_unregister_notify); + +#ifdef CONFIG_ACPI + +static int cros_usbpd_notify_add_acpi(struct acpi_device *adev) +{ + return 0; +} + +static void cros_usbpd_notify_acpi(struct acpi_device *adev, u32 event) +{ + blocking_notifier_call_chain(&cros_usbpd_notifier_list, event, NULL); +} + +static const struct acpi_device_id cros_usbpd_notify_acpi_device_ids[] = { + { ACPI_DRV_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_usbpd_notify_acpi_device_ids); + +static struct acpi_driver cros_usbpd_notify_acpi_driver = { + .name = DRV_NAME, + .class = DRV_NAME, + .ids = cros_usbpd_notify_acpi_device_ids, + .ops = { + .add = cros_usbpd_notify_add_acpi, + .notify = cros_usbpd_notify_acpi, + }, +}; + +#endif /* CONFIG_ACPI */ + +static int cros_usbpd_notify_plat(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *data) +{ + struct cros_ec_device *ec_dev = (struct cros_ec_device *)data; + u32 host_event = cros_ec_get_host_event(ec_dev); + + if (!host_event) + return NOTIFY_BAD; + + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU)) { + blocking_notifier_call_chain(&cros_usbpd_notifier_list, + host_event, NULL); + return NOTIFY_OK; + } + return NOTIFY_DONE; +} + +static int cros_usbpd_notify_probe_plat(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent); + struct notifier_block *nb; + int ret; + + nb = devm_kzalloc(dev, sizeof(*nb), GFP_KERNEL); + if (!nb) + return -ENOMEM; + + nb->notifier_call = cros_usbpd_notify_plat; + dev_set_drvdata(dev, nb); + + ret = blocking_notifier_chain_register(&ecdev->ec_dev->event_notifier, + nb); + if (ret < 0) { + dev_err(dev, "Failed to register notifier\n"); + return ret; + } + + return 0; +} + +static int cros_usbpd_notify_remove_plat(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_dev *ecdev = dev_get_drvdata(dev->parent); + struct notifier_block *nb = + (struct notifier_block *)dev_get_drvdata(dev); + + blocking_notifier_chain_unregister(&ecdev->ec_dev->event_notifier, nb); + + return 0; +} + +static struct platform_driver cros_usbpd_notify_plat_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = cros_usbpd_notify_probe_plat, + .remove = cros_usbpd_notify_remove_plat, +}; + +static int __init cros_usbpd_notify_init(void) +{ + int ret; + + ret = platform_driver_register(&cros_usbpd_notify_plat_driver); + if (ret < 0) + return ret; + +#ifdef CONFIG_ACPI + acpi_bus_register_driver(&cros_usbpd_notify_acpi_driver); +#endif + return 0; +} + +static void __exit cros_usbpd_notify_exit(void) +{ +#ifdef CONFIG_ACPI + acpi_bus_unregister_driver(&cros_usbpd_notify_acpi_driver); +#endif + platform_driver_unregister(&cros_usbpd_notify_plat_driver); +} + +module_init(cros_usbpd_notify_init); +module_exit(cros_usbpd_notify_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("ChromeOS power delivery notifier device"); +MODULE_AUTHOR("Jon Flatley "); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/platform_data/cros_usbpd_notify.h b/include/linux/platform_data/cros_usbpd_notify.h new file mode 100644 index 000000000000..4f2791722b6d --- /dev/null +++ b/include/linux/platform_data/cros_usbpd_notify.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ChromeOS EC Power Delivery Notifier Driver + * + * Copyright 2020 Google LLC + */ + +#ifndef __LINUX_PLATFORM_DATA_CROS_USBPD_NOTIFY_H +#define __LINUX_PLATFORM_DATA_CROS_USBPD_NOTIFY_H + +#include + +int cros_usbpd_register_notify(struct notifier_block *nb); + +void cros_usbpd_unregister_notify(struct notifier_block *nb); + +#endif /* __LINUX_PLATFORM_DATA_CROS_USBPD_NOTIFY_H */ -- cgit v1.2.3 From cc7eac1e4afdd151085be4d0341a155760388653 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 27 Jan 2020 17:37:42 +0900 Subject: usb: host: ehci-platform: add a quirk to avoid stuck Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting stuck very rarely after a full/low usb device was disconnected. To detect/recover from such a situation, the controllers require a special way which poll the EHCI PORTSC register and changes the OHCI functional state. So, this patch adds a polling timer into the ehci-platform driver, and if the ehci driver detects the issue by the EHCI PORTSC register, the ehci driver removes a companion device (= the OHCI controller) to change the OHCI functional state to USB Reset once. And then, the ehci driver adds the companion device again. Signed-off-by: Yoshihiro Shimoda Acked-by: Alan Stern Link: https://lore.kernel.org/r/1580114262-25029-1-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 127 +++++++++++++++++++++++++++++++++++++++ include/linux/usb/ehci_def.h | 2 +- 2 files changed, 128 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 769749ca5961..e4fc3f66d43b 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -44,6 +46,9 @@ struct ehci_platform_priv { struct clk *clks[EHCI_MAX_CLKS]; struct reset_control *rsts; bool reset_on_resume; + bool quirk_poll; + struct timer_list poll_timer; + struct delayed_work poll_work; }; static const char hcd_name[] = "ehci-platform"; @@ -118,6 +123,111 @@ static struct usb_ehci_pdata ehci_platform_defaults = { .power_off = ehci_platform_power_off, }; +/** + * quirk_poll_check_port_status - Poll port_status if the device sticks + * @ehci: the ehci hcd pointer + * + * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting + * stuck very rarely after a full/low usb device was disconnected. To + * detect such a situation, the controllers require a special way which poll + * the EHCI PORTSC register. + * + * Return: true if the controller's port_status indicated getting stuck + */ +static bool quirk_poll_check_port_status(struct ehci_hcd *ehci) +{ + u32 port_status = ehci_readl(ehci, &ehci->regs->port_status[0]); + + if (!(port_status & PORT_OWNER) && + (port_status & PORT_POWER) && + !(port_status & PORT_CONNECT) && + (port_status & PORT_LS_MASK)) + return true; + + return false; +} + +/** + * quirk_poll_rebind_companion - rebind comanion device to recover + * @ehci: the ehci hcd pointer + * + * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting + * stuck very rarely after a full/low usb device was disconnected. To + * recover from such a situation, the controllers require changing the OHCI + * functional state. + */ +static void quirk_poll_rebind_companion(struct ehci_hcd *ehci) +{ + struct device *companion_dev; + struct usb_hcd *hcd = ehci_to_hcd(ehci); + + companion_dev = usb_of_get_companion_dev(hcd->self.controller); + if (!companion_dev) + return; + + device_release_driver(companion_dev); + if (device_attach(companion_dev) < 0) + ehci_err(ehci, "%s: failed\n", __func__); + + put_device(companion_dev); +} + +static void quirk_poll_work(struct work_struct *work) +{ + struct ehci_platform_priv *priv = + container_of(to_delayed_work(work), struct ehci_platform_priv, + poll_work); + struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, + priv); + + /* check the status twice to reduce misdetection rate */ + if (!quirk_poll_check_port_status(ehci)) + return; + udelay(10); + if (!quirk_poll_check_port_status(ehci)) + return; + + ehci_dbg(ehci, "%s: detected getting stuck. rebind now!\n", __func__); + quirk_poll_rebind_companion(ehci); +} + +static void quirk_poll_timer(struct timer_list *t) +{ + struct ehci_platform_priv *priv = from_timer(priv, t, poll_timer); + struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd, + priv); + + if (quirk_poll_check_port_status(ehci)) { + /* + * Now scheduling the work for testing the port more. Note that + * updating the status is possible to be delayed when + * reconnection. So, this uses delayed work with 5 ms delay + * to avoid misdetection. + */ + schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(5)); + } + + mod_timer(&priv->poll_timer, jiffies + HZ); +} + +static void quirk_poll_init(struct ehci_platform_priv *priv) +{ + INIT_DELAYED_WORK(&priv->poll_work, quirk_poll_work); + timer_setup(&priv->poll_timer, quirk_poll_timer, 0); + mod_timer(&priv->poll_timer, jiffies + HZ); +} + +static void quirk_poll_end(struct ehci_platform_priv *priv) +{ + del_timer_sync(&priv->poll_timer); + cancel_delayed_work(&priv->poll_work); +} + +static const struct soc_device_attribute quirk_poll_match[] = { + { .family = "R-Car Gen3" }, + { /* sentinel*/ } +}; + static int ehci_platform_probe(struct platform_device *dev) { struct usb_hcd *hcd; @@ -176,6 +286,9 @@ static int ehci_platform_probe(struct platform_device *dev) "has-transaction-translator")) hcd->has_tt = 1; + if (soc_device_match(quirk_poll_match)) + priv->quirk_poll = true; + for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { priv->clks[clk] = of_clk_get(dev->dev.of_node, clk); if (IS_ERR(priv->clks[clk])) { @@ -247,6 +360,9 @@ static int ehci_platform_probe(struct platform_device *dev) device_enable_async_suspend(hcd->self.controller); platform_set_drvdata(dev, hcd); + if (priv->quirk_poll) + quirk_poll_init(priv); + return err; err_power: @@ -273,6 +389,9 @@ static int ehci_platform_remove(struct platform_device *dev) struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); int clk; + if (priv->quirk_poll) + quirk_poll_end(priv); + usb_remove_hcd(hcd); if (pdata->power_off) @@ -297,9 +416,13 @@ static int ehci_platform_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(dev); struct platform_device *pdev = to_platform_device(dev); + struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd); bool do_wakeup = device_may_wakeup(dev); int ret; + if (priv->quirk_poll) + quirk_poll_end(priv); + ret = ehci_suspend(hcd, do_wakeup); if (ret) return ret; @@ -331,6 +454,10 @@ static int ehci_platform_resume(struct device *dev) } ehci_resume(hcd, priv->reset_on_resume); + + if (priv->quirk_poll) + quirk_poll_init(priv); + return 0; } #endif /* CONFIG_PM_SLEEP */ diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index a15ce99dfc2d..78e006355557 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -151,7 +151,7 @@ struct ehci_regs { #define PORT_OWNER (1<<13) /* true: companion hc owns this port */ #define PORT_POWER (1<<12) /* true: has power (see PPC) */ #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ -/* 11:10 for detecting lowspeed devices (reset vs release ownership) */ +#define PORT_LS_MASK (3<<10) /* Link status (SE0, K or J */ /* 9 reserved */ #define PORT_LPM (1<<9) /* LPM transaction */ #define PORT_RESET (1<<8) /* reset port */ -- cgit v1.2.3 From 469e1906a1b121ecb0c2ef43f28a98fd9d453831 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sat, 8 Feb 2020 20:44:07 +0200 Subject: platform: constify properties in platform_device Constify 'struct property_entry *properties' in platform_device. It is always passed around as a pointer const struct. Reviewed-by: Andy Shevchenko Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20200208184407.1294-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/platform_device.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 276a03c24691..8e83c6ff140d 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -89,7 +89,7 @@ struct platform_device_info { size_t size_data; u64 dma_mask; - struct property_entry *properties; + const struct property_entry *properties; }; extern struct platform_device *platform_device_register_full( const struct platform_device_info *pdevinfo); -- cgit v1.2.3 From 901cff7cb96140a658a848a568b606ba764239bc Mon Sep 17 00:00:00 2001 From: Topi Miettinen Date: Thu, 23 Jan 2020 14:58:38 +0200 Subject: firmware_loader: load files from the mount namespace of init I have an experimental setup where almost every possible system service (even early startup ones) runs in separate namespace, using a dedicated, minimal file system. In process of minimizing the contents of the file systems with regards to modules and firmware files, I noticed that in my system, the firmware files are loaded from three different mount namespaces, those of systemd-udevd, init and systemd-networkd. The logic of the source namespace is not very clear, it seems to depend on the driver, but the namespace of the current process is used. So, this patch tries to make things a bit clearer and changes the loading of firmware files only from the mount namespace of init. This may also improve security, though I think that using firmware files as attack vector could be too impractical anyway. Later, it might make sense to make the mount namespace configurable, for example with a new file in /proc/sys/kernel/firmware_config/. That would allow a dedicated file system only for firmware files and those need not be present anywhere else. This configurability would make more sense if made also for kernel modules and /sbin/modprobe. Modules are already loaded from init namespace (usermodehelper uses kthreadd namespace) except when directly loaded by systemd-udevd. Instead of using the mount namespace of the current process to load firmware files, use the mount namespace of init process. Link: https://lore.kernel.org/lkml/bb46ebae-4746-90d9-ec5b-fce4c9328c86@gmail.com/ Link: https://lore.kernel.org/lkml/0e3f7653-c59d-9341-9db2-c88f5b988c68@gmail.com/ Signed-off-by: Topi Miettinen Link: https://lore.kernel.org/r/20200123125839.37168-1-toiwoton@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 6 +- fs/exec.c | 26 ++++ include/linux/fs.h | 2 + tools/testing/selftests/firmware/Makefile | 9 +- tools/testing/selftests/firmware/fw_namespace.c | 151 +++++++++++++++++++++++ tools/testing/selftests/firmware/fw_run_tests.sh | 4 + 6 files changed, 190 insertions(+), 8 deletions(-) create mode 100644 tools/testing/selftests/firmware/fw_namespace.c (limited to 'include') diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 57133a9dad09..59d1dc322080 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -493,8 +493,10 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, } fw_priv->size = 0; - rc = kernel_read_file_from_path(path, &buffer, &size, - msize, id); + + /* load firmware files from the mount namespace of init */ + rc = kernel_read_file_from_path_initns(path, &buffer, + &size, msize, id); if (rc) { if (rc != -ENOENT) dev_warn(device, "loading %s failed with error %d\n", diff --git a/fs/exec.c b/fs/exec.c index db17be51b112..688c824cdac8 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -985,6 +985,32 @@ int kernel_read_file_from_path(const char *path, void **buf, loff_t *size, } EXPORT_SYMBOL_GPL(kernel_read_file_from_path); +int kernel_read_file_from_path_initns(const char *path, void **buf, + loff_t *size, loff_t max_size, + enum kernel_read_file_id id) +{ + struct file *file; + struct path root; + int ret; + + if (!path || !*path) + return -EINVAL; + + task_lock(&init_task); + get_fs_root(init_task.fs, &root); + task_unlock(&init_task); + + file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0); + path_put(&root); + if (IS_ERR(file)) + return PTR_ERR(file); + + ret = kernel_read_file(file, buf, size, max_size, id); + fput(file); + return ret; +} +EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns); + int kernel_read_file_from_fd(int fd, void **buf, loff_t *size, loff_t max_size, enum kernel_read_file_id id) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 3cd4fe6b845e..f814ccd8d929 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3012,6 +3012,8 @@ extern int kernel_read_file(struct file *, void **, loff_t *, loff_t, enum kernel_read_file_id); extern int kernel_read_file_from_path(const char *, void **, loff_t *, loff_t, enum kernel_read_file_id); +extern int kernel_read_file_from_path_initns(const char *, void **, loff_t *, loff_t, + enum kernel_read_file_id); extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t, enum kernel_read_file_id); extern ssize_t kernel_read(struct file *, void *, size_t, loff_t *); diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile index 012b2cf69c11..40211cd8f0e6 100644 --- a/tools/testing/selftests/firmware/Makefile +++ b/tools/testing/selftests/firmware/Makefile @@ -1,13 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only # Makefile for firmware loading selftests - -# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" -all: +CFLAGS = -Wall \ + -O2 TEST_PROGS := fw_run_tests.sh TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_lib.sh +TEST_GEN_FILES := fw_namespace include ../lib.mk - -# Nothing to clean up. -clean: diff --git a/tools/testing/selftests/firmware/fw_namespace.c b/tools/testing/selftests/firmware/fw_namespace.c new file mode 100644 index 000000000000..5ebc1aec7923 --- /dev/null +++ b/tools/testing/selftests/firmware/fw_namespace.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Test triggering of loading of firmware from different mount + * namespaces. Expect firmware to be always loaded from the mount + * namespace of PID 1. */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef CLONE_NEWNS +# define CLONE_NEWNS 0x00020000 +#endif + +static char *fw_path = NULL; + +static void die(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (fw_path) + unlink(fw_path); + umount("/lib/firmware"); + exit(EXIT_FAILURE); +} + +static void trigger_fw(const char *fw_name, const char *sys_path) +{ + int fd; + + fd = open(sys_path, O_WRONLY); + if (fd < 0) + die("open failed: %s\n", + strerror(errno)); + if (write(fd, fw_name, strlen(fw_name)) != strlen(fw_name)) + exit(EXIT_FAILURE); + close(fd); +} + +static void setup_fw(const char *fw_path) +{ + int fd; + const char fw[] = "ABCD0123"; + + fd = open(fw_path, O_WRONLY | O_CREAT, 0600); + if (fd < 0) + die("open failed: %s\n", + strerror(errno)); + if (write(fd, fw, sizeof(fw) -1) != sizeof(fw) -1) + die("write failed: %s\n", + strerror(errno)); + close(fd); +} + +static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_fw_in_parent_ns) +{ + pid_t child; + + if (block_fw_in_parent_ns) + if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) + die("blocking firmware in parent ns failed\n"); + + child = fork(); + if (child == -1) { + die("fork failed: %s\n", + strerror(errno)); + } + if (child != 0) { /* parent */ + pid_t pid; + int status; + + pid = waitpid(child, &status, 0); + if (pid == -1) { + die("waitpid failed: %s\n", + strerror(errno)); + } + if (pid != child) { + die("waited for %d got %d\n", + child, pid); + } + if (!WIFEXITED(status)) { + die("child did not terminate cleanly\n"); + } + if (block_fw_in_parent_ns) + umount("/lib/firmware"); + return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; + } + + if (unshare(CLONE_NEWNS) != 0) { + die("unshare(CLONE_NEWNS) failed: %s\n", + strerror(errno)); + } + if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) == -1) + die("remount root in child ns failed\n"); + + if (!block_fw_in_parent_ns) { + if (mount("test", "/lib/firmware", "tmpfs", MS_RDONLY, NULL) == -1) + die("blocking firmware in child ns failed\n"); + } else + umount("/lib/firmware"); + + trigger_fw(fw_name, sys_path); + + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + const char *fw_name = "test-firmware.bin"; + char *sys_path; + if (argc != 2) + die("usage: %s sys_path\n", argv[0]); + + /* Mount tmpfs to /lib/firmware so we don't have to assume + that it is writable for us.*/ + if (mount("test", "/lib/firmware", "tmpfs", 0, NULL) == -1) + die("mounting tmpfs to /lib/firmware failed\n"); + + sys_path = argv[1]; + asprintf(&fw_path, "/lib/firmware/%s", fw_name); + + setup_fw(fw_path); + + setvbuf(stdout, NULL, _IONBF, 0); + /* Positive case: firmware in PID1 mount namespace */ + printf("Testing with firmware in parent namespace (assumed to be same file system as PID1)\n"); + if (!test_fw_in_ns(fw_name, sys_path, false)) + die("error: failed to access firmware\n"); + + /* Negative case: firmware in child mount namespace, expected to fail */ + printf("Testing with firmware in child namespace\n"); + if (test_fw_in_ns(fw_name, sys_path, true)) + die("error: firmware access did not fail\n"); + + unlink(fw_path); + free(fw_path); + umount("/lib/firmware"); + exit(EXIT_SUCCESS); +} diff --git a/tools/testing/selftests/firmware/fw_run_tests.sh b/tools/testing/selftests/firmware/fw_run_tests.sh index 8e14d555c197..777377078d5e 100755 --- a/tools/testing/selftests/firmware/fw_run_tests.sh +++ b/tools/testing/selftests/firmware/fw_run_tests.sh @@ -61,6 +61,10 @@ run_test_config_0003() check_mods check_setup +echo "Running namespace test: " +$TEST_DIR/fw_namespace $DIR/trigger_request +echo "OK" + if [ -f $FW_FORCE_SYSFS_FALLBACK ]; then run_test_config_0001 run_test_config_0002 -- cgit v1.2.3 From 0ab710458da113a71c461c4df27e7f1353d9f864 Mon Sep 17 00:00:00 2001 From: Bharath Ravi Date: Sat, 25 Jan 2020 01:19:25 -0500 Subject: scsi: iscsi: Perform connection failure entirely in kernel space Connection failure processing depends on a daemon being present to (at least) stop the connection and start recovery. This is a problem on a multipath scenario, where if the daemon failed for whatever reason, the SCSI path is never marked as down, multipath won't perform the failover and IO to the device will be forever waiting for that connection to come back. This patch performs the connection failure entirely inside the kernel. This way, the failover can happen and pending IO can continue even if the daemon is dead. Once the daemon comes alive again, it can execute recovery procedures if applicable. Cc: Mike Christie Cc: Lee Duncan Cc: Bart Van Assche Link: https://lore.kernel.org/r/20200125061925.191601-1-krisman@collabora.com Co-developed-by: Dave Clausen Co-developed-by: Nick Black Co-developed-by: Vaibhav Nagarnaik Co-developed-by: Anatol Pomazau Co-developed-by: Tahsin Erdogan Co-developed-by: Frank Mayhar Co-developed-by: Junho Ryu Co-developed-by: Khazhismel Kumykov Reviewed-by: Reviewed-by: Khazhismel Kumykov Co-developed-by: Gabriel Krisman Bertazi Reviewed-by: Lee Duncan Signed-off-by: Bharath Ravi Signed-off-by: Dave Clausen Signed-off-by: Nick Black Signed-off-by: Vaibhav Nagarnaik Signed-off-by: Anatol Pomazau Signed-off-by: Tahsin Erdogan Signed-off-by: Frank Mayhar Signed-off-by: Junho Ryu Signed-off-by: Khazhismel Kumykov Signed-off-by: Gabriel Krisman Bertazi Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_iscsi.c | 68 +++++++++++++++++++++++++++++++++++++ include/scsi/scsi_transport_iscsi.h | 1 + 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index dfc726fa34e3..f3f0ecdb09a2 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -86,6 +86,12 @@ struct iscsi_internal { struct transport_container session_cont; }; +/* Worker to perform connection failure on unresponsive connections + * completely in kernel space. + */ +static void stop_conn_work_fn(struct work_struct *work); +static DECLARE_WORK(stop_conn_work, stop_conn_work_fn); + static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ static struct workqueue_struct *iscsi_eh_timer_workq; @@ -1611,6 +1617,7 @@ static DEFINE_MUTEX(rx_queue_mutex); static LIST_HEAD(sesslist); static DEFINE_SPINLOCK(sesslock); static LIST_HEAD(connlist); +static LIST_HEAD(connlist_err); static DEFINE_SPINLOCK(connlock); static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn) @@ -2254,6 +2261,7 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid) mutex_init(&conn->ep_mutex); INIT_LIST_HEAD(&conn->conn_list); + INIT_LIST_HEAD(&conn->conn_list_err); conn->transport = transport; conn->cid = cid; @@ -2307,6 +2315,7 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn) spin_lock_irqsave(&connlock, flags); list_del(&conn->conn_list); + list_del(&conn->conn_list_err); spin_unlock_irqrestore(&connlock, flags); transport_unregister_device(&conn->dev); @@ -2421,6 +2430,51 @@ int iscsi_offload_mesg(struct Scsi_Host *shost, } EXPORT_SYMBOL_GPL(iscsi_offload_mesg); +static void stop_conn_work_fn(struct work_struct *work) +{ + struct iscsi_cls_conn *conn, *tmp; + unsigned long flags; + LIST_HEAD(recovery_list); + + spin_lock_irqsave(&connlock, flags); + if (list_empty(&connlist_err)) { + spin_unlock_irqrestore(&connlock, flags); + return; + } + list_splice_init(&connlist_err, &recovery_list); + spin_unlock_irqrestore(&connlock, flags); + + list_for_each_entry_safe(conn, tmp, &recovery_list, conn_list_err) { + uint32_t sid = iscsi_conn_get_sid(conn); + struct iscsi_cls_session *session; + + mutex_lock(&rx_queue_mutex); + + session = iscsi_session_lookup(sid); + if (session) { + if (system_state != SYSTEM_RUNNING) { + session->recovery_tmo = 0; + conn->transport->stop_conn(conn, + STOP_CONN_TERM); + } else { + conn->transport->stop_conn(conn, + STOP_CONN_RECOVER); + } + } + + list_del_init(&conn->conn_list_err); + + mutex_unlock(&rx_queue_mutex); + + /* we don't want to hold rx_queue_mutex for too long, + * for instance if many conns failed at the same time, + * since this stall other iscsi maintenance operations. + * Give other users a chance to proceed. + */ + cond_resched(); + } +} + void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) { struct nlmsghdr *nlh; @@ -2428,6 +2482,12 @@ void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) struct iscsi_uevent *ev; struct iscsi_internal *priv; int len = nlmsg_total_size(sizeof(*ev)); + unsigned long flags; + + spin_lock_irqsave(&connlock, flags); + list_add(&conn->conn_list_err, &connlist_err); + spin_unlock_irqrestore(&connlock, flags); + queue_work(system_unbound_wq, &stop_conn_work); priv = iscsi_if_transport_lookup(conn->transport); if (!priv) @@ -2757,11 +2817,19 @@ static int iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) { struct iscsi_cls_conn *conn; + unsigned long flags; conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid); if (!conn) return -EINVAL; + spin_lock_irqsave(&connlock, flags); + if (!list_empty(&conn->conn_list_err)) { + spin_unlock_irqrestore(&connlock, flags); + return -EAGAIN; + } + spin_unlock_irqrestore(&connlock, flags); + ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n"); if (transport->destroy_conn) transport->destroy_conn(conn); diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 325ae731d9ad..2129dc9e2dec 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -190,6 +190,7 @@ extern void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_cls_conn { struct list_head conn_list; /* item in connlist */ + struct list_head conn_list_err; /* item in connlist_err */ void *dd_data; /* LLD private data */ struct iscsi_transport *transport; uint32_t cid; /* connection id */ -- cgit v1.2.3 From 3d8b24a0f438d812d14849bd42516f54e80b57e8 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 19 Jan 2020 15:14:28 +0800 Subject: scsi: core: remove .for_blk_mq Not in use anymore. Remove the flag. Cc: Sathya Prakash Cc: Chaitra P B Cc: Suganath Prabu Subramani Cc: Kashyap Desai Cc: Sumit Saxena Cc: Shivasharan S Cc: Ewan D. Milne Cc: Christoph Hellwig Cc: Hannes Reinecke Cc: Bart Van Assche Link: https://lore.kernel.org/r/20200119071432.18558-3-ming.lei@redhat.com Reviewed-by: Christoph Hellwig Signed-off-by: Ming Lei Signed-off-by: Martin K. Petersen --- drivers/scsi/virtio_scsi.c | 1 - include/scsi/scsi_host.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'include') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index bfec84aacd90..0e0910c5b942 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -742,7 +742,6 @@ static struct scsi_host_template virtscsi_host_template = { .dma_boundary = UINT_MAX, .map_queues = virtscsi_map_queues, .track_queue_depth = 1, - .force_blk_mq = 1, }; #define virtscsi_config_get(vdev, fld) \ diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index f577647bf5f2..7a97fb8104cf 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -426,9 +426,6 @@ struct scsi_host_template { /* True if the controller does not support WRITE SAME */ unsigned no_write_same:1; - /* True if the low-level driver supports blk-mq only */ - unsigned force_blk_mq:1; - /* * Countdown for host blocking with no commands outstanding. */ -- cgit v1.2.3 From 1de8493069b8f510e586242c1b4329cd3c9b6fb9 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Mon, 10 Feb 2020 18:06:53 +0100 Subject: clk: sunxi-ng: a64: Export MBUS clock MBUS clock will be referenced in MBUS controller node. Export it. Signed-off-by: Jernej Skrabec Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun50i-a64.h | 4 ---- include/dt-bindings/clock/sun50i-a64-ccu.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-a64.h b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h index 116e6f826d04..54d1f96f4b68 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-a64.h +++ b/drivers/clk/sunxi-ng/ccu-sun50i-a64.h @@ -55,10 +55,6 @@ /* All the DRAM gates are exported */ -/* Some more module clocks are exported */ - -#define CLK_MBUS 112 - /* And the DSI and GPU module clock is exported */ #define CLK_NUMBER (CLK_GPU + 1) diff --git a/include/dt-bindings/clock/sun50i-a64-ccu.h b/include/dt-bindings/clock/sun50i-a64-ccu.h index e512a1c9b0fc..318eb15c414c 100644 --- a/include/dt-bindings/clock/sun50i-a64-ccu.h +++ b/include/dt-bindings/clock/sun50i-a64-ccu.h @@ -131,7 +131,7 @@ #define CLK_AVS 109 #define CLK_HDMI 110 #define CLK_HDMI_DDC 111 - +#define CLK_MBUS 112 #define CLK_DSI_DPHY 113 #define CLK_GPU 114 -- cgit v1.2.3 From 8673e944b50ec6e5afd4f599cf12b2798b629f3d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Feb 2020 11:48:28 +0200 Subject: platform/chrome: wilco_ec: Platform data shouldn't include kernel.h Replace with appropriate types.h. Also there is no need to include device.h, but mutex.h. For the pointers to unknown structures use forward declarations. In the *.c files we need to include all headers that provide APIs being used in the module. Signed-off-by: Andy Shevchenko Signed-off-by: Enric Balletbo i Serra --- drivers/platform/chrome/wilco_ec/properties.c | 3 +++ drivers/platform/chrome/wilco_ec/sysfs.c | 4 ++++ include/linux/platform_data/wilco-ec.h | 8 ++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/platform/chrome/wilco_ec/properties.c b/drivers/platform/chrome/wilco_ec/properties.c index 62f27610dd33..c2bf4c95c5d2 100644 --- a/drivers/platform/chrome/wilco_ec/properties.c +++ b/drivers/platform/chrome/wilco_ec/properties.c @@ -3,8 +3,11 @@ * Copyright 2019 Google LLC */ +#include +#include #include #include +#include #include /* Operation code; what the EC should do with the property */ diff --git a/drivers/platform/chrome/wilco_ec/sysfs.c b/drivers/platform/chrome/wilco_ec/sysfs.c index f0d174b6bb21..3c587b4054a5 100644 --- a/drivers/platform/chrome/wilco_ec/sysfs.c +++ b/drivers/platform/chrome/wilco_ec/sysfs.c @@ -8,8 +8,12 @@ * See Documentation/ABI/testing/sysfs-platform-wilco-ec for more information. */ +#include +#include #include +#include #include +#include #define CMD_KB_CMOS 0x7C #define SUB_CMD_KB_CMOS_AUTO_ON 0x03 diff --git a/include/linux/platform_data/wilco-ec.h b/include/linux/platform_data/wilco-ec.h index afede15a95bf..25f46a939637 100644 --- a/include/linux/platform_data/wilco-ec.h +++ b/include/linux/platform_data/wilco-ec.h @@ -8,8 +8,8 @@ #ifndef WILCO_EC_H #define WILCO_EC_H -#include -#include +#include +#include /* Message flags for using the mailbox() interface */ #define WILCO_EC_FLAG_NO_RESPONSE BIT(0) /* EC does not respond */ @@ -17,6 +17,10 @@ /* Normal commands have a maximum 32 bytes of data */ #define EC_MAILBOX_DATA_SIZE 32 +struct device; +struct resource; +struct platform_device; + /** * struct wilco_ec_device - Wilco Embedded Controller handle. * @dev: Device handle. -- cgit v1.2.3 From 42cd0ab476e2daffc23982c37822a78f9a53cdd5 Mon Sep 17 00:00:00 2001 From: Yicheng Li Date: Mon, 3 Feb 2020 14:53:56 -0800 Subject: platform/chrome: cros_ec: Query EC protocol version if EC transitions between RO/RW RO and RW of EC may have different EC protocol version. If EC transitions between RO and RW, but AP does not reboot (this is true for fingerprint microcontroller / cros_fp, but not true for main ec / cros_ec), the AP still uses the protocol version queried before transition, which can cause problems. In the case of fingerprint microcontroller, this causes AP to send the wrong version of EC_CMD_GET_NEXT_EVENT to RO in the interrupt handler, which in turn prevents RO to clear the interrupt line to AP, in an infinite loop. Once an EC_HOST_EVENT_INTERFACE_READY is received, we know that there might have been a transition between RO and RW, so re-query the protocol. Signed-off-by: Yicheng Li Tested-by: Marek Szyprowski Reviewed-by: Gwendal Grignou Signed-off-by: Enric Balletbo i Serra --- drivers/platform/chrome/cros_ec.c | 30 +++++++++++++++++++++++++++++ include/linux/platform_data/cros_ec_proto.h | 4 ++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index 6fc8f2c3ac51..7ee43b2e0654 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -138,6 +138,24 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) return ret; } +static int cros_ec_ready_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct cros_ec_device *ec_dev = container_of(nb, struct cros_ec_device, + notifier_ready); + u32 host_event = cros_ec_get_host_event(ec_dev); + + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INTERFACE_READY)) { + mutex_lock(&ec_dev->lock); + cros_ec_query_all(ec_dev); + mutex_unlock(&ec_dev->lock); + return NOTIFY_OK; + } + + return NOTIFY_DONE; +} + /** * cros_ec_register() - Register a new ChromeOS EC, using the provided info. * @ec_dev: Device to register. @@ -237,6 +255,18 @@ int cros_ec_register(struct cros_ec_device *ec_dev) dev_dbg(ec_dev->dev, "Error %d clearing sleep event to ec", err); + if (ec_dev->mkbp_event_supported) { + /* + * Register the notifier for EC_HOST_EVENT_INTERFACE_READY + * event. + */ + ec_dev->notifier_ready.notifier_call = cros_ec_ready_event; + err = blocking_notifier_chain_register(&ec_dev->event_notifier, + &ec_dev->notifier_ready); + if (err) + return err; + } + dev_info(dev, "Chrome EC device registered\n"); return 0; diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h index ba5914770191..383243326676 100644 --- a/include/linux/platform_data/cros_ec_proto.h +++ b/include/linux/platform_data/cros_ec_proto.h @@ -125,6 +125,9 @@ struct cros_ec_command { * @host_event_wake_mask: Mask of host events that cause wake from suspend. * @last_event_time: exact time from the hard irq when we got notified of * a new event. + * @notifier_ready: The notifier_block to let the kernel re-query EC + * communication protocol when the EC sends + * EC_HOST_EVENT_INTERFACE_READY. * @ec: The platform_device used by the mfd driver to interface with the * main EC. * @pd: The platform_device used by the mfd driver to interface with the @@ -166,6 +169,7 @@ struct cros_ec_device { u32 host_event_wake_mask; u32 last_resume_result; ktime_t last_event_time; + struct notifier_block notifier_ready; /* The platform devices used by the mfd driver */ struct platform_device *ec; -- cgit v1.2.3 From ed31685c96e18f773ca11dd1a637974d62130673 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 3 Feb 2020 15:31:30 +0200 Subject: console: Introduce ->exit() callback Some consoles might require special operations on unregistering. For instance, serial console, when registered in the kernel, keeps power on for entire time, until it gets unregistered. Example of use: ->setup(console): pm_runtime_get(...); ->exit(console): pm_runtime_put(...); For such cases to have a balance we would provide ->exit() callback. Link: http://lkml.kernel.org/r/20200203133130.11591-7-andriy.shevchenko@linux.intel.com To: linux-kernel@vger.kernel.org To: Steven Rostedt Signed-off-by: Andy Shevchenko Reviewed-by: Sergey Senozhatsky Reviewed-by: Petr Mladek Signed-off-by: Petr Mladek --- include/linux/console.h | 1 + kernel/printk/printk.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/console.h b/include/linux/console.h index d09951d5a94e..0cbd2a36aa00 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -149,6 +149,7 @@ struct console { struct tty_driver *(*device)(struct console *, int *); void (*unblank)(void); int (*setup)(struct console *, char *); + int (*exit)(struct console *); int (*match)(struct console *, char *name, int idx, char *options); short flags; short index; diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index f435a17ef988..633f41a11d75 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -2853,6 +2853,9 @@ int unregister_console(struct console *console) console_unlock(); console_sysfs_notify(); + if (console->exit) + res = console->exit(console); + return res; out_disable_unlock: -- cgit v1.2.3 From 1751060e2527462714359573a39dca10451ffbf8 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:01:26 +0100 Subject: locking/percpu-rwsem, lockdep: Make percpu-rwsem use its own lockdep_map As preparation for replacing the embedded rwsem, give percpu-rwsem its own lockdep_map. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151539.927625541@infradead.org --- include/linux/percpu-rwsem.h | 29 +++++++++++++++++++---------- kernel/cpu.c | 4 ++-- kernel/locking/percpu-rwsem.c | 16 ++++++++++++---- kernel/locking/rwsem.c | 4 ++-- kernel/locking/rwsem.h | 2 ++ 5 files changed, 37 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index ad2ca2a89d5b..f2c36fb5e661 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -15,8 +15,17 @@ struct percpu_rw_semaphore { struct rw_semaphore rw_sem; /* slowpath */ struct rcuwait writer; /* blocked writer */ int readers_block; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif }; +#ifdef CONFIG_DEBUG_LOCK_ALLOC +#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname) .dep_map = { .name = #lockname }, +#else +#define __PERCPU_RWSEM_DEP_MAP_INIT(lockname) +#endif + #define __DEFINE_PERCPU_RWSEM(name, is_static) \ static DEFINE_PER_CPU(unsigned int, __percpu_rwsem_rc_##name); \ is_static struct percpu_rw_semaphore name = { \ @@ -24,7 +33,9 @@ is_static struct percpu_rw_semaphore name = { \ .read_count = &__percpu_rwsem_rc_##name, \ .rw_sem = __RWSEM_INITIALIZER(name.rw_sem), \ .writer = __RCUWAIT_INITIALIZER(name.writer), \ + __PERCPU_RWSEM_DEP_MAP_INIT(name) \ } + #define DEFINE_PERCPU_RWSEM(name) \ __DEFINE_PERCPU_RWSEM(name, /* not static */) #define DEFINE_STATIC_PERCPU_RWSEM(name) \ @@ -37,7 +48,7 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem) { might_sleep(); - rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 0, _RET_IP_); + rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_); preempt_disable(); /* @@ -76,13 +87,15 @@ static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem) */ if (ret) - rwsem_acquire_read(&sem->rw_sem.dep_map, 0, 1, _RET_IP_); + rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_); return ret; } static inline void percpu_up_read(struct percpu_rw_semaphore *sem) { + rwsem_release(&sem->dep_map, _RET_IP_); + preempt_disable(); /* * Same as in percpu_down_read(). @@ -92,8 +105,6 @@ static inline void percpu_up_read(struct percpu_rw_semaphore *sem) else __percpu_up_read(sem); /* Unconditional memory barrier */ preempt_enable(); - - rwsem_release(&sem->rw_sem.dep_map, _RET_IP_); } extern void percpu_down_write(struct percpu_rw_semaphore *); @@ -110,15 +121,13 @@ extern void percpu_free_rwsem(struct percpu_rw_semaphore *); __percpu_init_rwsem(sem, #sem, &rwsem_key); \ }) -#define percpu_rwsem_is_held(sem) lockdep_is_held(&(sem)->rw_sem) - -#define percpu_rwsem_assert_held(sem) \ - lockdep_assert_held(&(sem)->rw_sem) +#define percpu_rwsem_is_held(sem) lockdep_is_held(sem) +#define percpu_rwsem_assert_held(sem) lockdep_assert_held(sem) static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { - lock_release(&sem->rw_sem.dep_map, ip); + lock_release(&sem->dep_map, ip); #ifdef CONFIG_RWSEM_SPIN_ON_OWNER if (!read) atomic_long_set(&sem->rw_sem.owner, RWSEM_OWNER_UNKNOWN); @@ -128,7 +137,7 @@ static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, static inline void percpu_rwsem_acquire(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { - lock_acquire(&sem->rw_sem.dep_map, 0, 1, read, 1, NULL, ip); + lock_acquire(&sem->dep_map, 0, 1, read, 1, NULL, ip); #ifdef CONFIG_RWSEM_SPIN_ON_OWNER if (!read) atomic_long_set(&sem->rw_sem.owner, (long)current); diff --git a/kernel/cpu.c b/kernel/cpu.c index 9c706af713fb..221bf6a9e98a 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -331,12 +331,12 @@ void lockdep_assert_cpus_held(void) static void lockdep_acquire_cpus_lock(void) { - rwsem_acquire(&cpu_hotplug_lock.rw_sem.dep_map, 0, 0, _THIS_IP_); + rwsem_acquire(&cpu_hotplug_lock.dep_map, 0, 0, _THIS_IP_); } static void lockdep_release_cpus_lock(void) { - rwsem_release(&cpu_hotplug_lock.rw_sem.dep_map, _THIS_IP_); + rwsem_release(&cpu_hotplug_lock.dep_map, _THIS_IP_); } /* diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index 364d38a0c444..aa2b118d2f88 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -11,7 +11,7 @@ #include "rwsem.h" int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, - const char *name, struct lock_class_key *rwsem_key) + const char *name, struct lock_class_key *key) { sem->read_count = alloc_percpu(int); if (unlikely(!sem->read_count)) @@ -19,9 +19,13 @@ int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ rcu_sync_init(&sem->rss); - __init_rwsem(&sem->rw_sem, name, rwsem_key); + init_rwsem(&sem->rw_sem); rcuwait_init(&sem->writer); sem->readers_block = 0; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + debug_check_no_locks_freed((void *)sem, sizeof(*sem)); + lockdep_init_map(&sem->dep_map, name, key, 0); +#endif return 0; } EXPORT_SYMBOL_GPL(__percpu_init_rwsem); @@ -142,10 +146,12 @@ static bool readers_active_check(struct percpu_rw_semaphore *sem) void percpu_down_write(struct percpu_rw_semaphore *sem) { + rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_); + /* Notify readers to take the slow path. */ rcu_sync_enter(&sem->rss); - down_write(&sem->rw_sem); + __down_write(&sem->rw_sem); /* * Notify new readers to block; up until now, and thus throughout the @@ -168,6 +174,8 @@ EXPORT_SYMBOL_GPL(percpu_down_write); void percpu_up_write(struct percpu_rw_semaphore *sem) { + rwsem_release(&sem->dep_map, _RET_IP_); + /* * Signal the writer is done, no fast path yet. * @@ -183,7 +191,7 @@ void percpu_up_write(struct percpu_rw_semaphore *sem) /* * Release the write lock, this will allow readers back in the game. */ - up_write(&sem->rw_sem); + __up_write(&sem->rw_sem); /* * Once this completes (at least one RCU-sched grace period hence) the diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 0d9b6be9ecc8..30df8dff217b 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -1383,7 +1383,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) /* * lock for writing */ -static inline void __down_write(struct rw_semaphore *sem) +inline void __down_write(struct rw_semaphore *sem) { long tmp = RWSEM_UNLOCKED_VALUE; @@ -1446,7 +1446,7 @@ inline void __up_read(struct rw_semaphore *sem) /* * unlock after writing */ -static inline void __up_write(struct rw_semaphore *sem) +inline void __up_write(struct rw_semaphore *sem) { long tmp; diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h index 2534ce49f648..d0d33a59622d 100644 --- a/kernel/locking/rwsem.h +++ b/kernel/locking/rwsem.h @@ -6,5 +6,7 @@ extern void __down_read(struct rw_semaphore *sem); extern void __up_read(struct rw_semaphore *sem); +extern void __down_write(struct rw_semaphore *sem); +extern void __up_write(struct rw_semaphore *sem); #endif /* __INTERNAL_RWSEM_H */ -- cgit v1.2.3 From 206c98ffbeda588dbbd9d272505c42acbc364a30 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:12:37 +0100 Subject: locking/percpu-rwsem: Convert to bool Use bool where possible. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151539.984626569@infradead.org --- include/linux/percpu-rwsem.h | 6 +++--- kernel/locking/percpu-rwsem.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index f2c36fb5e661..4ceaa1921951 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -41,7 +41,7 @@ is_static struct percpu_rw_semaphore name = { \ #define DEFINE_STATIC_PERCPU_RWSEM(name) \ __DEFINE_PERCPU_RWSEM(name, static) -extern int __percpu_down_read(struct percpu_rw_semaphore *, int); +extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool); extern void __percpu_up_read(struct percpu_rw_semaphore *); static inline void percpu_down_read(struct percpu_rw_semaphore *sem) @@ -69,9 +69,9 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem) preempt_enable(); } -static inline int percpu_down_read_trylock(struct percpu_rw_semaphore *sem) +static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem) { - int ret = 1; + bool ret = true; preempt_disable(); /* diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index aa2b118d2f88..969389df6eee 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -45,7 +45,7 @@ void percpu_free_rwsem(struct percpu_rw_semaphore *sem) } EXPORT_SYMBOL_GPL(percpu_free_rwsem); -int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) +bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) { /* * Due to having preemption disabled the decrement happens on @@ -69,7 +69,7 @@ int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) * release in percpu_up_write(). */ if (likely(!smp_load_acquire(&sem->readers_block))) - return 1; + return true; /* * Per the above comment; we still have preemption disabled and @@ -78,7 +78,7 @@ int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) __percpu_up_read(sem); if (try) - return 0; + return false; /* * We either call schedule() in the wait, or we'll fall through @@ -94,7 +94,7 @@ int __percpu_down_read(struct percpu_rw_semaphore *sem, int try) __up_read(&sem->rw_sem); preempt_disable(); - return 1; + return true; } EXPORT_SYMBOL_GPL(__percpu_down_read); -- cgit v1.2.3 From 71365d40232110f7b029befc9033ea311d680611 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:17:51 +0100 Subject: locking/percpu-rwsem: Move __this_cpu_inc() into the slowpath As preparation to rework __percpu_down_read() move the __this_cpu_inc() into it. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200131151540.041600199@infradead.org --- include/linux/percpu-rwsem.h | 10 ++++++---- kernel/locking/percpu-rwsem.c | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index 4ceaa1921951..bb5b71c1e446 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -59,8 +59,9 @@ static inline void percpu_down_read(struct percpu_rw_semaphore *sem) * and that once the synchronize_rcu() is done, the writer will see * anything we did within this RCU-sched read-size critical section. */ - __this_cpu_inc(*sem->read_count); - if (unlikely(!rcu_sync_is_idle(&sem->rss))) + if (likely(rcu_sync_is_idle(&sem->rss))) + __this_cpu_inc(*sem->read_count); + else __percpu_down_read(sem, false); /* Unconditional memory barrier */ /* * The preempt_enable() prevents the compiler from @@ -77,8 +78,9 @@ static inline bool percpu_down_read_trylock(struct percpu_rw_semaphore *sem) /* * Same as in percpu_down_read(). */ - __this_cpu_inc(*sem->read_count); - if (unlikely(!rcu_sync_is_idle(&sem->rss))) + if (likely(rcu_sync_is_idle(&sem->rss))) + __this_cpu_inc(*sem->read_count); + else ret = __percpu_down_read(sem, true); /* Unconditional memory barrier */ preempt_enable(); /* diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index 969389df6eee..becf925b27b5 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -47,6 +47,8 @@ EXPORT_SYMBOL_GPL(percpu_free_rwsem); bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) { + __this_cpu_inc(*sem->read_count); + /* * Due to having preemption disabled the decrement happens on * the same CPU as the increment, avoiding the -- cgit v1.2.3 From 7f26482a872c36b2ee87ea95b9dcd96e3d5805df Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 30 Oct 2019 20:30:41 +0100 Subject: locking/percpu-rwsem: Remove the embedded rwsem The filesystem freezer uses percpu-rwsem in a way that is effectively write_non_owner() and achieves this with a few horrible hacks that rely on the rwsem (!percpu) implementation. When PREEMPT_RT replaces the rwsem implementation with a PI aware variant this comes apart. Remove the embedded rwsem and implement it using a waitqueue and an atomic_t. - make readers_block an atomic, and use it, with the waitqueue for a blocking test-and-set write-side. - have the read-side wait for the 'lock' state to clear. Have the waiters use FIFO queueing and mark them (reader/writer) with a new WQ_FLAG. Use a custom wake_function to wake either a single writer or all readers until a writer. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200204092403.GB14879@hirez.programming.kicks-ass.net --- include/linux/percpu-rwsem.h | 19 ++---- include/linux/wait.h | 1 + kernel/locking/percpu-rwsem.c | 153 +++++++++++++++++++++++++++++++----------- kernel/locking/rwsem.c | 9 ++- kernel/locking/rwsem.h | 12 ---- 5 files changed, 123 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index bb5b71c1e446..f5ecf6a8a1dd 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -3,18 +3,18 @@ #define _LINUX_PERCPU_RWSEM_H #include -#include #include #include +#include #include #include struct percpu_rw_semaphore { struct rcu_sync rss; unsigned int __percpu *read_count; - struct rw_semaphore rw_sem; /* slowpath */ - struct rcuwait writer; /* blocked writer */ - int readers_block; + struct rcuwait writer; + wait_queue_head_t waiters; + atomic_t block; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif @@ -31,8 +31,9 @@ static DEFINE_PER_CPU(unsigned int, __percpu_rwsem_rc_##name); \ is_static struct percpu_rw_semaphore name = { \ .rss = __RCU_SYNC_INITIALIZER(name.rss), \ .read_count = &__percpu_rwsem_rc_##name, \ - .rw_sem = __RWSEM_INITIALIZER(name.rw_sem), \ .writer = __RCUWAIT_INITIALIZER(name.writer), \ + .waiters = __WAIT_QUEUE_HEAD_INITIALIZER(name.waiters), \ + .block = ATOMIC_INIT(0), \ __PERCPU_RWSEM_DEP_MAP_INIT(name) \ } @@ -130,20 +131,12 @@ static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { lock_release(&sem->dep_map, ip); -#ifdef CONFIG_RWSEM_SPIN_ON_OWNER - if (!read) - atomic_long_set(&sem->rw_sem.owner, RWSEM_OWNER_UNKNOWN); -#endif } static inline void percpu_rwsem_acquire(struct percpu_rw_semaphore *sem, bool read, unsigned long ip) { lock_acquire(&sem->dep_map, 0, 1, read, 1, NULL, ip); -#ifdef CONFIG_RWSEM_SPIN_ON_OWNER - if (!read) - atomic_long_set(&sem->rw_sem.owner, (long)current); -#endif } #endif diff --git a/include/linux/wait.h b/include/linux/wait.h index 3283c8d02137..feeb6be5cad6 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -20,6 +20,7 @@ int default_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int #define WQ_FLAG_EXCLUSIVE 0x01 #define WQ_FLAG_WOKEN 0x02 #define WQ_FLAG_BOOKMARK 0x04 +#define WQ_FLAG_CUSTOM 0x08 /* * A single wait-queue entry structure: diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index b155e8e7ac39..a136677543b4 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -1,15 +1,14 @@ // SPDX-License-Identifier: GPL-2.0-only #include -#include #include +#include #include #include #include #include +#include #include -#include "rwsem.h" - int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, const char *name, struct lock_class_key *key) { @@ -17,11 +16,10 @@ int __percpu_init_rwsem(struct percpu_rw_semaphore *sem, if (unlikely(!sem->read_count)) return -ENOMEM; - /* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */ rcu_sync_init(&sem->rss); - init_rwsem(&sem->rw_sem); rcuwait_init(&sem->writer); - sem->readers_block = 0; + init_waitqueue_head(&sem->waiters); + atomic_set(&sem->block, 0); #ifdef CONFIG_DEBUG_LOCK_ALLOC debug_check_no_locks_freed((void *)sem, sizeof(*sem)); lockdep_init_map(&sem->dep_map, name, key, 0); @@ -54,23 +52,23 @@ static bool __percpu_down_read_trylock(struct percpu_rw_semaphore *sem) * the same CPU as the increment, avoiding the * increment-on-one-CPU-and-decrement-on-another problem. * - * If the reader misses the writer's assignment of readers_block, then - * the writer is guaranteed to see the reader's increment. + * If the reader misses the writer's assignment of sem->block, then the + * writer is guaranteed to see the reader's increment. * * Conversely, any readers that increment their sem->read_count after - * the writer looks are guaranteed to see the readers_block value, - * which in turn means that they are guaranteed to immediately - * decrement their sem->read_count, so that it doesn't matter that the - * writer missed them. + * the writer looks are guaranteed to see the sem->block value, which + * in turn means that they are guaranteed to immediately decrement + * their sem->read_count, so that it doesn't matter that the writer + * missed them. */ smp_mb(); /* A matches D */ /* - * If !readers_block the critical section starts here, matched by the + * If !sem->block the critical section starts here, matched by the * release in percpu_up_write(). */ - if (likely(!smp_load_acquire(&sem->readers_block))) + if (likely(!atomic_read_acquire(&sem->block))) return true; __this_cpu_dec(*sem->read_count); @@ -81,6 +79,88 @@ static bool __percpu_down_read_trylock(struct percpu_rw_semaphore *sem) return false; } +static inline bool __percpu_down_write_trylock(struct percpu_rw_semaphore *sem) +{ + if (atomic_read(&sem->block)) + return false; + + return atomic_xchg(&sem->block, 1) == 0; +} + +static bool __percpu_rwsem_trylock(struct percpu_rw_semaphore *sem, bool reader) +{ + if (reader) { + bool ret; + + preempt_disable(); + ret = __percpu_down_read_trylock(sem); + preempt_enable(); + + return ret; + } + return __percpu_down_write_trylock(sem); +} + +/* + * The return value of wait_queue_entry::func means: + * + * <0 - error, wakeup is terminated and the error is returned + * 0 - no wakeup, a next waiter is tried + * >0 - woken, if EXCLUSIVE, counted towards @nr_exclusive. + * + * We use EXCLUSIVE for both readers and writers to preserve FIFO order, + * and play games with the return value to allow waking multiple readers. + * + * Specifically, we wake readers until we've woken a single writer, or until a + * trylock fails. + */ +static int percpu_rwsem_wake_function(struct wait_queue_entry *wq_entry, + unsigned int mode, int wake_flags, + void *key) +{ + struct task_struct *p = get_task_struct(wq_entry->private); + bool reader = wq_entry->flags & WQ_FLAG_CUSTOM; + struct percpu_rw_semaphore *sem = key; + + /* concurrent against percpu_down_write(), can get stolen */ + if (!__percpu_rwsem_trylock(sem, reader)) + return 1; + + list_del_init(&wq_entry->entry); + smp_store_release(&wq_entry->private, NULL); + + wake_up_process(p); + put_task_struct(p); + + return !reader; /* wake (readers until) 1 writer */ +} + +static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader) +{ + DEFINE_WAIT_FUNC(wq_entry, percpu_rwsem_wake_function); + bool wait; + + spin_lock_irq(&sem->waiters.lock); + /* + * Serialize against the wakeup in percpu_up_write(), if we fail + * the trylock, the wakeup must see us on the list. + */ + wait = !__percpu_rwsem_trylock(sem, reader); + if (wait) { + wq_entry.flags |= WQ_FLAG_EXCLUSIVE | reader * WQ_FLAG_CUSTOM; + __add_wait_queue_entry_tail(&sem->waiters, &wq_entry); + } + spin_unlock_irq(&sem->waiters.lock); + + while (wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!smp_load_acquire(&wq_entry.private)) + break; + schedule(); + } + __set_current_state(TASK_RUNNING); +} + bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) { if (__percpu_down_read_trylock(sem)) @@ -89,20 +169,10 @@ bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) if (try) return false; - /* - * We either call schedule() in the wait, or we'll fall through - * and reschedule on the preempt_enable() in percpu_down_read(). - */ - preempt_enable_no_resched(); - - /* - * Avoid lockdep for the down/up_read() we already have them. - */ - __down_read(&sem->rw_sem); - this_cpu_inc(*sem->read_count); - __up_read(&sem->rw_sem); - + preempt_enable(); + percpu_rwsem_wait(sem, /* .reader = */ true); preempt_disable(); + return true; } EXPORT_SYMBOL_GPL(__percpu_down_read); @@ -117,7 +187,7 @@ void __percpu_up_read(struct percpu_rw_semaphore *sem) */ __this_cpu_dec(*sem->read_count); - /* Prod writer to recheck readers_active */ + /* Prod writer to re-evaluate readers_active_check() */ rcuwait_wake_up(&sem->writer); } EXPORT_SYMBOL_GPL(__percpu_up_read); @@ -137,6 +207,8 @@ EXPORT_SYMBOL_GPL(__percpu_up_read); * zero. If this sum is zero, then it is stable due to the fact that if any * newly arriving readers increment a given counter, they will immediately * decrement that same counter. + * + * Assumes sem->block is set. */ static bool readers_active_check(struct percpu_rw_semaphore *sem) { @@ -160,23 +232,22 @@ void percpu_down_write(struct percpu_rw_semaphore *sem) /* Notify readers to take the slow path. */ rcu_sync_enter(&sem->rss); - __down_write(&sem->rw_sem); - /* - * Notify new readers to block; up until now, and thus throughout the - * longish rcu_sync_enter() above, new readers could still come in. + * Try set sem->block; this provides writer-writer exclusion. + * Having sem->block set makes new readers block. */ - WRITE_ONCE(sem->readers_block, 1); + if (!__percpu_down_write_trylock(sem)) + percpu_rwsem_wait(sem, /* .reader = */ false); - smp_mb(); /* D matches A */ + /* smp_mb() implied by __percpu_down_write_trylock() on success -- D matches A */ /* - * If they don't see our writer of readers_block, then we are - * guaranteed to see their sem->read_count increment, and therefore - * will wait for them. + * If they don't see our store of sem->block, then we are guaranteed to + * see their sem->read_count increment, and therefore will wait for + * them. */ - /* Wait for all now active readers to complete. */ + /* Wait for all active readers to complete. */ rcuwait_wait_event(&sem->writer, readers_active_check(sem)); } EXPORT_SYMBOL_GPL(percpu_down_write); @@ -195,12 +266,12 @@ void percpu_up_write(struct percpu_rw_semaphore *sem) * Therefore we force it through the slow path which guarantees an * acquire and thereby guarantees the critical section's consistency. */ - smp_store_release(&sem->readers_block, 0); + atomic_set_release(&sem->block, 0); /* - * Release the write lock, this will allow readers back in the game. + * Prod any pending reader/writer to make progress. */ - __up_write(&sem->rw_sem); + __wake_up(&sem->waiters, TASK_NORMAL, 1, sem); /* * Once this completes (at least one RCU-sched grace period hence) the diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 30df8dff217b..81c0d75d5dab 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -28,7 +28,6 @@ #include #include -#include "rwsem.h" #include "lock_events.h" /* @@ -1338,7 +1337,7 @@ static struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem) /* * lock for reading */ -inline void __down_read(struct rw_semaphore *sem) +static inline void __down_read(struct rw_semaphore *sem) { if (!rwsem_read_trylock(sem)) { rwsem_down_read_slowpath(sem, TASK_UNINTERRUPTIBLE); @@ -1383,7 +1382,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem) /* * lock for writing */ -inline void __down_write(struct rw_semaphore *sem) +static inline void __down_write(struct rw_semaphore *sem) { long tmp = RWSEM_UNLOCKED_VALUE; @@ -1426,7 +1425,7 @@ static inline int __down_write_trylock(struct rw_semaphore *sem) /* * unlock after reading */ -inline void __up_read(struct rw_semaphore *sem) +static inline void __up_read(struct rw_semaphore *sem) { long tmp; @@ -1446,7 +1445,7 @@ inline void __up_read(struct rw_semaphore *sem) /* * unlock after writing */ -inline void __up_write(struct rw_semaphore *sem) +static inline void __up_write(struct rw_semaphore *sem) { long tmp; diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h index d0d33a59622d..e69de29bb2d1 100644 --- a/kernel/locking/rwsem.h +++ b/kernel/locking/rwsem.h @@ -1,12 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef __INTERNAL_RWSEM_H -#define __INTERNAL_RWSEM_H -#include - -extern void __down_read(struct rw_semaphore *sem); -extern void __up_read(struct rw_semaphore *sem); -extern void __down_write(struct rw_semaphore *sem); -extern void __up_write(struct rw_semaphore *sem); - -#endif /* __INTERNAL_RWSEM_H */ -- cgit v1.2.3 From bcba67cd806800fa8e973ac49dbc7d2d8fb3e55e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 4 Feb 2020 09:34:37 +0100 Subject: locking/rwsem: Remove RWSEM_OWNER_UNKNOWN Remove the now unused RWSEM_OWNER_UNKNOWN hack. This hack breaks PREEMPT_RT and getting rid of it was the entire motivation for re-writing the percpu rwsem. The biggest problem is that it is fundamentally incompatible with any form of Priority Inheritance, any exclusively held lock must have a distinct owner. Requested-by: Christoph Hellwig Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Davidlohr Bueso Acked-by: Will Deacon Acked-by: Waiman Long Tested-by: Juri Lelli Link: https://lkml.kernel.org/r/20200204092228.GP14946@hirez.programming.kicks-ass.net --- include/linux/rwsem.h | 6 ------ kernel/locking/rwsem.c | 2 -- 2 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 00d6054687dd..8a418d9eeb7a 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -53,12 +53,6 @@ struct rw_semaphore { #endif }; -/* - * Setting all bits of the owner field except bit 0 will indicate - * that the rwsem is writer-owned with an unknown owner. - */ -#define RWSEM_OWNER_UNKNOWN (-2L) - /* In all implementations count != 0 means locked */ static inline int rwsem_is_locked(struct rw_semaphore *sem) { diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c index 81c0d75d5dab..e6f437bafb23 100644 --- a/kernel/locking/rwsem.c +++ b/kernel/locking/rwsem.c @@ -659,8 +659,6 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem, unsigned long flags; bool ret = true; - BUILD_BUG_ON(!(RWSEM_OWNER_UNKNOWN & RWSEM_NONSPINNABLE)); - if (need_resched()) { lockevent_inc(rwsem_opt_fail); return false; -- cgit v1.2.3 From ac8dec420970f5cbaf2f6eda39153a60ec5b257b Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Mon, 18 Nov 2019 15:19:35 -0800 Subject: locking/percpu-rwsem: Fold __percpu_up_read() Now that __percpu_up_read() is only ever used from percpu_up_read() merge them, it's a small function. Signed-off-by: Davidlohr Bueso Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Will Deacon Acked-by: Waiman Long Link: https://lkml.kernel.org/r/20200131151540.212415454@infradead.org --- include/linux/percpu-rwsem.h | 19 +++++++++++++++---- kernel/exit.c | 1 + kernel/locking/percpu-rwsem.c | 15 --------------- 3 files changed, 16 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index f5ecf6a8a1dd..5e033fe1ff4e 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -43,7 +43,6 @@ is_static struct percpu_rw_semaphore name = { \ __DEFINE_PERCPU_RWSEM(name, static) extern bool __percpu_down_read(struct percpu_rw_semaphore *, bool); -extern void __percpu_up_read(struct percpu_rw_semaphore *); static inline void percpu_down_read(struct percpu_rw_semaphore *sem) { @@ -103,10 +102,22 @@ static inline void percpu_up_read(struct percpu_rw_semaphore *sem) /* * Same as in percpu_down_read(). */ - if (likely(rcu_sync_is_idle(&sem->rss))) + if (likely(rcu_sync_is_idle(&sem->rss))) { __this_cpu_dec(*sem->read_count); - else - __percpu_up_read(sem); /* Unconditional memory barrier */ + } else { + /* + * slowpath; reader will only ever wake a single blocked + * writer. + */ + smp_mb(); /* B matches C */ + /* + * In other words, if they see our decrement (presumably to + * aggregate zero, as that is the only time it matters) they + * will also see our critical section. + */ + __this_cpu_dec(*sem->read_count); + rcuwait_wake_up(&sem->writer); + } preempt_enable(); } diff --git a/kernel/exit.c b/kernel/exit.c index 2833ffb0c211..f64a8f9d412a 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -258,6 +258,7 @@ void rcuwait_wake_up(struct rcuwait *w) wake_up_process(task); rcu_read_unlock(); } +EXPORT_SYMBOL_GPL(rcuwait_wake_up); /* * Determine if a process group is "orphaned", according to the POSIX diff --git a/kernel/locking/percpu-rwsem.c b/kernel/locking/percpu-rwsem.c index a136677543b4..8048a9a255d5 100644 --- a/kernel/locking/percpu-rwsem.c +++ b/kernel/locking/percpu-rwsem.c @@ -177,21 +177,6 @@ bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try) } EXPORT_SYMBOL_GPL(__percpu_down_read); -void __percpu_up_read(struct percpu_rw_semaphore *sem) -{ - smp_mb(); /* B matches C */ - /* - * In other words, if they see our decrement (presumably to aggregate - * zero, as that is the only time it matters) they will also see our - * critical section. - */ - __this_cpu_dec(*sem->read_count); - - /* Prod writer to re-evaluate readers_active_check() */ - rcuwait_wake_up(&sem->writer); -} -EXPORT_SYMBOL_GPL(__percpu_up_read); - #define per_cpu_sum(var) \ ({ \ typeof(var) __sum = 0; \ -- cgit v1.2.3 From bbfd5e4fab63703375eafaf241a0c696024a59e1 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Mon, 27 Jan 2020 08:53:54 -0800 Subject: perf/core: Add new branch sample type for HW index of raw branch records The low level index is the index in the underlying hardware buffer of the most recently captured taken branch which is always saved in branch_entries[0]. It is very useful for reconstructing the call stack. For example, in Intel LBR call stack mode, the depth of reconstructed LBR call stack limits to the number of LBR registers. With the low level index information, perf tool may stitch the stacks of two samples. The reconstructed LBR call stack can break the HW limitation. Add a new branch sample type to retrieve low level index of raw branch records. The low level index is between -1 (unknown) and max depth which can be retrieved in /sys/devices/cpu/caps/branches. Only when the new branch sample type is set, the low level index information is dumped into the PERF_SAMPLE_BRANCH_STACK output. Perf tool should check the attr.branch_sample_type, and apply the corresponding format for PERF_SAMPLE_BRANCH_STACK samples. Otherwise, some user case may be broken. For example, users may parse a perf.data, which include the new branch sample type, with an old version perf tool (without the check). Users probably get incorrect information without any warning. Signed-off-by: Kan Liang Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200127165355.27495-2-kan.liang@linux.intel.com --- arch/powerpc/perf/core-book3s.c | 1 + arch/x86/events/intel/lbr.c | 3 +++ include/linux/perf_event.h | 12 ++++++++++++ include/uapi/linux/perf_event.h | 8 +++++++- kernel/events/core.c | 10 ++++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/powerpc/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index 3086055bf681..3dcfecf858f3 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -518,6 +518,7 @@ static void power_pmu_bhrb_read(struct perf_event *event, struct cpu_hw_events * } } cpuhw->bhrb_stack.nr = u_index; + cpuhw->bhrb_stack.hw_idx = -1ULL; return; } diff --git a/arch/x86/events/intel/lbr.c b/arch/x86/events/intel/lbr.c index 534c76606049..7639e2097101 100644 --- a/arch/x86/events/intel/lbr.c +++ b/arch/x86/events/intel/lbr.c @@ -585,6 +585,7 @@ static void intel_pmu_lbr_read_32(struct cpu_hw_events *cpuc) cpuc->lbr_entries[i].reserved = 0; } cpuc->lbr_stack.nr = i; + cpuc->lbr_stack.hw_idx = -1ULL; } /* @@ -680,6 +681,7 @@ static void intel_pmu_lbr_read_64(struct cpu_hw_events *cpuc) out++; } cpuc->lbr_stack.nr = out; + cpuc->lbr_stack.hw_idx = -1ULL; } void intel_pmu_lbr_read(void) @@ -1120,6 +1122,7 @@ void intel_pmu_store_pebs_lbrs(struct pebs_lbr *lbr) int i; cpuc->lbr_stack.nr = x86_pmu.lbr_nr; + cpuc->lbr_stack.hw_idx = -1ULL; for (i = 0; i < x86_pmu.lbr_nr; i++) { u64 info = lbr->lbr[i].info; struct perf_branch_entry *e = &cpuc->lbr_entries[i]; diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 547773f5894e..68e21e828893 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -93,14 +93,26 @@ struct perf_raw_record { /* * branch stack layout: * nr: number of taken branches stored in entries[] + * hw_idx: The low level index of raw branch records + * for the most recent branch. + * -1ULL means invalid/unknown. * * Note that nr can vary from sample to sample * branches (to, from) are stored from most recent * to least recent, i.e., entries[0] contains the most * recent branch. + * The entries[] is an abstraction of raw branch records, + * which may not be stored in age order in HW, e.g. Intel LBR. + * The hw_idx is to expose the low level index of raw + * branch record for the most recent branch aka entries[0]. + * The hw_idx index is between -1 (unknown) and max depth, + * which can be retrieved in /sys/devices/cpu/caps/branches. + * For the architectures whose raw branch records are + * already stored in age order, the hw_idx should be 0. */ struct perf_branch_stack { __u64 nr; + __u64 hw_idx; struct perf_branch_entry entries[0]; }; diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 377d794d3105..397cfd65b3fe 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -181,6 +181,8 @@ enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT = 16, /* save branch type */ + PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */ + PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ }; @@ -208,6 +210,8 @@ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_TYPE_SAVE = 1U << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT, + PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT, + PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; @@ -853,7 +857,9 @@ enum perf_event_type { * char data[size];}&& PERF_SAMPLE_RAW * * { u64 nr; - * { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK + * { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX + * { u64 from, to, flags } lbr[nr]; + * } && PERF_SAMPLE_BRANCH_STACK * * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER diff --git a/kernel/events/core.c b/kernel/events/core.c index e453589da97c..3f1f77de7247 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -6555,6 +6555,11 @@ static void perf_output_read(struct perf_output_handle *handle, perf_output_read_one(handle, event, enabled, running); } +static inline bool perf_sample_save_hw_index(struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX; +} + void perf_output_sample(struct perf_output_handle *handle, struct perf_event_header *header, struct perf_sample_data *data, @@ -6643,6 +6648,8 @@ void perf_output_sample(struct perf_output_handle *handle, * sizeof(struct perf_branch_entry); perf_output_put(handle, data->br_stack->nr); + if (perf_sample_save_hw_index(event)) + perf_output_put(handle, data->br_stack->hw_idx); perf_output_copy(handle, data->br_stack->entries, size); } else { /* @@ -6836,6 +6843,9 @@ void perf_prepare_sample(struct perf_event_header *header, if (sample_type & PERF_SAMPLE_BRANCH_STACK) { int size = sizeof(u64); /* nr */ if (data->br_stack) { + if (perf_sample_save_hw_index(event)) + size += sizeof(u64); + size += data->br_stack->nr * sizeof(struct perf_branch_entry); } -- cgit v1.2.3 From 09e88f8a5c56ac5258935a5a543868c20a55d4dd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Feb 2020 12:14:22 +0900 Subject: ASoC: soc-pcm: add for_each_dapm_widgets() macro This patch adds new for_each_dapm_widgets() macro and use it. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Link: https://lore.kernel.org/r/878slbceyg.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 5 +++++ sound/soc/soc-dapm.c | 8 ++------ sound/soc/soc-pcm.c | 17 +++++++++-------- 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 2a306c6f3fbc..9439e75945f6 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -693,6 +693,11 @@ struct snd_soc_dapm_widget_list { struct snd_soc_dapm_widget *widgets[0]; }; +#define for_each_dapm_widgets(list, i, widget) \ + for ((i) = 0; \ + (i) < list->num_widgets && (widget = list->widgets[i]); \ + (i)++) + struct snd_soc_dapm_stats { int power_checks; int path_checks; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index bc20ad9abf8b..cc17a3730d3d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1724,9 +1724,7 @@ static void dapm_widget_update(struct snd_soc_card *card) wlist = dapm_kcontrol_get_wlist(update->kcontrol); - for (wi = 0; wi < wlist->num_widgets; wi++) { - w = wlist->widgets[wi]; - + for_each_dapm_widgets(wlist, wi, w) { if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); if (ret != 0) @@ -1753,9 +1751,7 @@ static void dapm_widget_update(struct snd_soc_card *card) w->name, ret); } - for (wi = 0; wi < wlist->num_widgets; wi++) { - w = wlist->widgets[wi]; - + for_each_dapm_widgets(wlist, wi, w) { if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) { ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); if (ret != 0) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 2a4f7ac5f563..7a490c05d4e9 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1306,12 +1306,12 @@ static inline struct snd_soc_dapm_widget * static int widget_in_list(struct snd_soc_dapm_widget_list *list, struct snd_soc_dapm_widget *widget) { + struct snd_soc_dapm_widget *w; int i; - for (i = 0; i < list->num_widgets; i++) { - if (widget == list->widgets[i]) + for_each_dapm_widgets(list, i, w) + if (widget == w) return 1; - } return 0; } @@ -1422,12 +1422,13 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_card *card = fe->card; struct snd_soc_dapm_widget_list *list = *list_; struct snd_soc_pcm_runtime *be; + struct snd_soc_dapm_widget *widget; int i, new = 0, err; /* Create any new FE <--> BE connections */ - for (i = 0; i < list->num_widgets; i++) { + for_each_dapm_widgets(list, i, widget) { - switch (list->widgets[i]->id) { + switch (widget->id) { case snd_soc_dapm_dai_in: if (stream != SNDRV_PCM_STREAM_PLAYBACK) continue; @@ -1441,10 +1442,10 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, } /* is there a valid BE rtd for this widget */ - be = dpcm_get_be(card, list->widgets[i], stream); + be = dpcm_get_be(card, widget, stream); if (!be) { dev_err(fe->dev, "ASoC: no BE found for %s\n", - list->widgets[i]->name); + widget->name); continue; } @@ -1460,7 +1461,7 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream, err = dpcm_be_connect(fe, be, stream); if (err < 0) { dev_err(fe->dev, "ASoC: can't connect %s\n", - list->widgets[i]->name); + widget->name); break; } else if (err == 0) /* already connected */ continue; -- cgit v1.2.3 From b56be800f1292c9b79c4f66571c701551bdf9e12 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Feb 2020 12:14:33 +0900 Subject: ASoC: soc-pcm: call snd_soc_dai_startup()/shutdown() once Current soc_pcm_open() calls snd_soc_dai_startup() under loop. Thus, it needs to care about started/not-yet-started codec DAI. But, if soc-dai.c is handling it, soc-pcm.c don't need to care about it. This patch adds started flag to soc-dai.h, and simplify soc-pcm.c. This is one of prepare for cleanup soc-pcm-open() Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/875zgfcey5.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 5 ++++- sound/soc/soc-dai.c | 11 +++++++++-- sound/soc/soc-pcm.c | 7 ++----- 3 files changed, 15 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index eaaeb00e9e84..04c23ac0dfff 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -324,7 +324,6 @@ struct snd_soc_dai { /* DAI runtime info */ unsigned int capture_active; /* stream usage count */ unsigned int playback_active; /* stream usage count */ - unsigned int probed:1; unsigned int active; @@ -348,6 +347,10 @@ struct snd_soc_dai { unsigned int rx_mask; struct list_head list; + + /* bit field */ + unsigned int probed:1; + unsigned int started:1; }; static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 51031e330179..73a829393652 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -295,17 +295,24 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, { int ret = 0; - if (dai->driver->ops->startup) + if (!dai->started && + dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); + if (ret == 0) + dai->started = 1; + return ret; } void snd_soc_dai_shutdown(struct snd_soc_dai *dai, struct snd_pcm_substream *substream) { - if (dai->driver->ops->shutdown) + if (dai->started && + dai->driver->ops->shutdown) dai->driver->ops->shutdown(substream, dai); + + dai->started = 0; } int snd_soc_dai_prepare(struct snd_soc_dai *dai, diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 8d8ed4774e9c..d53afb96b05b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -568,7 +568,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) { pr_err("ASoC: %s startup failed: %d\n", rtd->dai_link->name, ret); - goto machine_err; + goto codec_dai_err; } /* Dynamic PCM DAI links compat checks use dynamic capabilities */ @@ -637,11 +637,8 @@ dynamic: config_err: soc_rtd_shutdown(rtd, substream); -machine_err: - i = rtd->num_codecs; - codec_dai_err: - for_each_rtd_codec_dai_rollback(rtd, i, codec_dai) + for_each_rtd_codec_dai(rtd, i, codec_dai) snd_soc_dai_shutdown(codec_dai, substream); component_err: -- cgit v1.2.3 From d5e205079c34aa1f33157627814f707d6057727a Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 3 Feb 2020 19:05:35 +0530 Subject: drivers: qcom: rpmh: remove rpmh_flush export rpmh_flush() was exported with the idea that an external entity operation during CPU idle would know when to flush the sleep and wake TCS. Since, this is not the case when defining a power domain for the RSC. Remove the function export and instead allow the function to be called internally. Signed-off-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1580736940-6985-3-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 1 + drivers/soc/qcom/rpmh.c | 18 ++++++++---------- include/soc/qcom/rpmh.h | 5 ----- 3 files changed, 9 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index a7bbbb67991c..6eec32b97f83 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -110,5 +110,6 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, int rpmh_rsc_invalidate(struct rsc_drv *drv); void rpmh_tx_done(const struct tcs_request *msg, int r); +int rpmh_flush(struct rpmh_ctrlr *ctrlr); #endif /* __RPM_INTERNAL_H__ */ diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 3a4579d056a4..eb0ded059d2e 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -427,11 +427,10 @@ static int is_req_valid(struct cache_req *req) req->sleep_val != req->wake_val); } -static int send_single(const struct device *dev, enum rpmh_state state, +static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, u32 addr, u32 data) { - DEFINE_RPMH_MSG_ONSTACK(dev, state, NULL, rpm_msg); - struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + DEFINE_RPMH_MSG_ONSTACK(NULL, state, NULL, rpm_msg); /* Wake sets are always complete and sleep sets are not */ rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE); @@ -445,7 +444,7 @@ static int send_single(const struct device *dev, enum rpmh_state state, /** * rpmh_flush: Flushes the buffered active and sleep sets to TCS * - * @dev: The device making the request + * @ctrlr: controller making request to flush cached data * * Return: -EBUSY if the controller is busy, probably waiting on a response * to a RPMH request sent earlier. @@ -454,10 +453,9 @@ static int send_single(const struct device *dev, enum rpmh_state state, * that is powering down the entire system. Since no other RPMH API would be * executing at this time, it is safe to run lockless. */ -int rpmh_flush(const struct device *dev) +int rpmh_flush(struct rpmh_ctrlr *ctrlr) { struct cache_req *p; - struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); int ret; if (!ctrlr->dirty) { @@ -480,11 +478,12 @@ int rpmh_flush(const struct device *dev) __func__, p->addr, p->sleep_val, p->wake_val); continue; } - ret = send_single(dev, RPMH_SLEEP_STATE, p->addr, p->sleep_val); + ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, + p->sleep_val); if (ret) return ret; - ret = send_single(dev, RPMH_WAKE_ONLY_STATE, - p->addr, p->wake_val); + ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, + p->wake_val); if (ret) return ret; } @@ -493,7 +492,6 @@ int rpmh_flush(const struct device *dev) return 0; } -EXPORT_SYMBOL(rpmh_flush); /** * rpmh_invalidate: Invalidate all sleep and active sets diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h index 619e07c75da9..f9ec353d24a5 100644 --- a/include/soc/qcom/rpmh.h +++ b/include/soc/qcom/rpmh.h @@ -20,8 +20,6 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state, int rpmh_write_batch(const struct device *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 *n); -int rpmh_flush(const struct device *dev); - int rpmh_invalidate(const struct device *dev); #else @@ -40,9 +38,6 @@ static inline int rpmh_write_batch(const struct device *dev, const struct tcs_cmd *cmd, u32 *n) { return -ENODEV; } -static inline int rpmh_flush(const struct device *dev) -{ return -ENODEV; } - static inline int rpmh_invalidate(const struct device *dev) { return -ENODEV; } -- cgit v1.2.3 From 3f2e4c11e136e2cffd60dbc840b59ff65f017328 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 17 Dec 2019 18:48:55 +0100 Subject: kfifo: provide noirqsave variants of spinlocked in and out helpers Provide variants of spinlocked kfifo_in() and kfifo_out() routines which don't disable interrupts. Signed-off-by: Bartosz Golaszewski Acked-by: Stefani Seibold --- include/linux/kfifo.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'include') diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index fc4b0b10210f..123c200ed7cb 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -517,6 +517,26 @@ __kfifo_uint_must_check_helper( \ __ret; \ }) +/** + * kfifo_in_spinlocked_noirqsave - put data into fifo using a spinlock for + * locking, don't disable interrupts + * @fifo: address of the fifo to be used + * @buf: the data to be added + * @n: number of elements to be added + * @lock: pointer to the spinlock to use for locking + * + * This is a variant of kfifo_in_spinlocked() but uses spin_lock/unlock() + * for locking and doesn't disable interrupts. + */ +#define kfifo_in_spinlocked_noirqsave(fifo, buf, n, lock) \ +({ \ + unsigned int __ret; \ + spin_lock(lock); \ + __ret = kfifo_in(fifo, buf, n); \ + spin_unlock(lock); \ + __ret; \ +}) + /* alias for kfifo_in_spinlocked, will be removed in a future release */ #define kfifo_in_locked(fifo, buf, n, lock) \ kfifo_in_spinlocked(fifo, buf, n, lock) @@ -569,6 +589,28 @@ __kfifo_uint_must_check_helper( \ }) \ ) +/** + * kfifo_out_spinlocked_noirqsave - get data from the fifo using a spinlock + * for locking, don't disable interrupts + * @fifo: address of the fifo to be used + * @buf: pointer to the storage buffer + * @n: max. number of elements to get + * @lock: pointer to the spinlock to use for locking + * + * This is a variant of kfifo_out_spinlocked() which uses spin_lock/unlock() + * for locking and doesn't disable interrupts. + */ +#define kfifo_out_spinlocked_noirqsave(fifo, buf, n, lock) \ +__kfifo_uint_must_check_helper( \ +({ \ + unsigned int __ret; \ + spin_lock(lock); \ + __ret = kfifo_out(fifo, buf, n); \ + spin_unlock(lock); \ + __ret; \ +}) \ +) + /* alias for kfifo_out_spinlocked, will be removed in a future release */ #define kfifo_out_locked(fifo, buf, n, lock) \ kfifo_out_spinlocked(fifo, buf, n, lock) -- cgit v1.2.3 From 5195a89e8583bba43ec13871a7226763e401b44e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 17 Dec 2019 11:30:59 +0100 Subject: kfifo: provide kfifo_is_empty_spinlocked() Provide two spinlocked versions of kfifo_is_empty() to be used with spinlocked variants of kfifo_in() and kfifo_out(). Signed-off-by: Bartosz Golaszewski Acked-by: Stefani Seibold --- include/linux/kfifo.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 123c200ed7cb..86249476b57f 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -246,6 +246,37 @@ __kfifo_int_must_check_helper(int val) __tmpq->kfifo.in == __tmpq->kfifo.out; \ }) +/** + * kfifo_is_empty_spinlocked - returns true if the fifo is empty using + * a spinlock for locking + * @fifo: address of the fifo to be used + * @lock: spinlock to be used for locking + */ +#define kfifo_is_empty_spinlocked(fifo, lock) \ +({ \ + unsigned long __flags; \ + bool __ret; \ + spin_lock_irqsave(lock, __flags); \ + __ret = kfifo_is_empty(fifo); \ + spin_unlock_irqrestore(lock, __flags); \ + __ret; \ +}) + +/** + * kfifo_is_empty_spinlocked_noirqsave - returns true if the fifo is empty + * using a spinlock for locking, doesn't disable interrupts + * @fifo: address of the fifo to be used + * @lock: spinlock to be used for locking + */ +#define kfifo_is_empty_spinlocked_noirqsave(fifo, lock) \ +({ \ + bool __ret; \ + spin_lock(lock); \ + __ret = kfifo_is_empty(fifo); \ + spin_unlock(lock); \ + __ret; \ +}) + /** * kfifo_is_full - returns true if the fifo is full * @fifo: address of the fifo to be used -- cgit v1.2.3 From 51c1064e82e77b39a49889287ca50709303e2f26 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 22 Nov 2019 15:19:21 +0100 Subject: gpiolib: add new ioctl() for monitoring changes in line info Currently there is no way for user-space to be informed about changes in status of GPIO lines e.g. when someone else requests the line or its config changes. We can only periodically re-read the line-info. This is fine for simple one-off user-space tools, but any daemon that provides a centralized access to GPIO chips would benefit hugely from an event driven line info synchronization. This patch adds a new ioctl() that allows user-space processes to reuse the file descriptor associated with the character device for watching any changes in line properties. Every such event contains the updated line information. Currently the events are generated on three types of status changes: when a line is requested, when it's released and when its config is changed. The first two are self-explanatory. For the third one: this will only happen when another user-space process calls the new SET_CONFIG ioctl() as any changes that can happen from within the kernel (i.e. set_transitory() or set_debounce()) are of no interest to user-space. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij --- drivers/gpio/gpiolib.c | 186 ++++++++++++++++++++++++++++++++++++++++++++-- drivers/gpio/gpiolib.h | 1 + include/uapi/linux/gpio.h | 30 ++++++++ 3 files changed, 209 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 443321f9cf63..f73077f26eff 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -546,6 +546,9 @@ static long linehandle_set_config(struct linehandle_state *lh, if (ret) return ret; } + + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_CONFIG, desc); } return 0; } @@ -1201,14 +1204,25 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, spin_unlock_irqrestore(&gpio_lock, flags); } +struct gpio_chardev_data { + struct gpio_device *gdev; + wait_queue_head_t wait; + DECLARE_KFIFO(events, struct gpioline_info_changed, 32); + struct notifier_block lineinfo_changed_nb; + unsigned long *watched_lines; +}; + /* * gpio_ioctl() - ioctl handler for the GPIO chardev */ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct gpio_device *gdev = filp->private_data; + struct gpio_chardev_data *priv = filp->private_data; + struct gpio_device *gdev = priv->gdev; struct gpio_chip *chip = gdev->chip; void __user *ip = (void __user *)arg; + struct gpio_desc *desc; + __u32 offset; /* We fail any subsequent ioctl():s when the chip is gone */ if (!chip) @@ -1230,9 +1244,9 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_to_user(ip, &chipinfo, sizeof(chipinfo))) return -EFAULT; return 0; - } else if (cmd == GPIO_GET_LINEINFO_IOCTL) { + } else if (cmd == GPIO_GET_LINEINFO_IOCTL || + cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) { struct gpioline_info lineinfo; - struct gpio_desc *desc; if (copy_from_user(&lineinfo, ip, sizeof(lineinfo))) return -EFAULT; @@ -1245,11 +1259,25 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; + + if (cmd == GPIO_GET_LINEINFO_WATCH_IOCTL) + set_bit(desc_to_gpio(desc), priv->watched_lines); + return 0; } else if (cmd == GPIO_GET_LINEHANDLE_IOCTL) { return linehandle_create(gdev, ip); } else if (cmd == GPIO_GET_LINEEVENT_IOCTL) { return lineevent_create(gdev, ip); + } else if (cmd == GPIO_GET_LINEINFO_UNWATCH_IOCTL) { + if (copy_from_user(&offset, ip, sizeof(offset))) + return -EFAULT; + + desc = gpiochip_get_desc(chip, offset); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + clear_bit(desc_to_gpio(desc), &desc->flags); + return 0; } return -EINVAL; } @@ -1262,6 +1290,101 @@ static long gpio_ioctl_compat(struct file *filp, unsigned int cmd, } #endif +static struct gpio_chardev_data * +to_gpio_chardev_data(struct notifier_block *nb) +{ + return container_of(nb, struct gpio_chardev_data, lineinfo_changed_nb); +} + +static int lineinfo_changed_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct gpio_chardev_data *priv = to_gpio_chardev_data(nb); + struct gpioline_info_changed chg; + struct gpio_desc *desc = data; + int ret; + + if (!test_bit(desc_to_gpio(desc), priv->watched_lines)) + return NOTIFY_DONE; + + memset(&chg, 0, sizeof(chg)); + chg.info.line_offset = gpio_chip_hwgpio(desc); + chg.event_type = action; + chg.timestamp = ktime_get_ns(); + gpio_desc_to_lineinfo(desc, &chg.info); + + ret = kfifo_in_spinlocked(&priv->events, &chg, 1, &priv->wait.lock); + if (ret) + wake_up_poll(&priv->wait, EPOLLIN); + else + pr_debug_ratelimited("lineinfo event FIFO is full - event dropped\n"); + + return NOTIFY_OK; +} + +static __poll_t lineinfo_watch_poll(struct file *filep, + struct poll_table_struct *pollt) +{ + struct gpio_chardev_data *priv = filep->private_data; + __poll_t events = 0; + + poll_wait(filep, &priv->wait, pollt); + + if (!kfifo_is_empty_spinlocked_noirqsave(&priv->events, + &priv->wait.lock)) + events = EPOLLIN | EPOLLRDNORM; + + return events; +} + +static ssize_t lineinfo_watch_read(struct file *filep, char __user *buf, + size_t count, loff_t *off) +{ + struct gpio_chardev_data *priv = filep->private_data; + struct gpioline_info_changed event; + ssize_t bytes_read = 0; + int ret; + + if (count < sizeof(event)) + return -EINVAL; + + do { + spin_lock(&priv->wait.lock); + if (kfifo_is_empty(&priv->events)) { + if (bytes_read) { + spin_unlock(&priv->wait.lock); + return bytes_read; + } + + if (filep->f_flags & O_NONBLOCK) { + spin_unlock(&priv->wait.lock); + return -EAGAIN; + } + + ret = wait_event_interruptible_locked(priv->wait, + !kfifo_is_empty(&priv->events)); + if (ret) { + spin_unlock(&priv->wait.lock); + return ret; + } + } + + ret = kfifo_out(&priv->events, &event, 1); + spin_unlock(&priv->wait.lock); + if (ret != 1) { + ret = -EIO; + break; + /* We should never get here. See lineevent_read(). */ + } + + if (copy_to_user(buf + bytes_read, &event, sizeof(event))) + return -EFAULT; + bytes_read += sizeof(event); + } while (count >= bytes_read + sizeof(event)); + + return bytes_read; +} + /** * gpio_chrdev_open() - open the chardev for ioctl operations * @inode: inode for this chardev @@ -1272,14 +1395,48 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp) { struct gpio_device *gdev = container_of(inode->i_cdev, struct gpio_device, chrdev); + struct gpio_chardev_data *priv; + int ret = -ENOMEM; /* Fail on open if the backing gpiochip is gone */ if (!gdev->chip) return -ENODEV; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + if (!priv->watched_lines) + goto out_free_priv; + + init_waitqueue_head(&priv->wait); + INIT_KFIFO(priv->events); + priv->gdev = gdev; + + priv->lineinfo_changed_nb.notifier_call = lineinfo_changed_notify; + ret = atomic_notifier_chain_register(&gdev->notifier, + &priv->lineinfo_changed_nb); + if (ret) + goto out_free_bitmap; + get_device(&gdev->dev); - filp->private_data = gdev; + filp->private_data = priv; - return nonseekable_open(inode, filp); + ret = nonseekable_open(inode, filp); + if (ret) + goto out_unregister_notifier; + + return ret; + +out_unregister_notifier: + atomic_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); +out_free_bitmap: + bitmap_free(priv->watched_lines); +out_free_priv: + kfree(priv); + return ret; } /** @@ -1290,17 +1447,23 @@ static int gpio_chrdev_open(struct inode *inode, struct file *filp) */ static int gpio_chrdev_release(struct inode *inode, struct file *filp) { - struct gpio_device *gdev = container_of(inode->i_cdev, - struct gpio_device, chrdev); + struct gpio_chardev_data *priv = filp->private_data; + struct gpio_device *gdev = priv->gdev; + bitmap_free(priv->watched_lines); + atomic_notifier_chain_unregister(&gdev->notifier, + &priv->lineinfo_changed_nb); put_device(&gdev->dev); + kfree(priv); + return 0; } - static const struct file_operations gpio_fileops = { .release = gpio_chrdev_release, .open = gpio_chrdev_open, + .poll = lineinfo_watch_poll, + .read = lineinfo_watch_read, .owner = THIS_MODULE, .llseek = no_llseek, .unlocked_ioctl = gpio_ioctl, @@ -1511,6 +1674,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, spin_unlock_irqrestore(&gpio_lock, flags); + ATOMIC_INIT_NOTIFIER_HEAD(&gdev->notifier); + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -2843,6 +3008,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) } done: spin_unlock_irqrestore(&gpio_lock, flags); + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_REQUESTED, desc); return ret; } @@ -2940,6 +3107,9 @@ static bool gpiod_free_commit(struct gpio_desc *desc) } spin_unlock_irqrestore(&gpio_lock, flags); + atomic_notifier_call_chain(&desc->gdev->notifier, + GPIOLINE_CHANGED_RELEASED, desc); + return ret; } diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 3e0aab2945d8..5ab90746b519 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -56,6 +56,7 @@ struct gpio_device { const char *label; void *data; struct list_head list; + struct atomic_notifier_head notifier; #ifdef CONFIG_PINCTRL /* diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 799cf823d493..dca320764e4d 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -59,6 +59,34 @@ struct gpioline_info { /* Maximum number of requested handles */ #define GPIOHANDLES_MAX 64 +/* Possible line status change events */ +enum { + GPIOLINE_CHANGED_REQUESTED = 1, + GPIOLINE_CHANGED_RELEASED, + GPIOLINE_CHANGED_CONFIG, +}; + +/** + * struct gpioline_info_changed - Information about a change in status + * of a GPIO line + * @info: updated line information + * @timestamp: estimate of time of status change occurrence, in nanoseconds + * and GPIOLINE_CHANGED_CONFIG + * @event_type: one of GPIOLINE_CHANGED_REQUESTED, GPIOLINE_CHANGED_RELEASED + * + * Note: struct gpioline_info embedded here has 32-bit alignment on its own, + * but it works fine with 64-bit alignment too. With its 72 byte size, we can + * guarantee there are no implicit holes between it and subsequent members. + * The 20-byte padding at the end makes sure we don't add any implicit padding + * at the end of the structure on 64-bit architectures. + */ +struct gpioline_info_changed { + struct gpioline_info info; + __u64 timestamp; + __u32 event_type; + __u32 padding[5]; /* for future use */ +}; + /* Linerequest flags */ #define GPIOHANDLE_REQUEST_INPUT (1UL << 0) #define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1) @@ -176,6 +204,8 @@ struct gpioevent_data { #define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info) #define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info) +#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0b, struct gpioline_info) +#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0c, __u32) #define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request) #define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request) -- cgit v1.2.3 From 684cb4b79e594f786b942a3ac5097ecf0e9e3e08 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:29 +0300 Subject: usb: typec: Fix the description of struct typec_capability Removing descriptions of the mux and sw members. They are no longer part of the structure. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-5-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index c358b3fd05c9..44d28387ced4 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -198,8 +198,6 @@ struct typec_operations { * @pd_revision: USB Power Delivery Specification revision if supported * @prefer_role: Initial role preference (DRP ports). * @accessory: Supported Accessory Modes - * @sw: Cable plug orientation switch - * @mux: Multiplexer switch for Alternate/Accessory Modes * @fwnode: Optional fwnode of the port * @driver_data: Private pointer for driver specific info * @ops: Port operations vector -- cgit v1.2.3 From bbe80c9a89b868e98ef0710cb03ee68dd78a4d8d Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 11 Feb 2020 14:25:30 +0300 Subject: usb: typec: altmode: Remove the notification chain Using the generic notification chain is not reasonable with the alternate modes because it would require dependencies between the drivers of the components that need the notifications, and the typec drivers. There are no users for the alternate mode notifications, so removing the chain and the API for it completely. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200211112531.86510-6-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/usb/typec_bus.rst | 22 +--------- drivers/usb/typec/bus.c | 12 +----- drivers/usb/typec/bus.h | 2 - drivers/usb/typec/class.c | 67 +----------------------------- include/linux/usb/typec_altmode.h | 7 ---- 5 files changed, 3 insertions(+), 107 deletions(-) (limited to 'include') diff --git a/Documentation/driver-api/usb/typec_bus.rst b/Documentation/driver-api/usb/typec_bus.rst index f47a69bff498..03dfa9c018b7 100644 --- a/Documentation/driver-api/usb/typec_bus.rst +++ b/Documentation/driver-api/usb/typec_bus.rst @@ -53,9 +53,7 @@ in need to reconfigure the pins on the connector, the alternate mode driver needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver passes the negotiated SVID specific pin configuration value to the function as parameter. The bus driver will then configure the mux behind the connector using -that value as the state value for the mux, and also call blocking notification -chain to notify the external drivers about the state of the connector that need -to know it. +that value as the state value for the mux. NOTE: The SVID specific pin configuration values must always start from ``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for @@ -80,19 +78,6 @@ Helper macro ``TYPEC_MODAL_STATE()`` can also be used:: #define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0); #define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1); -Notification chain -~~~~~~~~~~~~~~~~~~ - -The drivers for the components that the alternate modes are designed for need to -get details regarding the results of the negotiation with the partner, and the -pin configuration of the connector. In case of DisplayPort alternate mode for -example, the GPU drivers will need to know those details. In case of -Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and -so on. - -The notification chain is designed for this purpose. The drivers can register -notifiers with :c:func:`typec_altmode_register_notifier()`. - Cable plug alternate modes ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -129,8 +114,3 @@ Cable Plug operations .. kernel-doc:: drivers/usb/typec/bus.c :functions: typec_altmode_get_plug typec_altmode_put_plug - -Notifications -~~~~~~~~~~~~~ -.. kernel-doc:: drivers/usb/typec/class.c - :functions: typec_altmode_register_notifier typec_altmode_unregister_notifier diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index 2e45eb479386..c823122f9cb7 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -30,17 +30,10 @@ static int typec_altmode_set_state(struct typec_altmode *adev, { bool is_port = is_typec_port(adev->dev.parent); struct altmode *port_altmode; - int ret; port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner; - ret = typec_altmode_set_mux(port_altmode, conf, data); - if (ret) - return ret; - - blocking_notifier_call_chain(&port_altmode->nh, conf, NULL); - - return 0; + return typec_altmode_set_mux(port_altmode, conf, data); } /* -------------------------------------------------------------------------- */ @@ -82,9 +75,6 @@ int typec_altmode_notify(struct typec_altmode *adev, if (ret) return ret; - blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh, - conf, data); - if (partner->adev.ops && partner->adev.ops->notify) return partner->adev.ops->notify(&partner->adev, conf, data); diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index 0c9661c96473..8ba8112d2740 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -22,8 +22,6 @@ struct altmode { struct altmode *partner; struct altmode *plug[2]; - - struct blocking_notifier_head nh; }; #define to_altmode(d) container_of(d, struct altmode, adev) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 9cf4f6deb5a6..12be5bb6d32c 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -206,69 +206,6 @@ static void typec_altmode_put_partner(struct altmode *altmode) put_device(&adev->dev); } -static void *typec_port_match(struct device_connection *con, int ep, void *data) -{ - struct device *dev; - - /* - * FIXME: Check does the fwnode supports the requested SVID. If it does - * we need to return ERR_PTR(-PROBE_DEFER) when there is no device. - */ - if (con->fwnode) - return class_find_device_by_fwnode(typec_class, con->fwnode); - - dev = class_find_device_by_name(typec_class, con->endpoint[ep]); - - return dev ? dev : ERR_PTR(-EPROBE_DEFER); -} - -struct typec_altmode * -typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, - struct notifier_block *nb) -{ - struct typec_device_id id = { svid, mode, }; - struct device *altmode_dev; - struct device *port_dev; - struct altmode *altmode; - int ret; - - /* Find the port linked to the caller */ - port_dev = device_connection_find_match(dev, NULL, NULL, - typec_port_match); - if (IS_ERR_OR_NULL(port_dev)) - return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV); - - /* Find the altmode with matching svid */ - altmode_dev = device_find_child(port_dev, &id, altmode_match); - - put_device(port_dev); - - if (!altmode_dev) - return ERR_PTR(-ENODEV); - - altmode = to_altmode(to_typec_altmode(altmode_dev)); - - /* Register notifier */ - ret = blocking_notifier_chain_register(&altmode->nh, nb); - if (ret) { - put_device(altmode_dev); - return ERR_PTR(ret); - } - - return &altmode->adev; -} -EXPORT_SYMBOL_GPL(typec_altmode_register_notifier); - -void typec_altmode_unregister_notifier(struct typec_altmode *adev, - struct notifier_block *nb) -{ - struct altmode *altmode = to_altmode(adev); - - blocking_notifier_chain_unregister(&altmode->nh, nb); - put_device(&adev->dev); -} -EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier); - /** * typec_altmode_update_active - Report Enter/Exit mode * @adev: Handle to the alternate mode @@ -538,9 +475,7 @@ typec_register_altmode(struct device *parent, dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id); /* Link partners and plugs with the ports */ - if (is_port) - BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh); - else + if (!is_port) typec_altmode_set_partner(alt); /* The partners are bind to drivers */ diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 923ff3af0628..d834e236c6df 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -126,13 +126,6 @@ void typec_altmode_put_plug(struct typec_altmode *plug); struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, size_t n, u16 svid, u8 mode); -struct typec_altmode * -typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode, - struct notifier_block *nb); - -void typec_altmode_unregister_notifier(struct typec_altmode *adev, - struct notifier_block *nb); - /** * typec_altmode_get_orientation - Get cable plug orientation * altmode: Handle to the alternate mode -- cgit v1.2.3 From c9d503370f240934f3c1c5da4c6c2452a7d05db2 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:29 +0200 Subject: USB: Make it possible to "subclass" usb_device_driver The kernel currenly has only 2 usb_device_drivers, one generic one, one that completely replaces the generic one to make USB devices usable over a network. Use the newly exported generic driver functions when a driver declares to want them run, in addition to its own code. This makes it possible to write drivers that extend the generic USB driver. Note that this patch is not enough for another driver to automatically get selected. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-3-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 26 +++++++++++++++++++++----- include/linux/usb.h | 4 ++++ 2 files changed, 25 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2b27d232d7a7..d3787d084937 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -261,9 +261,16 @@ static int usb_probe_device(struct device *dev) */ if (!udriver->supports_autosuspend) error = usb_autoresume_device(udev); + if (error) + return error; - if (!error) - error = udriver->probe(udev); + if (udriver->generic_subclass) + error = usb_generic_driver_probe(udev); + if (error) + return error; + + error = udriver->probe(udev); + /* TODO: fallback to generic driver in case of error */ return error; } @@ -273,7 +280,10 @@ static int usb_unbind_device(struct device *dev) struct usb_device *udev = to_usb_device(dev); struct usb_device_driver *udriver = to_usb_device_driver(dev->driver); - udriver->disconnect(udev); + if (udriver->disconnect) + udriver->disconnect(udev); + if (udriver->generic_subclass) + usb_generic_driver_disconnect(udev); if (!udriver->supports_autosuspend) usb_autosuspend_device(udev); return 0; @@ -1149,7 +1159,10 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) udev->do_remote_wakeup = 0; udriver = &usb_generic_driver; } - status = udriver->suspend(udev, msg); + if (udriver->suspend) + status = udriver->suspend(udev, msg); + if (status == 0 && udriver->generic_subclass) + status = usb_generic_driver_suspend(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); @@ -1181,7 +1194,10 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg) udev->reset_resume = 1; udriver = to_usb_device_driver(udev->dev.driver); - status = udriver->resume(udev, msg); + if (udriver->generic_subclass) + status = usb_generic_driver_resume(udev, msg); + if (status == 0 && udriver->resume) + status = udriver->resume(udev, msg); done: dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status); diff --git a/include/linux/usb.h b/include/linux/usb.h index e656e7b4b1e4..94bd3b48a485 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1228,6 +1228,9 @@ struct usb_driver { * @drvwrap: Driver-model core structure wrapper. * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend * for devices bound to this driver. + * @generic_subclass: if set to 1, the generic USB driver's probe, disconnect, + * resume and suspend functions will be called in addition to the driver's + * own, so this part of the setup does not need to be replicated. * * USB drivers must provide all the fields listed above except drvwrap. */ @@ -1242,6 +1245,7 @@ struct usb_device_driver { const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; unsigned int supports_autosuspend:1; + unsigned int generic_subclass:1; }; #define to_usb_device_driver(d) container_of(d, struct usb_device_driver, \ drvwrap.driver) -- cgit v1.2.3 From 88b7381a939de0fa1f1b1629c56b03dca7077309 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:31 +0200 Subject: USB: Select better matching USB drivers when available Now that USB device drivers can reuse code from the generic USB device driver, we need to make sure that they get selected rather than the generic driver. Add an id_table and match vfunc to the usb_device_driver struct, which will get used to select a better matching driver at ->probe time. This is a similar mechanism to that used in the HID drivers, with the generic driver being selected unless there's a better matching one found in the registered drivers (see hid_generic_match() in drivers/hid/hid-generic.c). Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-5-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 15 +++++++++++++-- drivers/usb/core/generic.c | 29 +++++++++++++++++++++++++++++ include/linux/usb.h | 2 ++ 3 files changed, 44 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 697898327b44..9d1502a9571d 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -818,13 +818,24 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) { /* devices and interfaces are handled separately */ if (is_usb_device(dev)) { + struct usb_device *udev; + struct usb_device_driver *udrv; /* interface drivers never match devices */ if (!is_usb_device_driver(drv)) return 0; - /* TODO: Add real matching code */ - return 1; + udev = to_usb_device(dev); + udrv = to_usb_device_driver(drv); + + if (udrv->id_table && + usb_device_match_id(udev, udrv->id_table) != NULL) { + return 1; + } + + if (udrv->match) + return udrv->match(udev); + return 0; } else if (is_usb_interface(dev)) { struct usb_interface *intf; diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 28ece4d77749..84da85c13825 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -195,6 +195,34 @@ int usb_choose_configuration(struct usb_device *udev) } EXPORT_SYMBOL_GPL(usb_choose_configuration); +static int __check_usb_generic(struct device_driver *drv, void *data) +{ + struct usb_device *udev = data; + struct usb_device_driver *udrv; + + if (!is_usb_device_driver(drv)) + return 0; + udrv = to_usb_device_driver(drv); + if (udrv == &usb_generic_driver) + return 0; + if (!udrv->id_table) + return 0; + + return usb_device_match_id(udev, udrv->id_table) != NULL; +} + +static bool usb_generic_driver_match(struct usb_device *udev) +{ + /* + * If any other driver wants the device, leave the device to this other + * driver. + */ + if (bus_for_each_drv(&usb_bus_type, NULL, udev, __check_usb_generic)) + return false; + + return true; +} + int usb_generic_driver_probe(struct usb_device *udev) { int err, c; @@ -285,6 +313,7 @@ int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg) struct usb_device_driver usb_generic_driver = { .name = "usb", + .match = usb_generic_driver_match, .probe = usb_generic_driver_probe, .disconnect = usb_generic_driver_disconnect, #ifdef CONFIG_PM diff --git a/include/linux/usb.h b/include/linux/usb.h index 94bd3b48a485..3663bd7b1fa4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1237,6 +1237,7 @@ struct usb_driver { struct usb_device_driver { const char *name; + bool (*match) (struct usb_device *udev); int (*probe) (struct usb_device *udev); void (*disconnect) (struct usb_device *udev); @@ -1244,6 +1245,7 @@ struct usb_device_driver { int (*resume) (struct usb_device *udev, pm_message_t message); const struct attribute_group **dev_groups; struct usbdrv_wrap drvwrap; + const struct usb_device_id *id_table; unsigned int supports_autosuspend:1; unsigned int generic_subclass:1; }; -- cgit v1.2.3 From 77419aa403ca1395f66e1e3de87743f54ba144b6 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Wed, 16 Oct 2019 11:39:32 +0200 Subject: USB: Fallback to generic driver when specific driver fails If ->probe fails for a device specific driver, ask the driver core to reprobe us, after having flagged the device for the generic driver to be forced. Signed-off-by: Bastien Nocera Acked-by: Alan Stern Link: https://lore.kernel.org/r/20191016093933.693-6-hadess@hadess.net Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 5 ++++- drivers/usb/core/generic.c | 3 +++ include/linux/usb.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 9d1502a9571d..f81606c6a35b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -270,7 +270,10 @@ static int usb_probe_device(struct device *dev) return error; error = udriver->probe(udev); - /* TODO: fallback to generic driver in case of error */ + if (error == -ENODEV && udriver != &usb_generic_driver) { + udev->use_generic_driver = 1; + return -EPROBE_DEFER; + } return error; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 84da85c13825..4626227a6dd2 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -213,6 +213,9 @@ static int __check_usb_generic(struct device_driver *drv, void *data) static bool usb_generic_driver_match(struct usb_device *udev) { + if (udev->use_generic_driver) + return true; + /* * If any other driver wants the device, leave the device to this other * driver. diff --git a/include/linux/usb.h b/include/linux/usb.h index 3663bd7b1fa4..ca1a5f1e1c5e 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -708,6 +708,7 @@ struct usb_device { unsigned lpm_disable_count; u16 hub_delay; + unsigned use_generic_driver:1; }; #define to_usb_device(d) container_of(d, struct usb_device, dev) -- cgit v1.2.3 From 4a4472fdc098fb78f52a0848788faf46674a8423 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 12 Feb 2020 10:43:17 +0100 Subject: of: clk: Make of_clk_get_parent_{count,name}() parameter const of_clk_get_parent_count() and of_clk_get_parent_name() never modify the device nodes passed, so they can be const. Signed-off-by: Geert Uytterhoeven Link: https://lkml.kernel.org/r/20200212094317.1150-1-geert+renesas@glider.be Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 4 ++-- include/linux/of_clk.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f0f2b599fd7e..95adf6c6db3d 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -4713,7 +4713,7 @@ EXPORT_SYMBOL(of_clk_get_by_name); * * Returns: The number of clocks that are possible parents of this node */ -unsigned int of_clk_get_parent_count(struct device_node *np) +unsigned int of_clk_get_parent_count(const struct device_node *np) { int count; @@ -4725,7 +4725,7 @@ unsigned int of_clk_get_parent_count(struct device_node *np) } EXPORT_SYMBOL_GPL(of_clk_get_parent_count); -const char *of_clk_get_parent_name(struct device_node *np, int index) +const char *of_clk_get_parent_name(const struct device_node *np, int index) { struct of_phandle_args clkspec; struct property *prop; diff --git a/include/linux/of_clk.h b/include/linux/of_clk.h index c86fcad23fc2..31b73a0da9db 100644 --- a/include/linux/of_clk.h +++ b/include/linux/of_clk.h @@ -11,17 +11,17 @@ struct of_device_id; #if defined(CONFIG_COMMON_CLK) && defined(CONFIG_OF) -unsigned int of_clk_get_parent_count(struct device_node *np); -const char *of_clk_get_parent_name(struct device_node *np, int index); +unsigned int of_clk_get_parent_count(const struct device_node *np); +const char *of_clk_get_parent_name(const struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches); #else /* !CONFIG_COMMON_CLK || !CONFIG_OF */ -static inline unsigned int of_clk_get_parent_count(struct device_node *np) +static inline unsigned int of_clk_get_parent_count(const struct device_node *np) { return 0; } -static inline const char *of_clk_get_parent_name(struct device_node *np, +static inline const char *of_clk_get_parent_name(const struct device_node *np, int index) { return NULL; -- cgit v1.2.3 From dbb92f88648d6206bf22fcb764fb9fe2939d401a Mon Sep 17 00:00:00 2001 From: Andrea Parri Date: Wed, 22 Jan 2020 19:39:52 +0100 Subject: workqueue: Document (some) memory-ordering properties of {queue,schedule}_work() It's desirable to be able to rely on the following property: All stores preceding (in program order) a call to a successful queue_work() will be visible from the CPU which will execute the queued work by the time such work executes, e.g., { x is initially 0 } CPU0 CPU1 WRITE_ONCE(x, 1); [ "work" is being executed ] r0 = queue_work(wq, work); r1 = READ_ONCE(x); Forbids: r0 == true && r1 == 0 The current implementation of queue_work() provides such memory-ordering property: - In __queue_work(), the ->lock spinlock is acquired. - On the other side, in worker_thread(), this same ->lock is held when dequeueing work. So the locking ordering makes things work out. Add this property to the DocBook headers of {queue,schedule}_work(). Suggested-by: Paul E. McKenney Signed-off-by: Andrea Parri Acked-by: Paul E. McKenney Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 4261d1c6e87b..e48554e6526c 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -487,6 +487,19 @@ extern void wq_worker_comm(char *buf, size_t size, struct task_struct *task); * * We queue the work to the CPU on which it was submitted, but if the CPU dies * it can be processed by another CPU. + * + * Memory-ordering properties: If it returns %true, guarantees that all stores + * preceding the call to queue_work() in the program order will be visible from + * the CPU which will execute @work by the time such work executes, e.g., + * + * { x is initially 0 } + * + * CPU0 CPU1 + * + * WRITE_ONCE(x, 1); [ @work is being executed ] + * r0 = queue_work(wq, work); r1 = READ_ONCE(x); + * + * Forbids: r0 == true && r1 == 0 */ static inline bool queue_work(struct workqueue_struct *wq, struct work_struct *work) @@ -546,6 +559,9 @@ static inline bool schedule_work_on(int cpu, struct work_struct *work) * This puts a job in the kernel-global workqueue if it was not already * queued and leaves it in the same position on the kernel-global * workqueue otherwise. + * + * Shares the same memory-ordering properties of queue_work(), cf. the + * DocBook header of queue_work(). */ static inline bool schedule_work(struct work_struct *work) { -- cgit v1.2.3 From 9c974c77246460fa6a92c18554c3311c8c83c160 Mon Sep 17 00:00:00 2001 From: Michal Koutný Date: Fri, 24 Jan 2020 12:40:15 +0100 Subject: cgroup: Iterate tasks that did not finish do_exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PF_EXITING is set earlier than actual removal from css_set when a task is exitting. This can confuse cgroup.procs readers who see no PF_EXITING tasks, however, rmdir is checking against css_set membership so it can transitionally fail with EBUSY. Fix this by listing tasks that weren't unlinked from css_set active lists. It may happen that other users of the task iterator (without CSS_TASK_ITER_PROCS) spot a PF_EXITING task before cgroup_exit(). This is equal to the state before commit c03cd7738a83 ("cgroup: Include dying leaders with live threads in PROCS iterations") but it may be reviewed later. Reported-by: Suren Baghdasaryan Fixes: c03cd7738a83 ("cgroup: Include dying leaders with live threads in PROCS iterations") Signed-off-by: Michal Koutný Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 1 + kernel/cgroup/cgroup.c | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d7ddebd0cdec..e75d2191226b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -62,6 +62,7 @@ struct css_task_iter { struct list_head *mg_tasks_head; struct list_head *dying_tasks_head; + struct list_head *cur_tasks_head; struct css_set *cur_cset; struct css_set *cur_dcset; struct task_struct *cur_task; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 927f7b82e5c1..c719a4154d6d 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -4400,12 +4400,16 @@ static void css_task_iter_advance_css_set(struct css_task_iter *it) } } while (!css_set_populated(cset) && list_empty(&cset->dying_tasks)); - if (!list_empty(&cset->tasks)) + if (!list_empty(&cset->tasks)) { it->task_pos = cset->tasks.next; - else if (!list_empty(&cset->mg_tasks)) + it->cur_tasks_head = &cset->tasks; + } else if (!list_empty(&cset->mg_tasks)) { it->task_pos = cset->mg_tasks.next; - else + it->cur_tasks_head = &cset->mg_tasks; + } else { it->task_pos = cset->dying_tasks.next; + it->cur_tasks_head = &cset->dying_tasks; + } it->tasks_head = &cset->tasks; it->mg_tasks_head = &cset->mg_tasks; @@ -4463,10 +4467,14 @@ repeat: else it->task_pos = it->task_pos->next; - if (it->task_pos == it->tasks_head) + if (it->task_pos == it->tasks_head) { it->task_pos = it->mg_tasks_head->next; - if (it->task_pos == it->mg_tasks_head) + it->cur_tasks_head = it->mg_tasks_head; + } + if (it->task_pos == it->mg_tasks_head) { it->task_pos = it->dying_tasks_head->next; + it->cur_tasks_head = it->dying_tasks_head; + } if (it->task_pos == it->dying_tasks_head) css_task_iter_advance_css_set(it); } else { @@ -4485,11 +4493,12 @@ repeat: goto repeat; /* and dying leaders w/o live member threads */ - if (!atomic_read(&task->signal->live)) + if (it->cur_tasks_head == it->dying_tasks_head && + !atomic_read(&task->signal->live)) goto repeat; } else { /* skip all dying ones */ - if (task->flags & PF_EXITING) + if (it->cur_tasks_head == it->dying_tasks_head) goto repeat; } } -- cgit v1.2.3 From f43caa2adc96fc9c95fd77eef63cdff86ebf33cb Mon Sep 17 00:00:00 2001 From: Michal Koutný Date: Fri, 24 Jan 2020 12:40:16 +0100 Subject: cgroup: Clean up css_set task traversal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit css_task_iter stores pointer to head of each iterable list, this dates back to commit 0f0a2b4fa621 ("cgroup: reorganize css_task_iter") when we did not store cur_cset. Let us utilize list heads directly in cur_cset and streamline css_task_iter_advance_css_set a bit. This is no intentional function change. Signed-off-by: Michal Koutný Signed-off-by: Tejun Heo --- include/linux/cgroup.h | 3 --- kernel/cgroup/cgroup.c | 61 +++++++++++++++++++++++--------------------------- 2 files changed, 28 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index e75d2191226b..f1219b927817 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -58,9 +58,6 @@ struct css_task_iter { struct list_head *tcset_head; struct list_head *task_pos; - struct list_head *tasks_head; - struct list_head *mg_tasks_head; - struct list_head *dying_tasks_head; struct list_head *cur_tasks_head; struct css_set *cur_cset; diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index c719a4154d6d..b4c4c4fbd6de 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -4391,29 +4391,24 @@ static void css_task_iter_advance_css_set(struct css_task_iter *it) lockdep_assert_held(&css_set_lock); - /* Advance to the next non-empty css_set */ - do { - cset = css_task_iter_next_css_set(it); - if (!cset) { - it->task_pos = NULL; - return; + /* Advance to the next non-empty css_set and find first non-empty tasks list*/ + while ((cset = css_task_iter_next_css_set(it))) { + if (!list_empty(&cset->tasks)) { + it->cur_tasks_head = &cset->tasks; + break; + } else if (!list_empty(&cset->mg_tasks)) { + it->cur_tasks_head = &cset->mg_tasks; + break; + } else if (!list_empty(&cset->dying_tasks)) { + it->cur_tasks_head = &cset->dying_tasks; + break; } - } while (!css_set_populated(cset) && list_empty(&cset->dying_tasks)); - - if (!list_empty(&cset->tasks)) { - it->task_pos = cset->tasks.next; - it->cur_tasks_head = &cset->tasks; - } else if (!list_empty(&cset->mg_tasks)) { - it->task_pos = cset->mg_tasks.next; - it->cur_tasks_head = &cset->mg_tasks; - } else { - it->task_pos = cset->dying_tasks.next; - it->cur_tasks_head = &cset->dying_tasks; } - - it->tasks_head = &cset->tasks; - it->mg_tasks_head = &cset->mg_tasks; - it->dying_tasks_head = &cset->dying_tasks; + if (!cset) { + it->task_pos = NULL; + return; + } + it->task_pos = it->cur_tasks_head->next; /* * We don't keep css_sets locked across iteration steps and thus @@ -4458,24 +4453,24 @@ static void css_task_iter_advance(struct css_task_iter *it) repeat: if (it->task_pos) { /* - * Advance iterator to find next entry. cset->tasks is - * consumed first and then ->mg_tasks. After ->mg_tasks, - * we move onto the next cset. + * Advance iterator to find next entry. We go through cset + * tasks, mg_tasks and dying_tasks, when consumed we move onto + * the next cset. */ if (it->flags & CSS_TASK_ITER_SKIPPED) it->flags &= ~CSS_TASK_ITER_SKIPPED; else it->task_pos = it->task_pos->next; - if (it->task_pos == it->tasks_head) { - it->task_pos = it->mg_tasks_head->next; - it->cur_tasks_head = it->mg_tasks_head; + if (it->task_pos == &it->cur_cset->tasks) { + it->cur_tasks_head = &it->cur_cset->mg_tasks; + it->task_pos = it->cur_tasks_head->next; } - if (it->task_pos == it->mg_tasks_head) { - it->task_pos = it->dying_tasks_head->next; - it->cur_tasks_head = it->dying_tasks_head; + if (it->task_pos == &it->cur_cset->mg_tasks) { + it->cur_tasks_head = &it->cur_cset->dying_tasks; + it->task_pos = it->cur_tasks_head->next; } - if (it->task_pos == it->dying_tasks_head) + if (it->task_pos == &it->cur_cset->dying_tasks) css_task_iter_advance_css_set(it); } else { /* called from start, proceed to the first cset */ @@ -4493,12 +4488,12 @@ repeat: goto repeat; /* and dying leaders w/o live member threads */ - if (it->cur_tasks_head == it->dying_tasks_head && + if (it->cur_tasks_head == &it->cur_cset->dying_tasks && !atomic_read(&task->signal->live)) goto repeat; } else { /* skip all dying ones */ - if (it->cur_tasks_head == it->dying_tasks_head) + if (it->cur_tasks_head == &it->cur_cset->dying_tasks) goto repeat; } } -- cgit v1.2.3 From a49e4629b5edf1db856de05fbf1aae05502ef1af Mon Sep 17 00:00:00 2001 From: Prateek Sood Date: Fri, 24 Jan 2020 20:37:29 +0530 Subject: cpuset: Make cpuset hotplug synchronous Convert cpuset_hotplug_workfn() into synchronous call for cpu hotplug path. For memory hotplug path it still gets queued as a work item. Since cpuset_hotplug_workfn() can be made synchronous for cpu hotplug path, it is not required to wait for cpuset hotplug while thawing processes. Signed-off-by: Prateek Sood Signed-off-by: Tejun Heo --- include/linux/cpuset.h | 3 --- kernel/cgroup/cpuset.c | 31 +++++++++++++++++++------------ kernel/power/process.c | 2 -- 3 files changed, 19 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index 04c20de66afc..cede4cb98b78 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -54,7 +54,6 @@ extern int cpuset_init(void); extern void cpuset_init_smp(void); extern void cpuset_force_rebuild(void); extern void cpuset_update_active_cpus(void); -extern void cpuset_wait_for_hotplug(void); extern void cpuset_read_lock(void); extern void cpuset_read_unlock(void); extern void cpuset_cpus_allowed(struct task_struct *p, struct cpumask *mask); @@ -176,8 +175,6 @@ static inline void cpuset_update_active_cpus(void) partition_sched_domains(1, NULL, NULL); } -static inline void cpuset_wait_for_hotplug(void) { } - static inline void cpuset_read_lock(void) { } static inline void cpuset_read_unlock(void) { } diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 58f5073acff7..cafd4d2ff882 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -3101,7 +3101,7 @@ update_tasks: } /** - * cpuset_hotplug_workfn - handle CPU/memory hotunplug for a cpuset + * cpuset_hotplug - handle CPU/memory hotunplug for a cpuset * * This function is called after either CPU or memory configuration has * changed and updates cpuset accordingly. The top_cpuset is always @@ -3116,7 +3116,7 @@ update_tasks: * Note that CPU offlining during suspend is ignored. We don't modify * cpusets across suspend/resume cycles at all. */ -static void cpuset_hotplug_workfn(struct work_struct *work) +static void cpuset_hotplug(bool use_cpu_hp_lock) { static cpumask_t new_cpus; static nodemask_t new_mems; @@ -3201,25 +3201,32 @@ static void cpuset_hotplug_workfn(struct work_struct *work) /* rebuild sched domains if cpus_allowed has changed */ if (cpus_updated || force_rebuild) { force_rebuild = false; - rebuild_sched_domains(); + if (use_cpu_hp_lock) + rebuild_sched_domains(); + else { + /* Acquiring cpu_hotplug_lock is not required. + * When cpuset_hotplug() is called in hotplug path, + * cpu_hotplug_lock is held by the hotplug context + * which is waiting for cpuhp_thread_fun to indicate + * completion of callback. + */ + percpu_down_write(&cpuset_rwsem); + rebuild_sched_domains_locked(); + percpu_up_write(&cpuset_rwsem); + } } free_cpumasks(NULL, ptmp); } -void cpuset_update_active_cpus(void) +static void cpuset_hotplug_workfn(struct work_struct *work) { - /* - * We're inside cpu hotplug critical region which usually nests - * inside cgroup synchronization. Bounce actual hotplug processing - * to a work item to avoid reverse locking order. - */ - schedule_work(&cpuset_hotplug_work); + cpuset_hotplug(true); } -void cpuset_wait_for_hotplug(void) +void cpuset_update_active_cpus(void) { - flush_work(&cpuset_hotplug_work); + cpuset_hotplug(false); } /* diff --git a/kernel/power/process.c b/kernel/power/process.c index 4b6a54da7e65..08f7019357ee 100644 --- a/kernel/power/process.c +++ b/kernel/power/process.c @@ -204,8 +204,6 @@ void thaw_processes(void) __usermodehelper_set_disable_depth(UMH_FREEZING); thaw_workqueues(); - cpuset_wait_for_hotplug(); - read_lock(&tasklist_lock); for_each_process_thread(g, p) { /* No other threads should have PF_SUSPEND_TASK set */ -- cgit v1.2.3 From ef2c41cf38a7559bbf91af42d5b6a4429db8fc68 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 5 Feb 2020 14:26:22 +0100 Subject: clone3: allow spawning processes into cgroups This adds support for creating a process in a different cgroup than its parent. Callers can limit and account processes and threads right from the moment they are spawned: - A service manager can directly spawn new services into dedicated cgroups. - A process can be directly created in a frozen cgroup and will be frozen as well. - The initial accounting jitter experienced by process supervisors and daemons is eliminated with this. - Threaded applications or even thread implementations can choose to create a specific cgroup layout where each thread is spawned directly into a dedicated cgroup. This feature is limited to the unified hierarchy. Callers need to pass a directory file descriptor for the target cgroup. The caller can choose to pass an O_PATH file descriptor. All usual migration restrictions apply, i.e. there can be no processes in inner nodes. In general, creating a process directly in a target cgroup adheres to all migration restrictions. One of the biggest advantages of this feature is that CLONE_INTO_GROUP does not need to grab the write side of the cgroup cgroup_threadgroup_rwsem. This global lock makes moving tasks/threads around super expensive. With clone3() this lock is avoided. Cc: Tejun Heo Cc: Ingo Molnar Cc: Oleg Nesterov Cc: Johannes Weiner Cc: Li Zefan Cc: Peter Zijlstra Cc: cgroups@vger.kernel.org Signed-off-by: Christian Brauner Signed-off-by: Tejun Heo --- include/linux/cgroup-defs.h | 5 +- include/linux/cgroup.h | 20 +++-- include/linux/sched/task.h | 4 + include/uapi/linux/sched.h | 5 ++ kernel/cgroup/cgroup.c | 191 ++++++++++++++++++++++++++++++++++++++------ kernel/cgroup/pids.c | 15 +++- kernel/fork.c | 13 ++- 7 files changed, 214 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 63097cb243cb..68c391f451d1 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -628,8 +628,9 @@ struct cgroup_subsys { void (*cancel_attach)(struct cgroup_taskset *tset); void (*attach)(struct cgroup_taskset *tset); void (*post_attach)(void); - int (*can_fork)(struct task_struct *task); - void (*cancel_fork)(struct task_struct *task); + int (*can_fork)(struct task_struct *task, + struct css_set *cset); + void (*cancel_fork)(struct task_struct *task, struct css_set *cset); void (*fork)(struct task_struct *task); void (*exit)(struct task_struct *task); void (*release)(struct task_struct *task); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index f1219b927817..4598e4da6b1b 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -27,6 +27,8 @@ #include +struct kernel_clone_args; + #ifdef CONFIG_CGROUPS /* @@ -119,9 +121,12 @@ int proc_cgroup_show(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *tsk); void cgroup_fork(struct task_struct *p); -extern int cgroup_can_fork(struct task_struct *p); -extern void cgroup_cancel_fork(struct task_struct *p); -extern void cgroup_post_fork(struct task_struct *p); +extern int cgroup_can_fork(struct task_struct *p, + struct kernel_clone_args *kargs); +extern void cgroup_cancel_fork(struct task_struct *p, + struct kernel_clone_args *kargs); +extern void cgroup_post_fork(struct task_struct *p, + struct kernel_clone_args *kargs); void cgroup_exit(struct task_struct *p); void cgroup_release(struct task_struct *p); void cgroup_free(struct task_struct *p); @@ -705,9 +710,12 @@ static inline int cgroupstats_build(struct cgroupstats *stats, struct dentry *dentry) { return -EINVAL; } static inline void cgroup_fork(struct task_struct *p) {} -static inline int cgroup_can_fork(struct task_struct *p) { return 0; } -static inline void cgroup_cancel_fork(struct task_struct *p) {} -static inline void cgroup_post_fork(struct task_struct *p) {} +static inline int cgroup_can_fork(struct task_struct *p, + struct kernel_clone_args *kargs) { return 0; } +static inline void cgroup_cancel_fork(struct task_struct *p, + struct kernel_clone_args *kargs) {} +static inline void cgroup_post_fork(struct task_struct *p, + struct kernel_clone_args *kargs) {} static inline void cgroup_exit(struct task_struct *p) {} static inline void cgroup_release(struct task_struct *p) {} static inline void cgroup_free(struct task_struct *p) {} diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h index f1879884238e..38359071236a 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h @@ -13,6 +13,7 @@ struct task_struct; struct rusage; union thread_union; +struct css_set; /* All the bits taken by the old clone syscall. */ #define CLONE_LEGACY_FLAGS 0xffffffffULL @@ -29,6 +30,9 @@ struct kernel_clone_args { pid_t *set_tid; /* Number of elements in *set_tid */ size_t set_tid_size; + int cgroup; + struct cgroup *cgrp; + struct css_set *cset; }; /* diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h index 2e3bc22c6f20..3bac0a8ceab2 100644 --- a/include/uapi/linux/sched.h +++ b/include/uapi/linux/sched.h @@ -35,6 +35,7 @@ /* Flags for the clone3() syscall. */ #define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */ +#define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given the right permissions. */ /* * cloning flags intersect with CSIGNAL so can be used with unshare and clone3 @@ -81,6 +82,8 @@ * @set_tid_size: This defines the size of the array referenced * in @set_tid. This cannot be larger than the * kernel's limit of nested PID namespaces. + * @cgroup: If CLONE_INTO_CGROUP is specified set this to + * a file descriptor for the cgroup. * * The structure is versioned by size and thus extensible. * New struct members must go at the end of the struct and @@ -97,11 +100,13 @@ struct clone_args { __aligned_u64 tls; __aligned_u64 set_tid; __aligned_u64 set_tid_size; + __aligned_u64 cgroup; }; #endif #define CLONE_ARGS_SIZE_VER0 64 /* sizeof first published struct */ #define CLONE_ARGS_SIZE_VER1 80 /* sizeof second published struct */ +#define CLONE_ARGS_SIZE_VER2 88 /* sizeof third published struct */ /* * Scheduling policies diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 6d8bdddd8c28..9a8a5ded3c48 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -5881,8 +5881,7 @@ out: * @child: pointer to task_struct of forking parent process. * * A task is associated with the init_css_set until cgroup_post_fork() - * attaches it to the parent's css_set. Empty cg_list indicates that - * @child isn't holding reference to its css_set. + * attaches it to the target css_set. */ void cgroup_fork(struct task_struct *child) { @@ -5908,24 +5907,154 @@ static struct cgroup *cgroup_get_from_file(struct file *f) return cgrp; } +/** + * cgroup_css_set_fork - find or create a css_set for a child process + * @kargs: the arguments passed to create the child process + * + * This functions finds or creates a new css_set which the child + * process will be attached to in cgroup_post_fork(). By default, + * the child process will be given the same css_set as its parent. + * + * If CLONE_INTO_CGROUP is specified this function will try to find an + * existing css_set which includes the requested cgroup and if not create + * a new css_set that the child will be attached to later. If this function + * succeeds it will hold cgroup_threadgroup_rwsem on return. If + * CLONE_INTO_CGROUP is requested this function will grab cgroup mutex + * before grabbing cgroup_threadgroup_rwsem and will hold a reference + * to the target cgroup. + */ +static int cgroup_css_set_fork(struct kernel_clone_args *kargs) + __acquires(&cgroup_mutex) __acquires(&cgroup_threadgroup_rwsem) +{ + int ret; + struct cgroup *dst_cgrp = NULL; + struct css_set *cset; + struct super_block *sb; + struct file *f; + + if (kargs->flags & CLONE_INTO_CGROUP) + mutex_lock(&cgroup_mutex); + + cgroup_threadgroup_change_begin(current); + + spin_lock_irq(&css_set_lock); + cset = task_css_set(current); + get_css_set(cset); + spin_unlock_irq(&css_set_lock); + + if (!(kargs->flags & CLONE_INTO_CGROUP)) { + kargs->cset = cset; + return 0; + } + + f = fget_raw(kargs->cgroup); + if (!f) { + ret = -EBADF; + goto err; + } + sb = f->f_path.dentry->d_sb; + + dst_cgrp = cgroup_get_from_file(f); + if (IS_ERR(dst_cgrp)) { + ret = PTR_ERR(dst_cgrp); + dst_cgrp = NULL; + goto err; + } + + if (cgroup_is_dead(dst_cgrp)) { + ret = -ENODEV; + goto err; + } + + /* + * Verify that we the target cgroup is writable for us. This is + * usually done by the vfs layer but since we're not going through + * the vfs layer here we need to do it "manually". + */ + ret = cgroup_may_write(dst_cgrp, sb); + if (ret) + goto err; + + ret = cgroup_attach_permissions(cset->dfl_cgrp, dst_cgrp, sb, + !(kargs->flags & CLONE_THREAD)); + if (ret) + goto err; + + kargs->cset = find_css_set(cset, dst_cgrp); + if (!kargs->cset) { + ret = -ENOMEM; + goto err; + } + + put_css_set(cset); + fput(f); + kargs->cgrp = dst_cgrp; + return ret; + +err: + cgroup_threadgroup_change_end(current); + mutex_unlock(&cgroup_mutex); + if (f) + fput(f); + if (dst_cgrp) + cgroup_put(dst_cgrp); + put_css_set(cset); + if (kargs->cset) + put_css_set(kargs->cset); + return ret; +} + +/** + * cgroup_css_set_put_fork - drop references we took during fork + * @kargs: the arguments passed to create the child process + * + * Drop references to the prepared css_set and target cgroup if + * CLONE_INTO_CGROUP was requested. + */ +static void cgroup_css_set_put_fork(struct kernel_clone_args *kargs) + __releases(&cgroup_threadgroup_rwsem) __releases(&cgroup_mutex) +{ + cgroup_threadgroup_change_end(current); + + if (kargs->flags & CLONE_INTO_CGROUP) { + struct cgroup *cgrp = kargs->cgrp; + struct css_set *cset = kargs->cset; + + mutex_unlock(&cgroup_mutex); + + if (cset) { + put_css_set(cset); + kargs->cset = NULL; + } + + if (cgrp) { + cgroup_put(cgrp); + kargs->cgrp = NULL; + } + } +} + /** * cgroup_can_fork - called on a new task before the process is exposed * @child: the child process * + * This prepares a new css_set for the child process which the child will + * be attached to in cgroup_post_fork(). * This calls the subsystem can_fork() callbacks. If the cgroup_can_fork() * callback returns an error, the fork aborts with that error code. This * allows for a cgroup subsystem to conditionally allow or deny new forks. */ -int cgroup_can_fork(struct task_struct *child) - __acquires(&cgroup_threadgroup_rwsem) __releases(&cgroup_threadgroup_rwsem) +int cgroup_can_fork(struct task_struct *child, struct kernel_clone_args *kargs) { struct cgroup_subsys *ss; int i, j, ret; - cgroup_threadgroup_change_begin(current); + ret = cgroup_css_set_fork(kargs); + if (ret) + return ret; do_each_subsys_mask(ss, i, have_canfork_callback) { - ret = ss->can_fork(child); + ret = ss->can_fork(child, kargs->cset); if (ret) goto out_revert; } while_each_subsys_mask(); @@ -5937,32 +6066,34 @@ out_revert: if (j >= i) break; if (ss->cancel_fork) - ss->cancel_fork(child); + ss->cancel_fork(child, kargs->cset); } - cgroup_threadgroup_change_end(current); + cgroup_css_set_put_fork(kargs); return ret; } /** - * cgroup_cancel_fork - called if a fork failed after cgroup_can_fork() - * @child: the child process - * - * This calls the cancel_fork() callbacks if a fork failed *after* - * cgroup_can_fork() succeded. - */ -void cgroup_cancel_fork(struct task_struct *child) - __releases(&cgroup_threadgroup_rwsem) + * cgroup_cancel_fork - called if a fork failed after cgroup_can_fork() + * @child: the child process + * @kargs: the arguments passed to create the child process + * + * This calls the cancel_fork() callbacks if a fork failed *after* + * cgroup_can_fork() succeded and cleans up references we took to + * prepare a new css_set for the child process in cgroup_can_fork(). + */ +void cgroup_cancel_fork(struct task_struct *child, + struct kernel_clone_args *kargs) { struct cgroup_subsys *ss; int i; for_each_subsys(ss, i) if (ss->cancel_fork) - ss->cancel_fork(child); + ss->cancel_fork(child, kargs->cset); - cgroup_threadgroup_change_end(current); + cgroup_css_set_put_fork(kargs); } /** @@ -5972,22 +6103,27 @@ void cgroup_cancel_fork(struct task_struct *child) * Attach the child process to its css_set calling the subsystem fork() * callbacks. */ -void cgroup_post_fork(struct task_struct *child) - __releases(&cgroup_threadgroup_rwsem) +void cgroup_post_fork(struct task_struct *child, + struct kernel_clone_args *kargs) + __releases(&cgroup_threadgroup_rwsem) __releases(&cgroup_mutex) { struct cgroup_subsys *ss; struct css_set *cset; int i; + cset = kargs->cset; + kargs->cset = NULL; + spin_lock_irq(&css_set_lock); /* init tasks are special, only link regular threads */ if (likely(child->pid)) { WARN_ON_ONCE(!list_empty(&child->cg_list)); - cset = task_css_set(current); /* current is @child's parent */ - get_css_set(cset); cset->nr_tasks++; css_set_move_task(child, NULL, cset, false); + } else { + put_css_set(cset); + cset = NULL; } /* @@ -6020,7 +6156,16 @@ void cgroup_post_fork(struct task_struct *child) ss->fork(child); } while_each_subsys_mask(); - cgroup_threadgroup_change_end(current); + /* Make the new cset the root_cset of the new cgroup namespace. */ + if (kargs->flags & CLONE_NEWCGROUP) { + struct css_set *rcset = child->nsproxy->cgroup_ns->root_cset; + + get_css_set(cset); + child->nsproxy->cgroup_ns->root_cset = cset; + put_css_set(rcset); + } + + cgroup_css_set_put_fork(kargs); } /** diff --git a/kernel/cgroup/pids.c b/kernel/cgroup/pids.c index 138059eb730d..511af87f685e 100644 --- a/kernel/cgroup/pids.c +++ b/kernel/cgroup/pids.c @@ -33,6 +33,7 @@ #include #include #include +#include #define PIDS_MAX (PID_MAX_LIMIT + 1ULL) #define PIDS_MAX_STR "max" @@ -214,13 +215,16 @@ static void pids_cancel_attach(struct cgroup_taskset *tset) * task_css_check(true) in pids_can_fork() and pids_cancel_fork() relies * on cgroup_threadgroup_change_begin() held by the copy_process(). */ -static int pids_can_fork(struct task_struct *task) +static int pids_can_fork(struct task_struct *task, struct css_set *cset) { struct cgroup_subsys_state *css; struct pids_cgroup *pids; int err; - css = task_css_check(current, pids_cgrp_id, true); + if (cset) + css = cset->subsys[pids_cgrp_id]; + else + css = task_css_check(current, pids_cgrp_id, true); pids = css_pids(css); err = pids_try_charge(pids, 1); if (err) { @@ -235,12 +239,15 @@ static int pids_can_fork(struct task_struct *task) return err; } -static void pids_cancel_fork(struct task_struct *task) +static void pids_cancel_fork(struct task_struct *task, struct css_set *cset) { struct cgroup_subsys_state *css; struct pids_cgroup *pids; - css = task_css_check(current, pids_cgrp_id, true); + if (cset) + css = cset->subsys[pids_cgrp_id]; + else + css = task_css_check(current, pids_cgrp_id, true); pids = css_pids(css); pids_uncharge(pids, 1); } diff --git a/kernel/fork.c b/kernel/fork.c index 9245b6e53f55..635d6369dfb9 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -2180,7 +2180,7 @@ static __latent_entropy struct task_struct *copy_process( * between here and cgroup_post_fork() if an organisation operation is in * progress. */ - retval = cgroup_can_fork(p); + retval = cgroup_can_fork(p, args); if (retval) goto bad_fork_put_pidfd; @@ -2287,7 +2287,7 @@ static __latent_entropy struct task_struct *copy_process( write_unlock_irq(&tasklist_lock); proc_fork_connector(p); - cgroup_post_fork(p); + cgroup_post_fork(p, args); perf_event_fork(p); trace_task_newtask(p, clone_flags); @@ -2298,7 +2298,7 @@ static __latent_entropy struct task_struct *copy_process( bad_fork_cancel_cgroup: spin_unlock(¤t->sighand->siglock); write_unlock_irq(&tasklist_lock); - cgroup_cancel_fork(p); + cgroup_cancel_fork(p, args); bad_fork_put_pidfd: if (clone_flags & CLONE_PIDFD) { fput(pidfile); @@ -2627,6 +2627,9 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, !valid_signal(args.exit_signal))) return -EINVAL; + if ((args.flags & CLONE_INTO_CGROUP) && args.cgroup < 0) + return -EINVAL; + *kargs = (struct kernel_clone_args){ .flags = args.flags, .pidfd = u64_to_user_ptr(args.pidfd), @@ -2637,6 +2640,7 @@ noinline static int copy_clone_args_from_user(struct kernel_clone_args *kargs, .stack_size = args.stack_size, .tls = args.tls, .set_tid_size = args.set_tid_size, + .cgroup = args.cgroup, }; if (args.set_tid && @@ -2680,7 +2684,8 @@ static inline bool clone3_stack_valid(struct kernel_clone_args *kargs) static bool clone3_args_valid(struct kernel_clone_args *kargs) { /* Verify that no unknown flags are passed along. */ - if (kargs->flags & ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND)) + if (kargs->flags & + ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP)) return false; /* -- cgit v1.2.3 From 04ac0ad7e8edc7f1a7b3e219db245060fcaf59a4 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Mon, 10 Feb 2020 09:31:05 +0530 Subject: dt-bindings: clk: qcom: Add support for GPU GX GDSCR In the cases where the GPU SW requires to use the GX GDSCR add support for the same. Signed-off-by: Taniya Das Link: https://lkml.kernel.org/r/1581307266-26989-1-git-send-email-tdas@codeaurora.org Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/qcom,gpucc-sc7180.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/dt-bindings/clock/qcom,gpucc-sc7180.h b/include/dt-bindings/clock/qcom,gpucc-sc7180.h index 0e4643b08b49..65e706d7d9c6 100644 --- a/include/dt-bindings/clock/qcom,gpucc-sc7180.h +++ b/include/dt-bindings/clock/qcom,gpucc-sc7180.h @@ -15,7 +15,8 @@ #define GPU_CC_CXO_CLK 6 #define GPU_CC_GMU_CLK_SRC 7 -/* CAM_CC GDSCRs */ +/* GPU_CC GDSCRs */ #define CX_GDSC 0 +#define GX_GDSC 1 #endif -- cgit v1.2.3 From fdd373a4e0c859c64149aaacd082b6f4e58a6489 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Fri, 24 Jan 2020 14:32:21 -0800 Subject: dt-bindings: clock: Add RPMHCC bindings for SM8250 Add bindings and update documentation for clock rpmh driver on SM8250. Acked-by: Rob Herring Reviewed-by: Vinod Koul Reviewed-by: Bjorn Andersson Signed-off-by: Taniya Das Signed-off-by: Venkata Narendra Kumar Gutta Link: https://lkml.kernel.org/r/1579905147-12142-2-git-send-email-vnkgutta@codeaurora.org Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml | 1 + include/dt-bindings/clock/qcom,rpmh.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml index 2cd158f13bab..2b633a4b0bc2 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,rpmhcc.yaml @@ -20,6 +20,7 @@ properties: - qcom,sc7180-rpmh-clk - qcom,sdm845-rpmh-clk - qcom,sm8150-rpmh-clk + - qcom,sm8250-rpmh-clk clocks: maxItems: 1 diff --git a/include/dt-bindings/clock/qcom,rpmh.h b/include/dt-bindings/clock/qcom,rpmh.h index edcab3f7b7d3..2e6c54e65455 100644 --- a/include/dt-bindings/clock/qcom,rpmh.h +++ b/include/dt-bindings/clock/qcom,rpmh.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2018, 2020, The Linux Foundation. All rights reserved. */ #ifndef _DT_BINDINGS_CLK_MSM_RPMH_H @@ -19,5 +19,7 @@ #define RPMH_RF_CLK3 10 #define RPMH_RF_CLK3_A 11 #define RPMH_IPA_CLK 12 +#define RPMH_LN_BB_CLK1 13 +#define RPMH_LN_BB_CLK1_A 14 #endif -- cgit v1.2.3 From dd03907bf129b42e9e3203fdf405ea9873b28dd3 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 10 Feb 2020 12:14:37 +0900 Subject: ASoC: soc-pcm: call snd_soc_component_open/close() once Current soc_pcm_open() calls snd_soc_component_open() under loop. Thus, it needs to care about opened/not-yet-opened Component. But, if soc-component.c is handling it, soc-pcm.c don't need to care about it. This patch adds opened flag to soc-component.h, and simplify soc-pcm.c. This is one of prepare for cleanup soc-pcm-open() Signed-off-by: Kuninori Morimoto Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/874kvzcey1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 7 +++++-- sound/soc/soc-component.c | 35 ++++++++++++++++++++++++++++------- sound/soc/soc-pcm.c | 19 ++++++------------- 3 files changed, 39 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 154d02fbbfed..1866ecc8e94b 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -147,8 +147,6 @@ struct snd_soc_component { unsigned int active; - unsigned int suspended:1; /* is in suspend PM state */ - struct list_head list; struct list_head card_aux_list; /* for auxiliary bound components */ struct list_head card_list; @@ -182,6 +180,11 @@ struct snd_soc_component { struct dentry *debugfs_root; const char *debugfs_prefix; #endif + + /* bit field */ + unsigned int suspended:1; /* is in suspend PM state */ + unsigned int opened:1; + unsigned int module:1; }; #define for_each_component_dais(component, dai)\ diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 14e175cdeeb8..ee00c09df5e7 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -297,34 +297,55 @@ EXPORT_SYMBOL_GPL(snd_soc_component_set_jack); int snd_soc_component_module_get(struct snd_soc_component *component, int upon_open) { + if (component->module) + return 0; + if (component->driver->module_get_upon_open == !!upon_open && !try_module_get(component->dev->driver->owner)) return -ENODEV; + component->module = 1; + return 0; } void snd_soc_component_module_put(struct snd_soc_component *component, int upon_open) { - if (component->driver->module_get_upon_open == !!upon_open) + if (component->module && + component->driver->module_get_upon_open == !!upon_open) module_put(component->dev->driver->owner); + + component->module = 0; } int snd_soc_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (component->driver->open) - return component->driver->open(component, substream); - return 0; + int ret = 0; + + if (!component->opened && + component->driver->open) + ret = component->driver->open(component, substream); + + if (ret == 0) + component->opened = 1; + + return ret; } int snd_soc_component_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - if (component->driver->close) - return component->driver->close(component, substream); - return 0; + int ret = 0; + + if (component->opened && + component->driver->close) + ret = component->driver->close(component, substream); + + component->opened = 0; + + return ret; } int snd_soc_component_prepare(struct snd_soc_component *component, diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index d53afb96b05b..ae94d8a86992 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -463,16 +463,13 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) hw->rate_max = min_not_zero(hw->rate_max, rate_max); } -static int soc_pcm_components_open(struct snd_pcm_substream *substream, - struct snd_soc_component **last) +static int soc_pcm_components_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; int i, ret = 0; for_each_rtd_components(rtd, i, component) { - *last = component; - ret = snd_soc_component_module_get_when_open(component); if (ret < 0) { dev_err(component->dev, @@ -489,21 +486,17 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream, return ret; } } - *last = NULL; + return 0; } -static int soc_pcm_components_close(struct snd_pcm_substream *substream, - struct snd_soc_component *last) +static int soc_pcm_components_close(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_component *component; int i, r, ret = 0; for_each_rtd_components(rtd, i, component) { - if (component == last) - break; - r = snd_soc_component_close(component, substream); if (r < 0) ret = r; /* use last ret */ @@ -545,7 +538,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) goto out; } - ret = soc_pcm_components_open(substream, &component); + ret = soc_pcm_components_open(substream); if (ret < 0) goto component_err; @@ -642,7 +635,7 @@ codec_dai_err: snd_soc_dai_shutdown(codec_dai, substream); component_err: - soc_pcm_components_close(substream, component); + soc_pcm_components_close(substream); snd_soc_dai_shutdown(cpu_dai, substream); out: @@ -696,7 +689,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) soc_rtd_shutdown(rtd, substream); - soc_pcm_components_close(substream, NULL); + soc_pcm_components_close(substream); snd_soc_dapm_stream_stop(rtd, substream->stream); -- cgit v1.2.3 From d2d46dfaa72b41b4d6adf6ef1068ee00a51ba0fc Mon Sep 17 00:00:00 2001 From: Horia Geantă Date: Thu, 16 Jan 2020 09:37:16 +0200 Subject: dt-bindings: clock: imx8mn: add SNVS clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add macro for the SNVS clock of the i.MX8MN. Signed-off-by: Horia Geantă Acked-by: Rob Herring Signed-off-by: Shawn Guo --- include/dt-bindings/clock/imx8mn-clock.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/dt-bindings/clock/imx8mn-clock.h b/include/dt-bindings/clock/imx8mn-clock.h index 0f2b8423ce1d..43883fe65db5 100644 --- a/include/dt-bindings/clock/imx8mn-clock.h +++ b/include/dt-bindings/clock/imx8mn-clock.h @@ -228,6 +228,8 @@ #define IMX8MN_SYS_PLL2_333M_CG 209 #define IMX8MN_SYS_PLL2_500M_CG 210 -#define IMX8MN_CLK_END 211 +#define IMX8MN_CLK_SNVS_ROOT 211 + +#define IMX8MN_CLK_END 212 #endif -- cgit v1.2.3 From 5a7ea52b6faec6f8877e49645a80349b2d970f0a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:58:17 +0100 Subject: PM: QoS: Drop pm_qos_update_request_timeout() The pm_qos_update_request_timeout() function is not called from anywhere, so drop it along with the work member in struct pm_qos_request needed by it. Also drop the useless pm_qos_update_request_timeout trace event that is only triggered by that function (so it never triggers at all) and update the trace events documentation accordingly. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- Documentation/trace/events-power.rst | 2 -- include/linux/pm_qos.h | 4 --- include/trace/events/power.h | 24 ------------------ kernel/power/qos.c | 48 ------------------------------------ 4 files changed, 78 deletions(-) (limited to 'include') diff --git a/Documentation/trace/events-power.rst b/Documentation/trace/events-power.rst index 2ef318962e29..eec7453a168e 100644 --- a/Documentation/trace/events-power.rst +++ b/Documentation/trace/events-power.rst @@ -78,11 +78,9 @@ target/flags update. pm_qos_add_request "pm_qos_class=%s value=%d" pm_qos_update_request "pm_qos_class=%s value=%d" pm_qos_remove_request "pm_qos_class=%s value=%d" - pm_qos_update_request_timeout "pm_qos_class=%s value=%d, timeout_us=%ld" The first parameter gives the QoS class name (e.g. "CPU_DMA_LATENCY"). The second parameter is value to be added/updated/removed. -The third parameter is timeout value in usec. :: pm_qos_update_target "action=%s prev_value=%d curr_value=%d" diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 19eafca5680e..4747bdb6bed2 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -8,7 +8,6 @@ #include #include #include -#include enum { PM_QOS_RESERVED = 0, @@ -43,7 +42,6 @@ enum pm_qos_flags_status { struct pm_qos_request { struct plist_node node; int pm_qos_class; - struct delayed_work work; /* for pm_qos_update_request_timeout */ }; struct pm_qos_flags_request { @@ -149,8 +147,6 @@ void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, s32 new_value); -void pm_qos_update_request_timeout(struct pm_qos_request *req, - s32 new_value, unsigned long timeout_us); void pm_qos_remove_request(struct pm_qos_request *req); int pm_qos_request(int pm_qos_class); diff --git a/include/trace/events/power.h b/include/trace/events/power.h index 7457e238e1b7..ecf39daabf16 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -404,30 +404,6 @@ DEFINE_EVENT(pm_qos_request, pm_qos_remove_request, TP_ARGS(pm_qos_class, value) ); -TRACE_EVENT(pm_qos_update_request_timeout, - - TP_PROTO(int pm_qos_class, s32 value, unsigned long timeout_us), - - TP_ARGS(pm_qos_class, value, timeout_us), - - TP_STRUCT__entry( - __field( int, pm_qos_class ) - __field( s32, value ) - __field( unsigned long, timeout_us ) - ), - - TP_fast_assign( - __entry->pm_qos_class = pm_qos_class; - __entry->value = value; - __entry->timeout_us = timeout_us; - ), - - TP_printk("pm_qos_class=%s value=%d, timeout_us=%ld", - __print_symbolic(__entry->pm_qos_class, - { PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }), - __entry->value, __entry->timeout_us) -); - DECLARE_EVENT_CLASS(pm_qos_update, TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), diff --git a/kernel/power/qos.c b/kernel/power/qos.c index d932fa42e8e4..67dab7f330e4 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -295,21 +295,6 @@ static void __pm_qos_update_request(struct pm_qos_request *req, &req->node, PM_QOS_UPDATE_REQ, new_value); } -/** - * pm_qos_work_fn - the timeout handler of pm_qos_update_request_timeout - * @work: work struct for the delayed work (timeout) - * - * This cancels the timeout request by falling back to the default at timeout. - */ -static void pm_qos_work_fn(struct work_struct *work) -{ - struct pm_qos_request *req = container_of(to_delayed_work(work), - struct pm_qos_request, - work); - - __pm_qos_update_request(req, PM_QOS_DEFAULT_VALUE); -} - /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle @@ -334,7 +319,6 @@ void pm_qos_add_request(struct pm_qos_request *req, return; } req->pm_qos_class = pm_qos_class; - INIT_DELAYED_WORK(&req->work, pm_qos_work_fn); trace_pm_qos_add_request(pm_qos_class, value); pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, &req->node, PM_QOS_ADD_REQ, value); @@ -362,40 +346,10 @@ void pm_qos_update_request(struct pm_qos_request *req, return; } - cancel_delayed_work_sync(&req->work); __pm_qos_update_request(req, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); -/** - * pm_qos_update_request_timeout - modifies an existing qos request temporarily. - * @req : handle to list element holding a pm_qos request to use - * @new_value: defines the temporal qos request - * @timeout_us: the effective duration of this qos request in usecs. - * - * After timeout_us, this qos request is cancelled automatically. - */ -void pm_qos_update_request_timeout(struct pm_qos_request *req, s32 new_value, - unsigned long timeout_us) -{ - if (!req) - return; - if (WARN(!pm_qos_request_active(req), - "%s called for unknown object.", __func__)) - return; - - cancel_delayed_work_sync(&req->work); - - trace_pm_qos_update_request_timeout(req->pm_qos_class, - new_value, timeout_us); - if (new_value != req->node.prio) - pm_qos_update_target( - pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_UPDATE_REQ, new_value); - - schedule_delayed_work(&req->work, usecs_to_jiffies(timeout_us)); -} - /** * pm_qos_remove_request - modifies an existing qos request * @req: handle to request list element @@ -415,8 +369,6 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } - cancel_delayed_work_sync(&req->work); - trace_pm_qos_remove_request(req->pm_qos_class, PM_QOS_DEFAULT_VALUE); pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, &req->node, PM_QOS_REMOVE_REQ, -- cgit v1.2.3 From 87ad7356799622b69478076cd99df7a5024992cb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Feb 2020 23:58:26 +0100 Subject: PM: QoS: Drop the PM_QOS_SUM QoS type The PM_QOS_SUM QoS type is not used, so drop it along with the code referring to it in pm_qos_get_value() and the related local variables in there. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 1 - kernel/power/qos.c | 9 --------- 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 4747bdb6bed2..48bfb96a9360 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -53,7 +53,6 @@ enum pm_qos_type { PM_QOS_UNITIALIZED, PM_QOS_MAX, /* return the largest value */ PM_QOS_MIN, /* return the smallest value */ - PM_QOS_SUM /* return the sum */ }; /* diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 67dab7f330e4..a6be7faa1974 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -101,9 +101,6 @@ static const struct file_operations pm_qos_power_fops = { /* unlocked internal variant */ static inline int pm_qos_get_value(struct pm_qos_constraints *c) { - struct plist_node *node; - int total_value = 0; - if (plist_head_empty(&c->list)) return c->no_constraint_value; @@ -114,12 +111,6 @@ static inline int pm_qos_get_value(struct pm_qos_constraints *c) case PM_QOS_MAX: return plist_last(&c->list)->prio; - case PM_QOS_SUM: - plist_for_each(node, &c->list) - total_value += node->prio; - - return total_value; - default: /* runtime check for not using enum */ BUG(); -- cgit v1.2.3 From 02c92a378940ba1a46b6b7ad277f1b07f8093dbc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:01:18 +0100 Subject: PM: QoS: Redefine struct pm_qos_request and drop struct pm_qos_object First, change the definition of struct pm_qos_request so that it contains a struct pm_qos_constraints pointer (called "qos") instead of a PM QoS class number (in preparation for dropping the PM QoS classes concept altogether going forward) and move its definition (along with the definition of struct pm_qos_flags_request that does not change) after the definition of struct pm_qos_constraints. Next, drop the definition of struct pm_qos_object and the null_pm_qos and cpu_dma_pm_qos variables of that type along with pm_qos_array[] holding pointers to them and change the code to refer to the pm_qos_constraints structure directly or to use the new qos pointer in struct pm_qos_request for that instead of going through pm_qos_array[] to access it. Also update kerneldoc comments that mention pm_qos_class to refer to PM_QOS_CPU_DMA_LATENCY directly instead. Finally, drop register_pm_qos_misc(), introduce cpu_latency_qos_miscdev (with the name field set to "cpu_dma_latency") to implement the CPU latency QoS interface in /dev/ and register it directly from pm_qos_power_init(). After these changes the notion of PM QoS classes remains only in the API (in the form of redundant function parameters that are ignored) and in the definitions of PM QoS trace events. While at it, some redundant local variables are dropped etc. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 20 ++++---- kernel/power/qos.c | 121 +++++++++++++++++-------------------------------- 2 files changed, 51 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 48bfb96a9360..bef110aa80cc 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -39,16 +39,6 @@ enum pm_qos_flags_status { #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) -struct pm_qos_request { - struct plist_node node; - int pm_qos_class; -}; - -struct pm_qos_flags_request { - struct list_head node; - s32 flags; /* Do not change to 64 bit */ -}; - enum pm_qos_type { PM_QOS_UNITIALIZED, PM_QOS_MAX, /* return the largest value */ @@ -69,6 +59,16 @@ struct pm_qos_constraints { struct blocking_notifier_head *notifiers; }; +struct pm_qos_request { + struct plist_node node; + struct pm_qos_constraints *qos; +}; + +struct pm_qos_flags_request { + struct list_head node; + s32 flags; /* Do not change to 64 bit */ +}; + struct pm_qos_flags { struct list_head list; s32 effective_flags; /* Do not change to 64 bit */ diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 9f67584d4466..952c5f55e23c 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -54,16 +54,8 @@ * or pm_qos_object list and pm_qos_objects need to happen with pm_qos_lock * held, taken with _irqsave. One lock to rule them all */ -struct pm_qos_object { - struct pm_qos_constraints *constraints; - struct miscdevice pm_qos_power_miscdev; - char *name; -}; - static DEFINE_SPINLOCK(pm_qos_lock); -static struct pm_qos_object null_pm_qos; - static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static struct pm_qos_constraints cpu_dma_constraints = { .list = PLIST_HEAD_INIT(cpu_dma_constraints.list), @@ -73,15 +65,6 @@ static struct pm_qos_constraints cpu_dma_constraints = { .type = PM_QOS_MIN, .notifiers = &cpu_dma_lat_notifier, }; -static struct pm_qos_object cpu_dma_pm_qos = { - .constraints = &cpu_dma_constraints, - .name = "cpu_dma_latency", -}; - -static struct pm_qos_object *pm_qos_array[] = { - &null_pm_qos, - &cpu_dma_pm_qos, -}; /** * pm_qos_read_value - Return the current effective constraint value. @@ -248,46 +231,34 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, /** * pm_qos_request - returns current system wide qos expectation - * @pm_qos_class: identification of which qos value is requested + * @pm_qos_class: Ignored. * * This function returns the current target value. */ int pm_qos_request(int pm_qos_class) { - return pm_qos_read_value(pm_qos_array[pm_qos_class]->constraints); + return pm_qos_read_value(&cpu_dma_constraints); } EXPORT_SYMBOL_GPL(pm_qos_request); int pm_qos_request_active(struct pm_qos_request *req) { - return req->pm_qos_class != 0; + return req->qos == &cpu_dma_constraints; } EXPORT_SYMBOL_GPL(pm_qos_request_active); -static void __pm_qos_update_request(struct pm_qos_request *req, - s32 new_value) -{ - trace_pm_qos_update_request(req->pm_qos_class, new_value); - - if (new_value != req->node.prio) - pm_qos_update_target( - pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_UPDATE_REQ, new_value); -} - /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle - * @pm_qos_class: identifies which list of qos request to use + * @pm_qos_class: Ignored. * @value: defines the qos request * - * This function inserts a new entry in the pm_qos_class list of requested qos - * performance characteristics. It recomputes the aggregate QoS expectations - * for the pm_qos_class of parameters and initializes the pm_qos_request + * This function inserts a new entry in the PM_QOS_CPU_DMA_LATENCY list of + * requested QoS performance characteristics. It recomputes the aggregate QoS + * expectations for the PM_QOS_CPU_DMA_LATENCY list and initializes the @req * handle. Caller needs to save this handle for later use in updates and * removal. */ - void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value) { @@ -298,10 +269,11 @@ void pm_qos_add_request(struct pm_qos_request *req, WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n"); return; } - req->pm_qos_class = pm_qos_class; - trace_pm_qos_add_request(pm_qos_class, value); - pm_qos_update_target(pm_qos_array[pm_qos_class]->constraints, - &req->node, PM_QOS_ADD_REQ, value); + + trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); + + req->qos = &cpu_dma_constraints; + pm_qos_update_target(req->qos, &req->node, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -310,13 +282,12 @@ EXPORT_SYMBOL_GPL(pm_qos_add_request); * @req : handle to list element holding a pm_qos request to use * @value: defines the qos request * - * Updates an existing qos request for the pm_qos_class of parameters along - * with updating the target pm_qos_class value. + * Updates an existing qos request for the PM_QOS_CPU_DMA_LATENCY list along + * with updating the target PM_QOS_CPU_DMA_LATENCY value. * * Attempts are made to make this code callable on hot code paths. */ -void pm_qos_update_request(struct pm_qos_request *req, - s32 new_value) +void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) { if (!req) /*guard against callers passing in null */ return; @@ -326,7 +297,12 @@ void pm_qos_update_request(struct pm_qos_request *req, return; } - __pm_qos_update_request(req, new_value); + trace_pm_qos_update_request(PM_QOS_CPU_DMA_LATENCY, new_value); + + if (new_value == req->node.prio) + return; + + pm_qos_update_target(req->qos, &req->node, PM_QOS_UPDATE_REQ, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); @@ -335,7 +311,7 @@ EXPORT_SYMBOL_GPL(pm_qos_update_request); * @req: handle to request list element * * Will remove pm qos request from the list of constraints and - * recompute the current target value for the pm_qos_class. Call this + * recompute the current target value for PM_QOS_CPU_DMA_LATENCY. Call this * on slow code paths. */ void pm_qos_remove_request(struct pm_qos_request *req) @@ -349,9 +325,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } - trace_pm_qos_remove_request(req->pm_qos_class, PM_QOS_DEFAULT_VALUE); - pm_qos_update_target(pm_qos_array[req->pm_qos_class]->constraints, - &req->node, PM_QOS_REMOVE_REQ, + trace_pm_qos_remove_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + + pm_qos_update_target(req->qos, &req->node, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } @@ -359,41 +335,31 @@ EXPORT_SYMBOL_GPL(pm_qos_remove_request); /** * pm_qos_add_notifier - sets notification entry for changes to target value - * @pm_qos_class: identifies which qos target changes should be notified. + * @pm_qos_class: Ignored. * @notifier: notifier block managed by caller. * * will register the notifier into a notification chain that gets called - * upon changes to the pm_qos_class target value. + * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. */ int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) { - int retval; - - retval = blocking_notifier_chain_register( - pm_qos_array[pm_qos_class]->constraints->notifiers, - notifier); - - return retval; + return blocking_notifier_chain_register(cpu_dma_constraints.notifiers, + notifier); } EXPORT_SYMBOL_GPL(pm_qos_add_notifier); /** * pm_qos_remove_notifier - deletes notification entry from chain. - * @pm_qos_class: identifies which qos target changes are notified. + * @pm_qos_class: Ignored. * @notifier: notifier block to be removed. * * will remove the notifier from the notification chain that gets called - * upon changes to the pm_qos_class target value. + * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. */ int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) { - int retval; - - retval = blocking_notifier_chain_unregister( - pm_qos_array[pm_qos_class]->constraints->notifiers, - notifier); - - return retval; + return blocking_notifier_chain_unregister(cpu_dma_constraints.notifiers, + notifier); } EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); @@ -436,7 +402,7 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); - value = pm_qos_get_value(pm_qos_array[req->pm_qos_class]->constraints); + value = pm_qos_get_value(&cpu_dma_constraints); spin_unlock_irqrestore(&pm_qos_lock, flags); return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); @@ -471,25 +437,20 @@ static const struct file_operations pm_qos_power_fops = { .llseek = noop_llseek, }; -static int register_pm_qos_misc(struct pm_qos_object *qos) -{ - qos->pm_qos_power_miscdev.minor = MISC_DYNAMIC_MINOR; - qos->pm_qos_power_miscdev.name = qos->name; - qos->pm_qos_power_miscdev.fops = &pm_qos_power_fops; - - return misc_register(&qos->pm_qos_power_miscdev); -} +static struct miscdevice cpu_latency_qos_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cpu_dma_latency", + .fops = &pm_qos_power_fops, +}; static int __init pm_qos_power_init(void) { int ret; - BUILD_BUG_ON(ARRAY_SIZE(pm_qos_array) != PM_QOS_NUM_CLASSES); - - ret = register_pm_qos_misc(pm_qos_array[PM_QOS_CPU_DMA_LATENCY]); + ret = misc_register(&cpu_latency_qos_miscdev); if (ret < 0) pr_err("%s: %s setup failed\n", __func__, - pm_qos_array[PM_QOS_CPU_DMA_LATENCY]->name); + cpu_latency_qos_miscdev.name); return ret; } -- cgit v1.2.3 From 3a4a0042228a854d9b1073050620820b4a977e6e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:02:30 +0100 Subject: PM: QoS: Drop PM_QOS_CPU_DMA_LATENCY notifier chain Notice that pm_qos_remove_notifier() is not used at all and the only caller of pm_qos_add_notifier() is the cpuidle core, which only needs the PM_QOS_CPU_DMA_LATENCY notifier to invoke wake_up_all_idle_cpus() upon changes of the PM_QOS_CPU_DMA_LATENCY target value. First, to ensure that wake_up_all_idle_cpus() will be called whenever the PM_QOS_CPU_DMA_LATENCY target value changes, modify the pm_qos_add/update/remove_request() family of functions to check if the effective constraint for the PM_QOS_CPU_DMA_LATENCY has changed and call wake_up_all_idle_cpus() directly in that case. Next, drop the PM_QOS_CPU_DMA_LATENCY notifier from cpuidle as it is not necessary any more. Finally, drop both pm_qos_add_notifier() and pm_qos_remove_notifier(), as they have no callers now, along with cpu_dma_lat_notifier which is only used by them. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/cpuidle/cpuidle.c | 40 +--------------------------------------- include/linux/pm_qos.h | 2 -- kernel/power/qos.c | 47 +++++++++++------------------------------------ 3 files changed, 12 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index de81298051b3..c149d9e20dfd 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -736,53 +736,15 @@ int cpuidle_register(struct cpuidle_driver *drv, } EXPORT_SYMBOL_GPL(cpuidle_register); -#ifdef CONFIG_SMP - -/* - * This function gets called when a part of the kernel has a new latency - * requirement. This means we need to get all processors out of their C-state, - * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that - * wakes them all right up. - */ -static int cpuidle_latency_notify(struct notifier_block *b, - unsigned long l, void *v) -{ - wake_up_all_idle_cpus(); - return NOTIFY_OK; -} - -static struct notifier_block cpuidle_latency_notifier = { - .notifier_call = cpuidle_latency_notify, -}; - -static inline void latency_notifier_init(struct notifier_block *n) -{ - pm_qos_add_notifier(PM_QOS_CPU_DMA_LATENCY, n); -} - -#else /* CONFIG_SMP */ - -#define latency_notifier_init(x) do { } while (0) - -#endif /* CONFIG_SMP */ - /** * cpuidle_init - core initializer */ static int __init cpuidle_init(void) { - int ret; - if (cpuidle_disabled()) return -ENODEV; - ret = cpuidle_add_interface(cpu_subsys.dev_root); - if (ret) - return ret; - - latency_notifier_init(&cpuidle_latency_notifier); - - return 0; + return cpuidle_add_interface(cpu_subsys.dev_root); } module_param(off, int, 0444); diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index bef110aa80cc..cb57e5918a25 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -149,8 +149,6 @@ void pm_qos_update_request(struct pm_qos_request *req, void pm_qos_remove_request(struct pm_qos_request *req); int pm_qos_request(int pm_qos_class); -int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier); -int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier); int pm_qos_request_active(struct pm_qos_request *req); s32 pm_qos_read_value(struct pm_qos_constraints *c); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 952c5f55e23c..201b43bc6457 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -56,14 +56,12 @@ */ static DEFINE_SPINLOCK(pm_qos_lock); -static BLOCKING_NOTIFIER_HEAD(cpu_dma_lat_notifier); static struct pm_qos_constraints cpu_dma_constraints = { .list = PLIST_HEAD_INIT(cpu_dma_constraints.list), .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .no_constraint_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, .type = PM_QOS_MIN, - .notifiers = &cpu_dma_lat_notifier, }; /** @@ -247,6 +245,14 @@ int pm_qos_request_active(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_request_active); +static void cpu_latency_qos_update(struct pm_qos_request *req, + enum pm_qos_req_action action, s32 value) +{ + int ret = pm_qos_update_target(req->qos, &req->node, action, value); + if (ret > 0) + wake_up_all_idle_cpus(); +} + /** * pm_qos_add_request - inserts new qos request into the list * @req: pointer to a preallocated handle @@ -273,7 +279,7 @@ void pm_qos_add_request(struct pm_qos_request *req, trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); req->qos = &cpu_dma_constraints; - pm_qos_update_target(req->qos, &req->node, PM_QOS_ADD_REQ, value); + cpu_latency_qos_update(req, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -302,7 +308,7 @@ void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) if (new_value == req->node.prio) return; - pm_qos_update_target(req->qos, &req->node, PM_QOS_UPDATE_REQ, new_value); + cpu_latency_qos_update(req, PM_QOS_UPDATE_REQ, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); @@ -327,42 +333,11 @@ void pm_qos_remove_request(struct pm_qos_request *req) trace_pm_qos_remove_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); - pm_qos_update_target(req->qos, &req->node, PM_QOS_REMOVE_REQ, - PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_update(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } EXPORT_SYMBOL_GPL(pm_qos_remove_request); -/** - * pm_qos_add_notifier - sets notification entry for changes to target value - * @pm_qos_class: Ignored. - * @notifier: notifier block managed by caller. - * - * will register the notifier into a notification chain that gets called - * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. - */ -int pm_qos_add_notifier(int pm_qos_class, struct notifier_block *notifier) -{ - return blocking_notifier_chain_register(cpu_dma_constraints.notifiers, - notifier); -} -EXPORT_SYMBOL_GPL(pm_qos_add_notifier); - -/** - * pm_qos_remove_notifier - deletes notification entry from chain. - * @pm_qos_class: Ignored. - * @notifier: notifier block to be removed. - * - * will remove the notifier from the notification chain that gets called - * upon changes to the PM_QOS_CPU_DMA_LATENCY target value. - */ -int pm_qos_remove_notifier(int pm_qos_class, struct notifier_block *notifier) -{ - return blocking_notifier_chain_unregister(cpu_dma_constraints.notifiers, - notifier); -} -EXPORT_SYMBOL_GPL(pm_qos_remove_notifier); - /* User space interface to global PM QoS via misc device. */ static int pm_qos_power_open(struct inode *inode, struct file *filp) -- cgit v1.2.3 From 2552d3520132a22834e0be85c51168a7a798608c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:04:31 +0100 Subject: PM: QoS: Rename things related to the CPU latency QoS First, rename PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE to PM_QOS_CPU_LATENCY_DEFAULT_VALUE and update all of the code referring to it accordingly. Next, rename cpu_dma_constraints to cpu_latency_constraints, move the definition of it closer to the functions referring to it and update all of them accordingly. [While at it, add a comment to mark the start of the code related to the CPU latency QoS.] Finally, rename the pm_qos_power_*() family of functions and pm_qos_power_fops to cpu_latency_qos_*() and cpu_latency_qos_fops, respectively, and update the definition of cpu_latency_qos_miscdev. [While at it, update the miscdev interface code start comment.] No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Acked-by: Greg Kroah-Hartman Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- drivers/tty/serial/8250/8250_omap.c | 6 ++-- drivers/tty/serial/omap-serial.c | 6 ++-- include/linux/pm_qos.h | 2 +- kernel/power/qos.c | 56 +++++++++++++++++++------------------ 4 files changed, 36 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 6f343ca08440..19f8d2f9e7ba 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1222,8 +1222,8 @@ static int omap8250_probe(struct platform_device *pdev) DEFAULT_CLK_SPEED); } - priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; - priv->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + priv->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; pm_qos_add_request(&priv->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, priv->latency); INIT_WORK(&priv->qos_work, omap8250_uart_qos_work); @@ -1445,7 +1445,7 @@ static int omap8250_runtime_suspend(struct device *dev) if (up->dma && up->dma->rxchan) omap_8250_rx_dma_flush(up); - priv->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + priv->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; schedule_work(&priv->qos_work); return 0; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 48017cec7f2f..ce2558767eee 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -1722,8 +1722,8 @@ static int serial_omap_probe(struct platform_device *pdev) DEFAULT_CLK_SPEED); } - up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; - up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + up->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; + up->calc_latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; pm_qos_add_request(&up->pm_qos_request, PM_QOS_CPU_DMA_LATENCY, up->latency); INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); @@ -1869,7 +1869,7 @@ static int serial_omap_runtime_suspend(struct device *dev) serial_omap_enable_wakeup(up, true); - up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE; + up->latency = PM_QOS_CPU_LATENCY_DEFAULT_VALUE; schedule_work(&up->qos_work); return 0; diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index cb57e5918a25..a3e0bfc6c470 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -28,7 +28,7 @@ enum pm_qos_flags_status { #define PM_QOS_LATENCY_ANY S32_MAX #define PM_QOS_LATENCY_ANY_NS ((s64)PM_QOS_LATENCY_ANY * NSEC_PER_USEC) -#define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) +#define PM_QOS_CPU_LATENCY_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE PM_QOS_LATENCY_ANY #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY #define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 201b43bc6457..a6bf53e9db17 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -56,14 +56,6 @@ */ static DEFINE_SPINLOCK(pm_qos_lock); -static struct pm_qos_constraints cpu_dma_constraints = { - .list = PLIST_HEAD_INIT(cpu_dma_constraints.list), - .target_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, - .default_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, - .no_constraint_value = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE, - .type = PM_QOS_MIN, -}; - /** * pm_qos_read_value - Return the current effective constraint value. * @c: List of PM QoS constraint requests. @@ -227,6 +219,16 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, return prev_value != curr_value; } +/* Definitions related to the CPU latency QoS. */ + +static struct pm_qos_constraints cpu_latency_constraints = { + .list = PLIST_HEAD_INIT(cpu_latency_constraints.list), + .target_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE, + .default_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE, + .no_constraint_value = PM_QOS_CPU_LATENCY_DEFAULT_VALUE, + .type = PM_QOS_MIN, +}; + /** * pm_qos_request - returns current system wide qos expectation * @pm_qos_class: Ignored. @@ -235,13 +237,13 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, */ int pm_qos_request(int pm_qos_class) { - return pm_qos_read_value(&cpu_dma_constraints); + return pm_qos_read_value(&cpu_latency_constraints); } EXPORT_SYMBOL_GPL(pm_qos_request); int pm_qos_request_active(struct pm_qos_request *req) { - return req->qos == &cpu_dma_constraints; + return req->qos == &cpu_latency_constraints; } EXPORT_SYMBOL_GPL(pm_qos_request_active); @@ -278,7 +280,7 @@ void pm_qos_add_request(struct pm_qos_request *req, trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); - req->qos = &cpu_dma_constraints; + req->qos = &cpu_latency_constraints; cpu_latency_qos_update(req, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -338,9 +340,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_remove_request); -/* User space interface to global PM QoS via misc device. */ +/* User space interface to the CPU latency QoS via misc device. */ -static int pm_qos_power_open(struct inode *inode, struct file *filp) +static int cpu_latency_qos_open(struct inode *inode, struct file *filp) { struct pm_qos_request *req; @@ -354,7 +356,7 @@ static int pm_qos_power_open(struct inode *inode, struct file *filp) return 0; } -static int pm_qos_power_release(struct inode *inode, struct file *filp) +static int cpu_latency_qos_release(struct inode *inode, struct file *filp) { struct pm_qos_request *req = filp->private_data; @@ -366,8 +368,8 @@ static int pm_qos_power_release(struct inode *inode, struct file *filp) return 0; } -static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, - size_t count, loff_t *f_pos) +static ssize_t cpu_latency_qos_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) { struct pm_qos_request *req = filp->private_data; unsigned long flags; @@ -377,14 +379,14 @@ static ssize_t pm_qos_power_read(struct file *filp, char __user *buf, return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); - value = pm_qos_get_value(&cpu_dma_constraints); + value = pm_qos_get_value(&cpu_latency_constraints); spin_unlock_irqrestore(&pm_qos_lock, flags); return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32)); } -static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, - size_t count, loff_t *f_pos) +static ssize_t cpu_latency_qos_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) { s32 value; @@ -404,21 +406,21 @@ static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf, return count; } -static const struct file_operations pm_qos_power_fops = { - .write = pm_qos_power_write, - .read = pm_qos_power_read, - .open = pm_qos_power_open, - .release = pm_qos_power_release, +static const struct file_operations cpu_latency_qos_fops = { + .write = cpu_latency_qos_write, + .read = cpu_latency_qos_read, + .open = cpu_latency_qos_open, + .release = cpu_latency_qos_release, .llseek = noop_llseek, }; static struct miscdevice cpu_latency_qos_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "cpu_dma_latency", - .fops = &pm_qos_power_fops, + .fops = &cpu_latency_qos_fops, }; -static int __init pm_qos_power_init(void) +static int __init cpu_latency_qos_init(void) { int ret; @@ -429,7 +431,7 @@ static int __init pm_qos_power_init(void) return ret; } -late_initcall(pm_qos_power_init); +late_initcall(cpu_latency_qos_init); /* Definitions related to the frequency QoS below. */ -- cgit v1.2.3 From 333eed7d20069e2d80446f5fdf9ac3868b55e7b9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:06:17 +0100 Subject: PM: QoS: Simplify definitions of CPU latency QoS trace events Modify the definitions of the CPU latency QoS trace events to take one argument (since PM_QOS_CPU_DMA_LATENCY is always passed as the pm_qos_class argument to them) and update the documentation of them accordingly (while at it, make it explicitly mention CPU latency QoS and relocate it after the device PM QoS trace events documentation). The names and output format of the trace events do not change to preserve user space compatibility. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- Documentation/trace/events-power.rst | 19 ++++++++++--------- include/trace/events/power.h | 35 +++++++++++++++++------------------ kernel/power/qos.c | 16 ++++++++-------- 3 files changed, 35 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/Documentation/trace/events-power.rst b/Documentation/trace/events-power.rst index eec7453a168e..f45bf11fa88d 100644 --- a/Documentation/trace/events-power.rst +++ b/Documentation/trace/events-power.rst @@ -73,14 +73,6 @@ The second parameter is the power domain target state. ================ The PM QoS events are used for QoS add/update/remove request and for target/flags update. -:: - - pm_qos_add_request "pm_qos_class=%s value=%d" - pm_qos_update_request "pm_qos_class=%s value=%d" - pm_qos_remove_request "pm_qos_class=%s value=%d" - -The first parameter gives the QoS class name (e.g. "CPU_DMA_LATENCY"). -The second parameter is value to be added/updated/removed. :: pm_qos_update_target "action=%s prev_value=%d curr_value=%d" @@ -90,7 +82,7 @@ The first parameter gives the QoS action name (e.g. "ADD_REQ"). The second parameter is the previous QoS value. The third parameter is the current QoS value to update. -And, there are also events used for device PM QoS add/update/remove request. +There are also events used for device PM QoS add/update/remove request. :: dev_pm_qos_add_request "device=%s type=%s new_value=%d" @@ -101,3 +93,12 @@ The first parameter gives the device name which tries to add/update/remove QoS requests. The second parameter gives the request type (e.g. "DEV_PM_QOS_RESUME_LATENCY"). The third parameter is value to be added/updated/removed. + +And, there are events used for CPU latency QoS add/update/remove request. +:: + + pm_qos_add_request "value=%d" + pm_qos_update_request "value=%d" + pm_qos_remove_request "value=%d" + +The parameter is the value to be added/updated/removed. diff --git a/include/trace/events/power.h b/include/trace/events/power.h index ecf39daabf16..af5018aa9517 100644 --- a/include/trace/events/power.h +++ b/include/trace/events/power.h @@ -359,51 +359,50 @@ DEFINE_EVENT(power_domain, power_domain_target, ); /* - * The pm qos events are used for pm qos update + * CPU latency QoS events used for global CPU latency QoS list updates */ -DECLARE_EVENT_CLASS(pm_qos_request, +DECLARE_EVENT_CLASS(cpu_latency_qos_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value), + TP_ARGS(value), TP_STRUCT__entry( - __field( int, pm_qos_class ) __field( s32, value ) ), TP_fast_assign( - __entry->pm_qos_class = pm_qos_class; __entry->value = value; ), - TP_printk("pm_qos_class=%s value=%d", - __print_symbolic(__entry->pm_qos_class, - { PM_QOS_CPU_DMA_LATENCY, "CPU_DMA_LATENCY" }), + TP_printk("CPU_DMA_LATENCY value=%d", __entry->value) ); -DEFINE_EVENT(pm_qos_request, pm_qos_add_request, +DEFINE_EVENT(cpu_latency_qos_request, pm_qos_add_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value) + TP_ARGS(value) ); -DEFINE_EVENT(pm_qos_request, pm_qos_update_request, +DEFINE_EVENT(cpu_latency_qos_request, pm_qos_update_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value) + TP_ARGS(value) ); -DEFINE_EVENT(pm_qos_request, pm_qos_remove_request, +DEFINE_EVENT(cpu_latency_qos_request, pm_qos_remove_request, - TP_PROTO(int pm_qos_class, s32 value), + TP_PROTO(s32 value), - TP_ARGS(pm_qos_class, value) + TP_ARGS(value) ); +/* + * General PM QoS events used for updates of PM QoS request lists + */ DECLARE_EVENT_CLASS(pm_qos_update, TP_PROTO(enum pm_qos_req_action action, int prev_value, int curr_value), diff --git a/kernel/power/qos.c b/kernel/power/qos.c index a6bf53e9db17..afac7010e0f2 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -247,8 +247,8 @@ int pm_qos_request_active(struct pm_qos_request *req) } EXPORT_SYMBOL_GPL(pm_qos_request_active); -static void cpu_latency_qos_update(struct pm_qos_request *req, - enum pm_qos_req_action action, s32 value) +static void cpu_latency_qos_apply(struct pm_qos_request *req, + enum pm_qos_req_action action, s32 value) { int ret = pm_qos_update_target(req->qos, &req->node, action, value); if (ret > 0) @@ -278,10 +278,10 @@ void pm_qos_add_request(struct pm_qos_request *req, return; } - trace_pm_qos_add_request(PM_QOS_CPU_DMA_LATENCY, value); + trace_pm_qos_add_request(value); req->qos = &cpu_latency_constraints; - cpu_latency_qos_update(req, PM_QOS_ADD_REQ, value); + cpu_latency_qos_apply(req, PM_QOS_ADD_REQ, value); } EXPORT_SYMBOL_GPL(pm_qos_add_request); @@ -305,12 +305,12 @@ void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) return; } - trace_pm_qos_update_request(PM_QOS_CPU_DMA_LATENCY, new_value); + trace_pm_qos_update_request(new_value); if (new_value == req->node.prio) return; - cpu_latency_qos_update(req, PM_QOS_UPDATE_REQ, new_value); + cpu_latency_qos_apply(req, PM_QOS_UPDATE_REQ, new_value); } EXPORT_SYMBOL_GPL(pm_qos_update_request); @@ -333,9 +333,9 @@ void pm_qos_remove_request(struct pm_qos_request *req) return; } - trace_pm_qos_remove_request(PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + trace_pm_qos_remove_request(PM_QOS_DEFAULT_VALUE); - cpu_latency_qos_update(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } EXPORT_SYMBOL_GPL(pm_qos_remove_request); -- cgit v1.2.3 From e033b6c175a32870a2f7cd3741c0d48c858c2d04 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:07:01 +0100 Subject: PM: QoS: Adjust pm_qos_request() signature and reorder pm_qos.h Change the return type of pm_qos_request() to be the same as the one of pm_qos_read_value() called by it internally and stop exporting it to modules (because its only caller, cpuidle, is not modular). Also move the pm_qos_read_value() header away from the CPU latency QoS API function headers in pm_qos.h (because it technically does not belong to that API). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 6 +++--- kernel/power/qos.c | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index a3e0bfc6c470..3c4bee29ecda 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -137,20 +137,20 @@ static inline int dev_pm_qos_request_active(struct dev_pm_qos_request *req) return req->dev != NULL; } +s32 pm_qos_read_value(struct pm_qos_constraints *c); int pm_qos_update_target(struct pm_qos_constraints *c, struct plist_node *node, enum pm_qos_req_action action, int value); bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, enum pm_qos_req_action action, s32 val); + void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, s32 value); void pm_qos_update_request(struct pm_qos_request *req, s32 new_value); void pm_qos_remove_request(struct pm_qos_request *req); - -int pm_qos_request(int pm_qos_class); +s32 pm_qos_request(int pm_qos_class); int pm_qos_request_active(struct pm_qos_request *req); -s32 pm_qos_read_value(struct pm_qos_constraints *c); #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index afac7010e0f2..7bb55aca03bb 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -235,11 +235,10 @@ static struct pm_qos_constraints cpu_latency_constraints = { * * This function returns the current target value. */ -int pm_qos_request(int pm_qos_class) +s32 pm_qos_request(int pm_qos_class) { return pm_qos_read_value(&cpu_latency_constraints); } -EXPORT_SYMBOL_GPL(pm_qos_request); int pm_qos_request_active(struct pm_qos_request *req) { -- cgit v1.2.3 From fa048c59bf1ba13d8fe61890495577073a0ea919 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:07:42 +0100 Subject: PM: QoS: Add CPU latency QoS API wrappers Introduce (temporary) wrappers around pm_qos_request(), pm_qos_request_active() and pm_qos_add/update/remove_request() to provide replacements for them with function signatures that will be used in the final CPU latency QoS API, so that the users of it can be switched over to the new arrangement one by one before the API is finally set. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 3c4bee29ecda..63d39e66f95d 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -152,6 +152,33 @@ void pm_qos_remove_request(struct pm_qos_request *req); s32 pm_qos_request(int pm_qos_class); int pm_qos_request_active(struct pm_qos_request *req); +static inline void cpu_latency_qos_add_request(struct pm_qos_request *req, + s32 value) +{ + pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, value); +} + +static inline void cpu_latency_qos_update_request(struct pm_qos_request *req, + s32 new_value) +{ + pm_qos_update_request(req, new_value); +} + +static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) +{ + pm_qos_remove_request(req); +} + +static inline bool cpu_latency_qos_request_active(struct pm_qos_request *req) +{ + return pm_qos_request_active(req); +} + +static inline s32 cpu_latency_qos_limit(void) +{ + return pm_qos_request(PM_QOS_CPU_DMA_LATENCY); +} + #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask); -- cgit v1.2.3 From 306e59cc32a3cdcf5332e5ea5d6ad899629f3604 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 22 Jan 2020 11:04:49 +0100 Subject: dt-bindings: clk: meson: add the gxl internal dac gate Add the gxl ACODEC clock id to the gxbb clock controller bindings Acked-by: Neil Armstrong Signed-off-by: Jerome Brunet --- include/dt-bindings/clock/gxbb-clkc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h index db0763e96173..4073eb7a9da1 100644 --- a/include/dt-bindings/clock/gxbb-clkc.h +++ b/include/dt-bindings/clock/gxbb-clkc.h @@ -146,5 +146,6 @@ #define CLKID_CTS_VDAC 201 #define CLKID_HDMI_TX 202 #define CLKID_HDMI 205 +#define CLKID_ACODEC 206 #endif /* __GXBB_CLKC_H */ -- cgit v1.2.3 From 06b72824386795bf6f0a6ac0f0cfef6b7f0165c1 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Thu, 13 Feb 2020 16:51:53 +0100 Subject: ASoC: meson: aiu: add audio output dt-bindings Add the dt-bindings and documentation of the AIU audio controller. This component provides most of the audio outputs found on the Amlogic Gx SoC family. Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200213155159.3235792-4-jbrunet@baylibre.com Signed-off-by: Mark Brown --- .../devicetree/bindings/sound/amlogic,aiu.yaml | 111 +++++++++++++++++++++ include/dt-bindings/sound/meson-aiu.h | 18 ++++ 2 files changed, 129 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/amlogic,aiu.yaml create mode 100644 include/dt-bindings/sound/meson-aiu.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml new file mode 100644 index 000000000000..3ef7632dcb59 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,aiu.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,aiu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic AIU audio output controller + +maintainers: + - Jerome Brunet + +properties: + $nodename: + pattern: "^audio-controller@.*" + + "#sound-dai-cells": + const: 2 + + compatible: + items: + - enum: + - amlogic,aiu-gxbb + - amlogic,aiu-gxl + - const: + amlogic,aiu + + clocks: + items: + - description: AIU peripheral clock + - description: I2S peripheral clock + - description: I2S output clock + - description: I2S master clock + - description: I2S mixer clock + - description: SPDIF peripheral clock + - description: SPDIF output clock + - description: SPDIF master clock + - description: SPDIF master clock multiplexer + + clock-names: + items: + - const: pclk + - const: i2s_pclk + - const: i2s_aoclk + - const: i2s_mclk + - const: i2s_mixer + - const: spdif_pclk + - const: spdif_aoclk + - const: spdif_mclk + - const: spdif_mclk_sel + + interrupts: + items: + - description: I2S interrupt line + - description: SPDIF interrupt line + + interrupt-names: + items: + - const: i2s + - const: spdif + + reg: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - "#sound-dai-cells" + - compatible + - clocks + - clock-names + - interrupts + - interrupt-names + - reg + - resets + +examples: + - | + #include + #include + #include + #include + + aiu: audio-controller@5400 { + compatible = "amlogic,aiu-gxl", "amlogic,aiu"; + #sound-dai-cells = <2>; + reg = <0x0 0x5400 0x0 0x2ac>; + interrupts = , + ; + interrupt-names = "i2s", "spdif"; + clocks = <&clkc CLKID_AIU_GLUE>, + <&clkc CLKID_I2S_OUT>, + <&clkc CLKID_AOCLK_GATE>, + <&clkc CLKID_CTS_AMCLK>, + <&clkc CLKID_MIXER_IFACE>, + <&clkc CLKID_IEC958>, + <&clkc CLKID_IEC958_GATE>, + <&clkc CLKID_CTS_MCLK_I958>, + <&clkc CLKID_CTS_I958>; + clock-names = "pclk", + "i2s_pclk", + "i2s_aoclk", + "i2s_mclk", + "i2s_mixer", + "spdif_pclk", + "spdif_aoclk", + "spdif_mclk", + "spdif_mclk_sel"; + resets = <&reset RESET_AIU>; + }; + diff --git a/include/dt-bindings/sound/meson-aiu.h b/include/dt-bindings/sound/meson-aiu.h new file mode 100644 index 000000000000..1051b8af298b --- /dev/null +++ b/include/dt-bindings/sound/meson-aiu.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_MESON_AIU_H +#define __DT_MESON_AIU_H + +#define AIU_CPU 0 +#define AIU_HDMI 1 +#define AIU_ACODEC 2 + +#define CPU_I2S_FIFO 0 +#define CPU_SPDIF_FIFO 1 +#define CPU_I2S_ENCODER 2 +#define CPU_SPDIF_ENCODER 3 + +#define CTRL_I2S 0 +#define CTRL_PCM 1 +#define CTRL_OUT 2 + +#endif /* __DT_MESON_AIU_H */ -- cgit v1.2.3 From ac1cc6b4856ffa7ecb818b3ceb82bfc43597d613 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 11 Feb 2020 15:37:22 -0800 Subject: ACPI: button: move HIDs to acpi/button.h This makes it possible to use ACPI_BUTTON_HID_POWER in another driver. Signed-off-by: Josh Triplett Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 3 --- include/acpi/button.h | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index f6925f16c4a2..00112cf15322 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -30,17 +30,14 @@ #define ACPI_BUTTON_NOTIFY_STATUS 0x80 #define ACPI_BUTTON_SUBCLASS_POWER "power" -#define ACPI_BUTTON_HID_POWER "PNP0C0C" #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" #define ACPI_BUTTON_TYPE_POWER 0x01 #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" -#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" #define ACPI_BUTTON_TYPE_SLEEP 0x03 #define ACPI_BUTTON_SUBCLASS_LID "lid" -#define ACPI_BUTTON_HID_LID "PNP0C0D" #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" #define ACPI_BUTTON_TYPE_LID 0x05 diff --git a/include/acpi/button.h b/include/acpi/button.h index 340da7784cc8..af2fce5d2ee3 100644 --- a/include/acpi/button.h +++ b/include/acpi/button.h @@ -2,6 +2,10 @@ #ifndef ACPI_BUTTON_H #define ACPI_BUTTON_H +#define ACPI_BUTTON_HID_POWER "PNP0C0C" +#define ACPI_BUTTON_HID_LID "PNP0C0D" +#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" + #if IS_ENABLED(CONFIG_ACPI_BUTTON) extern int acpi_lid_open(void); #else -- cgit v1.2.3 From 7a8d3b90bdfe44d2e90523444ff82f0060d9560e Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 28 Jan 2020 05:28:41 +0000 Subject: clk: imx: imx8mq: use imx8m_clk_hw_composite_core Use imx8m_clk_hw_composite_core to simplify code. Add new definitions, and X_SRC/CG/DIV will be alias to the new definitions for backwards compatibility Reviewed-by: Abel Vesa Signed-off-by: Peng Fan Reviewed-by: Leonard Crestez Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mq.c | 34 ++++++++++++++++++-------------- include/dt-bindings/clock/imx8mq-clock.h | 7 ++++++- 2 files changed, 25 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 4c0edca1a6d0..ac9452cd9a82 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -403,22 +403,26 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) /* CORE */ hws[IMX8MQ_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mq_a53_sels, ARRAY_SIZE(imx8mq_a53_sels)); - hws[IMX8MQ_CLK_M4_SRC] = imx_clk_hw_mux2("arm_m4_src", base + 0x8080, 24, 3, imx8mq_arm_m4_sels, ARRAY_SIZE(imx8mq_arm_m4_sels)); - hws[IMX8MQ_CLK_VPU_SRC] = imx_clk_hw_mux2("vpu_src", base + 0x8100, 24, 3, imx8mq_vpu_sels, ARRAY_SIZE(imx8mq_vpu_sels)); - hws[IMX8MQ_CLK_GPU_CORE_SRC] = imx_clk_hw_mux2("gpu_core_src", base + 0x8180, 24, 3, imx8mq_gpu_core_sels, ARRAY_SIZE(imx8mq_gpu_core_sels)); - hws[IMX8MQ_CLK_GPU_SHADER_SRC] = imx_clk_hw_mux2("gpu_shader_src", base + 0x8200, 24, 3, imx8mq_gpu_shader_sels, ARRAY_SIZE(imx8mq_gpu_shader_sels)); - hws[IMX8MQ_CLK_A53_CG] = imx_clk_hw_gate3_flags("arm_a53_cg", "arm_a53_src", base + 0x8000, 28, CLK_IS_CRITICAL); - hws[IMX8MQ_CLK_M4_CG] = imx_clk_hw_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); - hws[IMX8MQ_CLK_VPU_CG] = imx_clk_hw_gate3("vpu_cg", "vpu_src", base + 0x8100, 28); - hws[IMX8MQ_CLK_GPU_CORE_CG] = imx_clk_hw_gate3("gpu_core_cg", "gpu_core_src", base + 0x8180, 28); - hws[IMX8MQ_CLK_GPU_SHADER_CG] = imx_clk_hw_gate3("gpu_shader_cg", "gpu_shader_src", base + 0x8200, 28); - hws[IMX8MQ_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3); - hws[IMX8MQ_CLK_M4_DIV] = imx_clk_hw_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); - hws[IMX8MQ_CLK_VPU_DIV] = imx_clk_hw_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3); - hws[IMX8MQ_CLK_GPU_CORE_DIV] = imx_clk_hw_divider2("gpu_core_div", "gpu_core_cg", base + 0x8180, 0, 3); - hws[IMX8MQ_CLK_GPU_SHADER_DIV] = imx_clk_hw_divider2("gpu_shader_div", "gpu_shader_cg", base + 0x8200, 0, 3); + + hws[IMX8MQ_CLK_M4_CORE] = imx8m_clk_hw_composite_core("arm_m4_core", imx8mq_arm_m4_sels, base + 0x8080); + hws[IMX8MQ_CLK_VPU_CORE] = imx8m_clk_hw_composite_core("vpu_core", imx8mq_vpu_sels, base + 0x8100); + hws[IMX8MQ_CLK_GPU_CORE] = imx8m_clk_hw_composite_core("gpu_core", imx8mq_gpu_core_sels, base + 0x8180); + hws[IMX8MQ_CLK_GPU_SHADER] = imx8m_clk_hw_composite("gpu_shader", imx8mq_gpu_shader_sels, base + 0x8200); + /* For backwards compatibility */ + hws[IMX8MQ_CLK_M4_SRC] = hws[IMX8MQ_CLK_M4_CORE]; + hws[IMX8MQ_CLK_M4_CG] = hws[IMX8MQ_CLK_M4_CORE]; + hws[IMX8MQ_CLK_M4_DIV] = hws[IMX8MQ_CLK_M4_CORE]; + hws[IMX8MQ_CLK_VPU_SRC] = hws[IMX8MQ_CLK_VPU_CORE]; + hws[IMX8MQ_CLK_VPU_CG] = hws[IMX8MQ_CLK_VPU_CORE]; + hws[IMX8MQ_CLK_VPU_DIV] = hws[IMX8MQ_CLK_VPU_CORE]; + hws[IMX8MQ_CLK_GPU_CORE_SRC] = hws[IMX8MQ_CLK_GPU_CORE]; + hws[IMX8MQ_CLK_GPU_CORE_CG] = hws[IMX8MQ_CLK_GPU_CORE]; + hws[IMX8MQ_CLK_GPU_CORE_DIV] = hws[IMX8MQ_CLK_GPU_CORE]; + hws[IMX8MQ_CLK_GPU_SHADER_SRC] = hws[IMX8MQ_CLK_GPU_SHADER]; + hws[IMX8MQ_CLK_GPU_SHADER_CG] = hws[IMX8MQ_CLK_GPU_SHADER]; + hws[IMX8MQ_CLK_GPU_SHADER_DIV] = hws[IMX8MQ_CLK_GPU_SHADER]; /* BUS */ hws[IMX8MQ_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mq_main_axi_sels, base + 0x8800); @@ -567,7 +571,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) hws[IMX8MQ_CLK_WDOG2_ROOT] = imx_clk_hw_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0); hws[IMX8MQ_CLK_WDOG3_ROOT] = imx_clk_hw_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0); hws[IMX8MQ_CLK_VPU_G1_ROOT] = imx_clk_hw_gate2_flags("vpu_g1_root_clk", "vpu_g1", base + 0x4560, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); - hws[IMX8MQ_CLK_GPU_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_core_div", base + 0x4570, 0); + hws[IMX8MQ_CLK_GPU_ROOT] = imx_clk_hw_gate4("gpu_root_clk", "gpu_core", base + 0x4570, 0); hws[IMX8MQ_CLK_VPU_G2_ROOT] = imx_clk_hw_gate2_flags("vpu_g2_root_clk", "vpu_g2", base + 0x45a0, 0, CLK_SET_RATE_PARENT | CLK_OPS_PARENT_ENABLE); hws[IMX8MQ_CLK_DISP_ROOT] = imx_clk_hw_gate2_shared2("disp_root_clk", "disp_dc8000", base + 0x45d0, 0, &share_count_dcss); hws[IMX8MQ_CLK_DISP_AXI_ROOT] = imx_clk_hw_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_dcss); diff --git a/include/dt-bindings/clock/imx8mq-clock.h b/include/dt-bindings/clock/imx8mq-clock.h index 3bab9b21c8d7..2b88723310bd 100644 --- a/include/dt-bindings/clock/imx8mq-clock.h +++ b/include/dt-bindings/clock/imx8mq-clock.h @@ -424,6 +424,11 @@ #define IMX8MQ_SYS2_PLL_500M_CG 283 #define IMX8MQ_SYS2_PLL_1000M_CG 284 -#define IMX8MQ_CLK_END 285 +#define IMX8MQ_CLK_GPU_CORE 285 +#define IMX8MQ_CLK_GPU_SHADER 286 +#define IMX8MQ_CLK_M4_CORE 287 +#define IMX8MQ_CLK_VPU_CORE 288 + +#define IMX8MQ_CLK_END 289 #endif /* __DT_BINDINGS_CLOCK_IMX8MQ_H */ -- cgit v1.2.3 From 811e4171d0f5ac6d1172333adf361c74ce2c8ec5 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 28 Jan 2020 05:28:46 +0000 Subject: clk: imx: imx8mm: use imx8m_clk_hw_composite_core Use imx8m_clk_hw_composite_core to simplify code. Add new definitions, and X_SRC/CG/DIV will be alias to the new definitions for backwards compatibility Reviewed-by: Abel Vesa Signed-off-by: Peng Fan Reviewed-by: Leonard Crestez Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mm.c | 35 +++++++++++++++++++------------- include/dt-bindings/clock/imx8mm-clock.h | 7 ++++++- 2 files changed, 27 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 2ed93fc25087..f79a5f5d9ed5 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -414,20 +414,27 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) /* Core Slice */ hws[IMX8MM_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mm_a53_sels, ARRAY_SIZE(imx8mm_a53_sels)); - hws[IMX8MM_CLK_M4_SRC] = imx_clk_hw_mux2("arm_m4_src", base + 0x8080, 24, 3, imx8mm_m4_sels, ARRAY_SIZE(imx8mm_m4_sels)); - hws[IMX8MM_CLK_VPU_SRC] = imx_clk_hw_mux2("vpu_src", base + 0x8100, 24, 3, imx8mm_vpu_sels, ARRAY_SIZE(imx8mm_vpu_sels)); - hws[IMX8MM_CLK_GPU3D_SRC] = imx_clk_hw_mux2("gpu3d_src", base + 0x8180, 24, 3, imx8mm_gpu3d_sels, ARRAY_SIZE(imx8mm_gpu3d_sels)); - hws[IMX8MM_CLK_GPU2D_SRC] = imx_clk_hw_mux2("gpu2d_src", base + 0x8200, 24, 3, imx8mm_gpu2d_sels, ARRAY_SIZE(imx8mm_gpu2d_sels)); hws[IMX8MM_CLK_A53_CG] = imx_clk_hw_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28); - hws[IMX8MM_CLK_M4_CG] = imx_clk_hw_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); - hws[IMX8MM_CLK_VPU_CG] = imx_clk_hw_gate3("vpu_cg", "vpu_src", base + 0x8100, 28); - hws[IMX8MM_CLK_GPU3D_CG] = imx_clk_hw_gate3("gpu3d_cg", "gpu3d_src", base + 0x8180, 28); - hws[IMX8MM_CLK_GPU2D_CG] = imx_clk_hw_gate3("gpu2d_cg", "gpu2d_src", base + 0x8200, 28); hws[IMX8MM_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3); - hws[IMX8MM_CLK_M4_DIV] = imx_clk_hw_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); - hws[IMX8MM_CLK_VPU_DIV] = imx_clk_hw_divider2("vpu_div", "vpu_cg", base + 0x8100, 0, 3); - hws[IMX8MM_CLK_GPU3D_DIV] = imx_clk_hw_divider2("gpu3d_div", "gpu3d_cg", base + 0x8180, 0, 3); - hws[IMX8MM_CLK_GPU2D_DIV] = imx_clk_hw_divider2("gpu2d_div", "gpu2d_cg", base + 0x8200, 0, 3); + + hws[IMX8MM_CLK_M4_CORE] = imx8m_clk_hw_composite_core("arm_m4_core", imx8mm_m4_sels, base + 0x8080); + hws[IMX8MM_CLK_VPU_CORE] = imx8m_clk_hw_composite_core("vpu_core", imx8mm_vpu_sels, base + 0x8100); + hws[IMX8MM_CLK_GPU3D_CORE] = imx8m_clk_hw_composite_core("gpu3d_core", imx8mm_gpu3d_sels, base + 0x8180); + hws[IMX8MM_CLK_GPU2D_CORE] = imx8m_clk_hw_composite_core("gpu2d_core", imx8mm_gpu2d_sels, base + 0x8200); + + /* For backwards compatibility */ + hws[IMX8MM_CLK_M4_SRC] = hws[IMX8MM_CLK_M4_CORE]; + hws[IMX8MM_CLK_M4_CG] = hws[IMX8MM_CLK_M4_CORE]; + hws[IMX8MM_CLK_M4_DIV] = hws[IMX8MM_CLK_M4_CORE]; + hws[IMX8MM_CLK_VPU_SRC] = hws[IMX8MM_CLK_VPU_CORE]; + hws[IMX8MM_CLK_VPU_CG] = hws[IMX8MM_CLK_VPU_CORE]; + hws[IMX8MM_CLK_VPU_DIV] = hws[IMX8MM_CLK_VPU_CORE]; + hws[IMX8MM_CLK_GPU3D_SRC] = hws[IMX8MM_CLK_GPU3D_CORE]; + hws[IMX8MM_CLK_GPU3D_CG] = hws[IMX8MM_CLK_GPU3D_CORE]; + hws[IMX8MM_CLK_GPU3D_DIV] = hws[IMX8MM_CLK_GPU3D_CORE]; + hws[IMX8MM_CLK_GPU2D_SRC] = hws[IMX8MM_CLK_GPU2D_CORE]; + hws[IMX8MM_CLK_GPU2D_CG] = hws[IMX8MM_CLK_GPU2D_CORE]; + hws[IMX8MM_CLK_GPU2D_DIV] = hws[IMX8MM_CLK_GPU2D_CORE]; /* BUS */ hws[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800); @@ -564,7 +571,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) hws[IMX8MM_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); hws[IMX8MM_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); hws[IMX8MM_CLK_USB1_CTRL_ROOT] = imx_clk_hw_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0); - hws[IMX8MM_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_div", base + 0x44f0, 0); + hws[IMX8MM_CLK_GPU3D_ROOT] = imx_clk_hw_gate4("gpu3d_root_clk", "gpu3d_core", base + 0x44f0, 0); hws[IMX8MM_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); hws[IMX8MM_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); hws[IMX8MM_CLK_WDOG1_ROOT] = imx_clk_hw_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); @@ -586,7 +593,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) hws[IMX8MM_CLK_SDMA1_ROOT] = imx_clk_hw_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0); hws[IMX8MM_CLK_SDMA2_ROOT] = imx_clk_hw_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0); hws[IMX8MM_CLK_SDMA3_ROOT] = imx_clk_hw_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0); - hws[IMX8MM_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_div", base + 0x4660, 0); + hws[IMX8MM_CLK_GPU2D_ROOT] = imx_clk_hw_gate4("gpu2d_root_clk", "gpu2d_core", base + 0x4660, 0); hws[IMX8MM_CLK_CSI1_ROOT] = imx_clk_hw_gate4("csi1_root_clk", "csi1_core", base + 0x4650, 0); hws[IMX8MM_CLK_GPT_3M] = imx_clk_hw_fixed_factor("gpt_3m", "osc_24m", 1, 8); diff --git a/include/dt-bindings/clock/imx8mm-clock.h b/include/dt-bindings/clock/imx8mm-clock.h index edeece2289f0..038c28d349e8 100644 --- a/include/dt-bindings/clock/imx8mm-clock.h +++ b/include/dt-bindings/clock/imx8mm-clock.h @@ -265,6 +265,11 @@ #define IMX8MM_SYS_PLL2_333M_CG 244 #define IMX8MM_SYS_PLL2_500M_CG 245 -#define IMX8MM_CLK_END 246 +#define IMX8MM_CLK_M4_CORE 246 +#define IMX8MM_CLK_VPU_CORE 247 +#define IMX8MM_CLK_GPU3D_CORE 248 +#define IMX8MM_CLK_GPU2D_CORE 249 + +#define IMX8MM_CLK_END 250 #endif -- cgit v1.2.3 From 33db2ce73e9affe9a25130b166048f5a209d4dc1 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Tue, 28 Jan 2020 05:28:50 +0000 Subject: clk: imx: imx8mn: use imx8m_clk_hw_composite_core Use imx8m_clk_hw_composite_core to simplify code. Add new definitions, and X_SRC/CG/DIV will be alias to the new definitions for backwards compatibility Reviewed-by: Abel Vesa Signed-off-by: Peng Fan Reviewed-by: Leonard Crestez Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mn.c | 19 +++++++++++-------- include/dt-bindings/clock/imx8mn-clock.h | 4 +++- 2 files changed, 14 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index a181eb2df876..7eea15a81838 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -413,15 +413,18 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) /* CORE */ hws[IMX8MN_CLK_A53_SRC] = imx_clk_hw_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mn_a53_sels, ARRAY_SIZE(imx8mn_a53_sels)); - hws[IMX8MN_CLK_GPU_CORE_SRC] = imx_clk_hw_mux2("gpu_core_src", base + 0x8180, 24, 3, imx8mn_gpu_core_sels, ARRAY_SIZE(imx8mn_gpu_core_sels)); - hws[IMX8MN_CLK_GPU_SHADER_SRC] = imx_clk_hw_mux2("gpu_shader_src", base + 0x8200, 24, 3, imx8mn_gpu_shader_sels, ARRAY_SIZE(imx8mn_gpu_shader_sels)); hws[IMX8MN_CLK_A53_CG] = imx_clk_hw_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28); - hws[IMX8MN_CLK_GPU_CORE_CG] = imx_clk_hw_gate3("gpu_core_cg", "gpu_core_src", base + 0x8180, 28); - hws[IMX8MN_CLK_GPU_SHADER_CG] = imx_clk_hw_gate3("gpu_shader_cg", "gpu_shader_src", base + 0x8200, 28); - hws[IMX8MN_CLK_A53_DIV] = imx_clk_hw_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3); - hws[IMX8MN_CLK_GPU_CORE_DIV] = imx_clk_hw_divider2("gpu_core_div", "gpu_core_cg", base + 0x8180, 0, 3); - hws[IMX8MN_CLK_GPU_SHADER_DIV] = imx_clk_hw_divider2("gpu_shader_div", "gpu_shader_cg", base + 0x8200, 0, 3); + + hws[IMX8MN_CLK_GPU_CORE] = imx8m_clk_hw_composite_core("gpu_core", imx8mn_gpu_core_sels, base + 0x8180); + hws[IMX8MN_CLK_GPU_SHADER] = imx8m_clk_hw_composite_core("gpu_shader", imx8mn_gpu_shader_sels, base + 0x8200); + + hws[IMX8MN_CLK_GPU_CORE_SRC] = hws[IMX8MN_CLK_GPU_CORE]; + hws[IMX8MN_CLK_GPU_CORE_CG] = hws[IMX8MN_CLK_GPU_CORE]; + hws[IMX8MN_CLK_GPU_CORE_DIV] = hws[IMX8MN_CLK_GPU_CORE]; + hws[IMX8MN_CLK_GPU_SHADER_SRC] = hws[IMX8MN_CLK_GPU_SHADER]; + hws[IMX8MN_CLK_GPU_SHADER_CG] = hws[IMX8MN_CLK_GPU_SHADER]; + hws[IMX8MN_CLK_GPU_SHADER_DIV] = hws[IMX8MN_CLK_GPU_SHADER]; /* BUS */ hws[IMX8MN_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mn_main_axi_sels, base + 0x8800); @@ -529,7 +532,7 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) hws[IMX8MN_CLK_UART3_ROOT] = imx_clk_hw_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); hws[IMX8MN_CLK_UART4_ROOT] = imx_clk_hw_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); hws[IMX8MN_CLK_USB1_CTRL_ROOT] = imx_clk_hw_gate4("usb1_ctrl_root_clk", "usb_bus", base + 0x44d0, 0); - hws[IMX8MN_CLK_GPU_CORE_ROOT] = imx_clk_hw_gate4("gpu_core_root_clk", "gpu_core_div", base + 0x44f0, 0); + hws[IMX8MN_CLK_GPU_CORE_ROOT] = imx_clk_hw_gate4("gpu_core_root_clk", "gpu_core", base + 0x44f0, 0); hws[IMX8MN_CLK_USDHC1_ROOT] = imx_clk_hw_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); hws[IMX8MN_CLK_USDHC2_ROOT] = imx_clk_hw_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); hws[IMX8MN_CLK_WDOG1_ROOT] = imx_clk_hw_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); diff --git a/include/dt-bindings/clock/imx8mn-clock.h b/include/dt-bindings/clock/imx8mn-clock.h index 43883fe65db5..c42a22d3cf7c 100644 --- a/include/dt-bindings/clock/imx8mn-clock.h +++ b/include/dt-bindings/clock/imx8mn-clock.h @@ -229,7 +229,9 @@ #define IMX8MN_SYS_PLL2_500M_CG 210 #define IMX8MN_CLK_SNVS_ROOT 211 +#define IMX8MN_CLK_GPU_CORE 212 +#define IMX8MN_CLK_GPU_SHADER 213 -#define IMX8MN_CLK_END 212 +#define IMX8MN_CLK_END 214 #endif -- cgit v1.2.3 From db6d9e9e8b48b7ab68c61553eb5fa68534dd0fde Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Tue, 17 Dec 2019 10:30:57 -0800 Subject: mac80211: Fix setting txpower to zero With multiple VIFS ath10k, and probably others, tries to find the minimum txpower for all vifs and uses that when setting txpower in the firmware. If a second vif is added and starts to scan, it's txpower is not initialized yet and it set to zero. ath10k had a patch to ignore zero values, but then it is impossible to actually set txpower to zero. So, instead initialize the txpower to INT_MIN in mac80211, and let drivers know that means the power has not been set and so should be ignored. This should fix regression in: commit 88407beb1b1462f706a1950a355fd086e1c450b6 Author: Ryan Hsu Date: Tue Dec 13 14:55:19 2016 -0800 ath10k: fix incorrect txpower set by P2P_DEVICE interface Tested on ath10k 9984 with ath10k-ct firmware. Signed-off-by: Ben Greear Link: https://lore.kernel.org/r/20191217183057.24586-1-greearb@candelatech.com Signed-off-by: Johannes Berg --- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- drivers/net/wireless/ath/ath9k/main.c | 3 +++ drivers/net/wireless/ath/ath9k/xmit.c | 7 +++++-- include/net/mac80211.h | 2 +- net/mac80211/iface.c | 1 + net/mac80211/main.c | 2 ++ 6 files changed, 14 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 7fee35ff966b..2f820d4eb308 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -5103,7 +5103,8 @@ static int ath10k_mac_txpower_recalc(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { - if (arvif->txpower <= 0) + /* txpower not initialized yet? */ + if (arvif->txpower == INT_MIN) continue; if (txpower == -1) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 0548aa3702e3..983b6d6c89f0 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1196,6 +1196,9 @@ static void ath9k_tpc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { int *power = data; + if (vif->bss_conf.txpower == INT_MIN) + return; + if (*power < vif->bss_conf.txpower) *power = vif->bss_conf.txpower; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 31e7b108279c..e60d4737fc6e 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2095,10 +2095,13 @@ static void setup_frame_info(struct ieee80211_hw *hw, if (tx_info->control.vif) { struct ieee80211_vif *vif = tx_info->control.vif; - + if (vif->bss_conf.txpower == INT_MIN) + goto nonvifpower; txpower = 2 * vif->bss_conf.txpower; } else { - struct ath_softc *sc = hw->priv; + struct ath_softc *sc; + nonvifpower: + sc = hw->priv; txpower = sc->cur_chan->cur_txpower; } diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 6d4ea71523d2..7287ad3034dd 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -574,7 +574,7 @@ struct ieee80211_ftm_responder_params { * @ssid: The SSID of the current vif. Valid in AP and IBSS mode. * @ssid_len: Length of SSID given in @ssid. * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. - * @txpower: TX power in dBm + * @txpower: TX power in dBm. INT_MIN means not configured. * @txpower_type: TX power adjustment used to control per packet Transmit * Power Control (TPC) in lower driver for the current vif. In particular * TPC is enabled if value passed in %txpower_type is diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2fb26bc105f8..3c00408e9c8c 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1465,6 +1465,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, sdata->control_port_no_encrypt = false; sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM; sdata->vif.bss_conf.idle = true; + sdata->vif.bss_conf.txpower = INT_MIN; /* unset */ sdata->noack_map = 0; sdata->hw_80211_encap = false; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 265a31a8104d..cae3a34d3503 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -146,6 +146,8 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) continue; if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) continue; + if (sdata->vif.bss_conf.txpower == INT_MIN) + continue; power = min(power, sdata->vif.bss_conf.txpower); } rcu_read_unlock(); -- cgit v1.2.3 From 256db7423c31c873abe6fb780513dd7b5705a510 Mon Sep 17 00:00:00 2001 From: Sergey Matyukevich Date: Thu, 13 Feb 2020 13:16:17 +0000 Subject: ieee80211: add WPA3 OWE AKM suite selector Add the definition for Opportunistic Wireless Encryption AKM selector. Signed-off-by: Sergey Matyukevich Link: https://lore.kernel.org/r/20200213131608.10541-3-sergey.matyukevich.os@quantenna.com Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 6f3e7c5c600a..33d907eec0b6 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -3044,6 +3044,7 @@ struct ieee80211_multiple_bssid_configuration { #define WLAN_AKM_SUITE_FILS_SHA384 SUITE(0x000FAC, 15) #define WLAN_AKM_SUITE_FT_FILS_SHA256 SUITE(0x000FAC, 16) #define WLAN_AKM_SUITE_FT_FILS_SHA384 SUITE(0x000FAC, 17) +#define WLAN_AKM_SUITE_OWE SUITE(0x000FAC, 18) #define WLAN_MAX_KEY_LEN 32 -- cgit v1.2.3 From 1f6e0baa703d31002c312c3e423c108b04325df0 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Tue, 11 Feb 2020 13:26:04 +0100 Subject: mac80211: allow setting queue_len for drivers not using wake_tx_queue Currently a mac80211 driver can only set the txq_limit when using wake_tx_queue. Not all drivers use wake_tx_queue. This patch adds a new element to wiphy allowing a driver to set a custom tx_queue_len and the code that will apply it in case it is set. The current default is 1000 which is too low for ath11k when doing HE rates. Signed-off-by: John Crispin Link: https://lore.kernel.org/r/20200211122605.13002-1-john@phrozen.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ net/mac80211/iface.c | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ec0de3c43c61..992bf0de6b9b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4739,6 +4739,8 @@ struct wiphy { u32 txq_memory_limit; u32 txq_quantum; + unsigned long tx_queue_len; + u8 support_mbssid:1, support_only_he_mbssid:1; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 3c00408e9c8c..128b3468d13e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1834,6 +1834,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, if_setup, txqs, 1); if (!ndev) return -ENOMEM; + + if (!local->ops->wake_tx_queue && local->hw.wiphy->tx_queue_len) + ndev->tx_queue_len = local->hw.wiphy->tx_queue_len; + dev_net_set(ndev, wiphy_net(local->hw.wiphy)); ndev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); -- cgit v1.2.3 From 67b06ba01857ed077e1a66bfa139156e7c68bab2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:35:04 +0100 Subject: PM: QoS: Drop PM_QOS_CPU_DMA_LATENCY and rename related functions Drop the PM QoS classes enum including PM_QOS_CPU_DMA_LATENCY, drop the wrappers around pm_qos_request(), pm_qos_request_active(), and pm_qos_add/update/remove_request() introduced previously, rename these functions, respectively, to cpu_latency_qos_limit(), cpu_latency_qos_request_active(), and cpu_latency_qos_add/update/remove_request(), and update their kerneldoc comments. [While at it, drop some useless comments from these functions.] No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 47 +++--------------------- kernel/power/qos.c | 98 +++++++++++++++++++++++++------------------------- 2 files changed, 54 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 63d39e66f95d..e0ca4d780457 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -9,14 +9,6 @@ #include #include -enum { - PM_QOS_RESERVED = 0, - PM_QOS_CPU_DMA_LATENCY, - - /* insert new class ID */ - PM_QOS_NUM_CLASSES, -}; - enum pm_qos_flags_status { PM_QOS_FLAGS_UNDEFINED = -1, PM_QOS_FLAGS_NONE, @@ -144,40 +136,11 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, enum pm_qos_req_action action, s32 val); -void pm_qos_add_request(struct pm_qos_request *req, int pm_qos_class, - s32 value); -void pm_qos_update_request(struct pm_qos_request *req, - s32 new_value); -void pm_qos_remove_request(struct pm_qos_request *req); -s32 pm_qos_request(int pm_qos_class); -int pm_qos_request_active(struct pm_qos_request *req); - -static inline void cpu_latency_qos_add_request(struct pm_qos_request *req, - s32 value) -{ - pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, value); -} - -static inline void cpu_latency_qos_update_request(struct pm_qos_request *req, - s32 new_value) -{ - pm_qos_update_request(req, new_value); -} - -static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) -{ - pm_qos_remove_request(req); -} - -static inline bool cpu_latency_qos_request_active(struct pm_qos_request *req) -{ - return pm_qos_request_active(req); -} - -static inline s32 cpu_latency_qos_limit(void) -{ - return pm_qos_request(PM_QOS_CPU_DMA_LATENCY); -} +s32 cpu_latency_qos_limit(void); +bool cpu_latency_qos_request_active(struct pm_qos_request *req); +void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value); +void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value); +void cpu_latency_qos_remove_request(struct pm_qos_request *req); #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 7bb55aca03bb..7374c76f409a 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -230,21 +230,25 @@ static struct pm_qos_constraints cpu_latency_constraints = { }; /** - * pm_qos_request - returns current system wide qos expectation - * @pm_qos_class: Ignored. - * - * This function returns the current target value. + * cpu_latency_qos_limit - Return current system-wide CPU latency QoS limit. */ -s32 pm_qos_request(int pm_qos_class) +s32 cpu_latency_qos_limit(void) { return pm_qos_read_value(&cpu_latency_constraints); } -int pm_qos_request_active(struct pm_qos_request *req) +/** + * cpu_latency_qos_request_active - Check the given PM QoS request. + * @req: PM QoS request to check. + * + * Return: 'true' if @req has been added to the CPU latency QoS list, 'false' + * otherwise. + */ +bool cpu_latency_qos_request_active(struct pm_qos_request *req) { return req->qos == &cpu_latency_constraints; } -EXPORT_SYMBOL_GPL(pm_qos_request_active); +EXPORT_SYMBOL_GPL(cpu_latency_qos_request_active); static void cpu_latency_qos_apply(struct pm_qos_request *req, enum pm_qos_req_action action, s32 value) @@ -255,25 +259,24 @@ static void cpu_latency_qos_apply(struct pm_qos_request *req, } /** - * pm_qos_add_request - inserts new qos request into the list - * @req: pointer to a preallocated handle - * @pm_qos_class: Ignored. - * @value: defines the qos request + * cpu_latency_qos_add_request - Add new CPU latency QoS request. + * @req: Pointer to a preallocated handle. + * @value: Requested constraint value. + * + * Use @value to initialize the request handle pointed to by @req, insert it as + * a new entry to the CPU latency QoS list and recompute the effective QoS + * constraint for that list. * - * This function inserts a new entry in the PM_QOS_CPU_DMA_LATENCY list of - * requested QoS performance characteristics. It recomputes the aggregate QoS - * expectations for the PM_QOS_CPU_DMA_LATENCY list and initializes the @req - * handle. Caller needs to save this handle for later use in updates and - * removal. + * Callers need to save the handle for later use in updates and removal of the + * QoS request represented by it. */ -void pm_qos_add_request(struct pm_qos_request *req, - int pm_qos_class, s32 value) +void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value) { - if (!req) /*guard against callers passing in null */ + if (!req) return; - if (pm_qos_request_active(req)) { - WARN(1, KERN_ERR "pm_qos_add_request() called for already added request\n"); + if (cpu_latency_qos_request_active(req)) { + WARN(1, KERN_ERR "%s called for already added request\n", __func__); return; } @@ -282,25 +285,24 @@ void pm_qos_add_request(struct pm_qos_request *req, req->qos = &cpu_latency_constraints; cpu_latency_qos_apply(req, PM_QOS_ADD_REQ, value); } -EXPORT_SYMBOL_GPL(pm_qos_add_request); +EXPORT_SYMBOL_GPL(cpu_latency_qos_add_request); /** - * pm_qos_update_request - modifies an existing qos request - * @req : handle to list element holding a pm_qos request to use - * @value: defines the qos request - * - * Updates an existing qos request for the PM_QOS_CPU_DMA_LATENCY list along - * with updating the target PM_QOS_CPU_DMA_LATENCY value. + * cpu_latency_qos_update_request - Modify existing CPU latency QoS request. + * @req : QoS request to update. + * @new_value: New requested constraint value. * - * Attempts are made to make this code callable on hot code paths. + * Use @new_value to update the QoS request represented by @req in the CPU + * latency QoS list along with updating the effective constraint value for that + * list. */ -void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) +void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value) { - if (!req) /*guard against callers passing in null */ + if (!req) return; - if (!pm_qos_request_active(req)) { - WARN(1, KERN_ERR "pm_qos_update_request() called for unknown object\n"); + if (!cpu_latency_qos_request_active(req)) { + WARN(1, KERN_ERR "%s called for unknown object\n", __func__); return; } @@ -311,24 +313,22 @@ void pm_qos_update_request(struct pm_qos_request *req, s32 new_value) cpu_latency_qos_apply(req, PM_QOS_UPDATE_REQ, new_value); } -EXPORT_SYMBOL_GPL(pm_qos_update_request); +EXPORT_SYMBOL_GPL(cpu_latency_qos_update_request); /** - * pm_qos_remove_request - modifies an existing qos request - * @req: handle to request list element + * cpu_latency_qos_remove_request - Remove existing CPU latency QoS request. + * @req: QoS request to remove. * - * Will remove pm qos request from the list of constraints and - * recompute the current target value for PM_QOS_CPU_DMA_LATENCY. Call this - * on slow code paths. + * Remove the CPU latency QoS request represented by @req from the CPU latency + * QoS list along with updating the effective constraint value for that list. */ -void pm_qos_remove_request(struct pm_qos_request *req) +void cpu_latency_qos_remove_request(struct pm_qos_request *req) { - if (!req) /*guard against callers passing in null */ + if (!req) return; - /* silent return to keep pcm code cleaner */ - if (!pm_qos_request_active(req)) { - WARN(1, KERN_ERR "pm_qos_remove_request() called for unknown object\n"); + if (!cpu_latency_qos_request_active(req)) { + WARN(1, KERN_ERR "%s called for unknown object\n", __func__); return; } @@ -337,7 +337,7 @@ void pm_qos_remove_request(struct pm_qos_request *req) cpu_latency_qos_apply(req, PM_QOS_REMOVE_REQ, PM_QOS_DEFAULT_VALUE); memset(req, 0, sizeof(*req)); } -EXPORT_SYMBOL_GPL(pm_qos_remove_request); +EXPORT_SYMBOL_GPL(cpu_latency_qos_remove_request); /* User space interface to the CPU latency QoS via misc device. */ @@ -349,7 +349,7 @@ static int cpu_latency_qos_open(struct inode *inode, struct file *filp) if (!req) return -ENOMEM; - pm_qos_add_request(req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); + cpu_latency_qos_add_request(req, PM_QOS_DEFAULT_VALUE); filp->private_data = req; return 0; @@ -361,7 +361,7 @@ static int cpu_latency_qos_release(struct inode *inode, struct file *filp) filp->private_data = NULL; - pm_qos_remove_request(req); + cpu_latency_qos_remove_request(req); kfree(req); return 0; @@ -374,7 +374,7 @@ static ssize_t cpu_latency_qos_read(struct file *filp, char __user *buf, unsigned long flags; s32 value; - if (!req || !pm_qos_request_active(req)) + if (!req || !cpu_latency_qos_request_active(req)) return -EINVAL; spin_lock_irqsave(&pm_qos_lock, flags); @@ -400,7 +400,7 @@ static ssize_t cpu_latency_qos_write(struct file *filp, const char __user *buf, return ret; } - pm_qos_update_request(filp->private_data, value); + cpu_latency_qos_update_request(filp->private_data, value); return count; } -- cgit v1.2.3 From fe52de36dc5d60960230cabec88c357113d9c32a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:35:39 +0100 Subject: PM: QoS: Update file information comments Update the file information comments in include/linux/pm_qos.h and kernel/power/qos.c by adding titles along with copyright and authors information to them and changing the qos.c description to better reflect its contents (outdated information is dropped from it in particular). Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 15 +++++++++++---- kernel/power/qos.c | 34 ++++++++++++---------------------- 2 files changed, 23 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index e0ca4d780457..df065db3f57a 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -1,10 +1,17 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _LINUX_PM_QOS_H -#define _LINUX_PM_QOS_H -/* interface for the pm_qos_power infrastructure of the linux kernel. +/* + * Definitions related to Power Management Quality of Service (PM QoS). + * + * Copyright (C) 2020 Intel Corporation * - * Mark Gross + * Authors: + * Mark Gross + * Rafael J. Wysocki */ + +#ifndef _LINUX_PM_QOS_H +#define _LINUX_PM_QOS_H + #include #include #include diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 7374c76f409a..ef73573db43d 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -1,31 +1,21 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * This module exposes the interface to kernel space for specifying - * QoS dependencies. It provides infrastructure for registration of: + * Power Management Quality of Service (PM QoS) support base. * - * Dependents on a QoS value : register requests - * Watchers of QoS value : get notified when target QoS value changes + * Copyright (C) 2020 Intel Corporation * - * This QoS design is best effort based. Dependents register their QoS needs. - * Watchers register to keep track of the current QoS needs of the system. + * Authors: + * Mark Gross + * Rafael J. Wysocki * - * There are 3 basic classes of QoS parameter: latency, timeout, throughput - * each have defined units: - * latency: usec - * timeout: usec <-- currently not used. - * throughput: kbs (kilo byte / sec) + * Provided here is an interface for specifying PM QoS dependencies. It allows + * entities depending on QoS constraints to register their requests which are + * aggregated as appropriate to produce effective constraints (target values) + * that can be monitored by entities needing to respect them, either by polling + * or through a built-in notification mechanism. * - * There are lists of pm_qos_objects each one wrapping requests, notifiers - * - * User mode requests on a QOS parameter register themselves to the - * subsystem by opening the device node /dev/... and writing there request to - * the node. As long as the process holds a file handle open to the node the - * client continues to be accounted for. Upon file release the usermode - * request is removed and a new qos target is computed. This way when the - * request that the application has is cleaned up when closes the file - * pointer or exits the pm_qos_object will get an opportunity to clean up. - * - * Mark Gross + * In addition to the basic functionality, more specific interfaces for managing + * global CPU latency QoS requests and frequency QoS requests are provided. */ /*#define DEBUG*/ -- cgit v1.2.3 From 814d51f8889bc4afa75f647eeffd5ff0a5620e8d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Feb 2020 00:37:11 +0100 Subject: PM: QoS: Make CPU latency QoS depend on CONFIG_CPU_IDLE Because cpuidle is the only user of the effective constraint coming from the CPU latency QoS, add #ifdef CONFIG_CPU_IDLE around that code to avoid building it unnecessarily. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson Reviewed-by: Amit Kucheria Tested-by: Amit Kucheria --- include/linux/pm_qos.h | 13 +++++++++++++ kernel/power/qos.c | 2 ++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index df065db3f57a..4a69d4af3ff8 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -143,11 +143,24 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, struct pm_qos_flags_request *req, enum pm_qos_req_action action, s32 val); +#ifdef CONFIG_CPU_IDLE s32 cpu_latency_qos_limit(void); bool cpu_latency_qos_request_active(struct pm_qos_request *req); void cpu_latency_qos_add_request(struct pm_qos_request *req, s32 value); void cpu_latency_qos_update_request(struct pm_qos_request *req, s32 new_value); void cpu_latency_qos_remove_request(struct pm_qos_request *req); +#else +static inline s32 cpu_latency_qos_limit(void) { return INT_MAX; } +static inline bool cpu_latency_qos_request_active(struct pm_qos_request *req) +{ + return false; +} +static inline void cpu_latency_qos_add_request(struct pm_qos_request *req, + s32 value) {} +static inline void cpu_latency_qos_update_request(struct pm_qos_request *req, + s32 new_value) {} +static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) {} +#endif #ifdef CONFIG_PM enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask); diff --git a/kernel/power/qos.c b/kernel/power/qos.c index ef73573db43d..32927682bcc4 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -209,6 +209,7 @@ bool pm_qos_update_flags(struct pm_qos_flags *pqf, return prev_value != curr_value; } +#ifdef CONFIG_CPU_IDLE /* Definitions related to the CPU latency QoS. */ static struct pm_qos_constraints cpu_latency_constraints = { @@ -421,6 +422,7 @@ static int __init cpu_latency_qos_init(void) return ret; } late_initcall(cpu_latency_qos_init); +#endif /* CONFIG_CPU_IDLE */ /* Definitions related to the frequency QoS below. */ -- cgit v1.2.3 From e92a4eb490cb445fd644cc9a344db3f01b866d29 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 11 Feb 2020 19:15:14 +0100 Subject: drivers base/arch_topology: Remove 'struct sched_domain' forward declaration The sched domain pointer argument from topology_get_freq_scale() and topology_get_cpu_scale() got removed by commit 7673c8a4c75d ("sched/cpufreq: Remove arch_scale_freq_capacity()'s 'sd' parameter") and commit 8ec59c0f5f49 ("sched/topology: Remove unused 'sd' parameter from arch_scale_cpu_capacity()"). So the 'struct sched_domain' forward declaration is no longer needed. Remove it. Signed-off-by: Dietmar Eggemann Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20200211181515.32570-2-dietmar.eggemann@arm.com Signed-off-by: Greg Kroah-Hartman --- include/linux/arch_topology.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 3015ecbb90b1..f4b1d4fd7efb 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -16,7 +16,6 @@ bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); DECLARE_PER_CPU(unsigned long, cpu_scale); -struct sched_domain; static inline unsigned long topology_get_cpu_scale(int cpu) { -- cgit v1.2.3 From 99c73ce158a44a1a11cb146062bc32df0c1d95db Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 11 Feb 2020 19:15:15 +0100 Subject: drivers base/arch_topology: Reformat topology_get_[cpu/freq]_scale() function name The storage class and inline definition as well as the return type, function name and parameter list fit all into one line. Signed-off-by: Dietmar Eggemann Reviewed-by: Sudeep Holla Link: https://lore.kernel.org/r/20200211181515.32570-3-dietmar.eggemann@arm.com Signed-off-by: Greg Kroah-Hartman --- include/linux/arch_topology.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index f4b1d4fd7efb..c507e9ddd909 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -16,8 +16,7 @@ bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); DECLARE_PER_CPU(unsigned long, cpu_scale); -static inline -unsigned long topology_get_cpu_scale(int cpu) +static inline unsigned long topology_get_cpu_scale(int cpu) { return per_cpu(cpu_scale, cpu); } @@ -26,8 +25,7 @@ void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); DECLARE_PER_CPU(unsigned long, freq_scale); -static inline -unsigned long topology_get_freq_scale(int cpu) +static inline unsigned long topology_get_freq_scale(int cpu) { return per_cpu(freq_scale, cpu); } -- cgit v1.2.3 From eab2404ba798a8efda2a970f44071c3406d94e57 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Fri, 14 Feb 2020 10:08:57 -0800 Subject: Bluetooth: Add BT_PHY socket option This adds BT_PHY socket option (read-only) which can be used to read the PHYs in use by the underline connection. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 17 ++++++ include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_conn.c | 107 ++++++++++++++++++++++++++++++++++++++ net/bluetooth/l2cap_sock.c | 13 +++++ net/bluetooth/sco.c | 13 +++++ 5 files changed, 152 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index e42bb8e03c09..1576353a2773 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -121,6 +121,23 @@ struct bt_voice { #define BT_SNDMTU 12 #define BT_RCVMTU 13 +#define BT_PHY 14 + +#define BT_PHY_BR_1M_1SLOT 0x00000001 +#define BT_PHY_BR_1M_3SLOT 0x00000002 +#define BT_PHY_BR_1M_5SLOT 0x00000004 +#define BT_PHY_EDR_2M_1SLOT 0x00000008 +#define BT_PHY_EDR_2M_3SLOT 0x00000010 +#define BT_PHY_EDR_2M_5SLOT 0x00000020 +#define BT_PHY_EDR_3M_1SLOT 0x00000040 +#define BT_PHY_EDR_3M_3SLOT 0x00000080 +#define BT_PHY_EDR_3M_5SLOT 0x00000100 +#define BT_PHY_LE_1M_TX 0x00000200 +#define BT_PHY_LE_1M_RX 0x00000400 +#define BT_PHY_LE_2M_TX 0x00000800 +#define BT_PHY_LE_2M_RX 0x00001000 +#define BT_PHY_LE_CODED_TX 0x00002000 +#define BT_PHY_LE_CODED_RX 0x00004000 __printf(1, 2) void bt_info(const char *fmt, ...); diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 89ecf0a80aa1..dcc0dc6e2624 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1477,6 +1477,8 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param, u32 timeout); +u32 hci_conn_get_phy(struct hci_conn *conn); + /* ----- HCI Sockets ----- */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb); void hci_send_to_channel(unsigned short channel, struct sk_buff *skb, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 87691404d0c6..65fa44cbe514 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -1725,3 +1725,110 @@ struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle) return hchan; } + +u32 hci_conn_get_phy(struct hci_conn *conn) +{ + u32 phys = 0; + + hci_dev_lock(conn->hdev); + + /* BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 2, Part B page 471: + * Table 6.2: Packets defined for synchronous, asynchronous, and + * CSB logical transport types. + */ + switch (conn->type) { + case SCO_LINK: + /* SCO logical transport (1 Mb/s): + * HV1, HV2, HV3 and DV. + */ + phys |= BT_PHY_BR_1M_1SLOT; + + break; + + case ACL_LINK: + /* ACL logical transport (1 Mb/s) ptt=0: + * DH1, DM3, DH3, DM5 and DH5. + */ + phys |= BT_PHY_BR_1M_1SLOT; + + if (conn->pkt_type & (HCI_DM3 | HCI_DH3)) + phys |= BT_PHY_BR_1M_3SLOT; + + if (conn->pkt_type & (HCI_DM5 | HCI_DH5)) + phys |= BT_PHY_BR_1M_5SLOT; + + /* ACL logical transport (2 Mb/s) ptt=1: + * 2-DH1, 2-DH3 and 2-DH5. + */ + if (!(conn->pkt_type & HCI_2DH1)) + phys |= BT_PHY_EDR_2M_1SLOT; + + if (!(conn->pkt_type & HCI_2DH3)) + phys |= BT_PHY_EDR_2M_3SLOT; + + if (!(conn->pkt_type & HCI_2DH5)) + phys |= BT_PHY_EDR_2M_5SLOT; + + /* ACL logical transport (3 Mb/s) ptt=1: + * 3-DH1, 3-DH3 and 3-DH5. + */ + if (!(conn->pkt_type & HCI_3DH1)) + phys |= BT_PHY_EDR_3M_1SLOT; + + if (!(conn->pkt_type & HCI_3DH3)) + phys |= BT_PHY_EDR_3M_3SLOT; + + if (!(conn->pkt_type & HCI_3DH5)) + phys |= BT_PHY_EDR_3M_5SLOT; + + break; + + case ESCO_LINK: + /* eSCO logical transport (1 Mb/s): EV3, EV4 and EV5 */ + phys |= BT_PHY_BR_1M_1SLOT; + + if (!(conn->pkt_type & (ESCO_EV4 | ESCO_EV5))) + phys |= BT_PHY_BR_1M_3SLOT; + + /* eSCO logical transport (2 Mb/s): 2-EV3, 2-EV5 */ + if (!(conn->pkt_type & ESCO_2EV3)) + phys |= BT_PHY_EDR_2M_1SLOT; + + if (!(conn->pkt_type & ESCO_2EV5)) + phys |= BT_PHY_EDR_2M_3SLOT; + + /* eSCO logical transport (3 Mb/s): 3-EV3, 3-EV5 */ + if (!(conn->pkt_type & ESCO_3EV3)) + phys |= BT_PHY_EDR_3M_1SLOT; + + if (!(conn->pkt_type & ESCO_3EV5)) + phys |= BT_PHY_EDR_3M_3SLOT; + + break; + + case LE_LINK: + if (conn->le_tx_phy & HCI_LE_SET_PHY_1M) + phys |= BT_PHY_LE_1M_TX; + + if (conn->le_rx_phy & HCI_LE_SET_PHY_1M) + phys |= BT_PHY_LE_1M_RX; + + if (conn->le_tx_phy & HCI_LE_SET_PHY_2M) + phys |= BT_PHY_LE_2M_TX; + + if (conn->le_rx_phy & HCI_LE_SET_PHY_2M) + phys |= BT_PHY_LE_2M_RX; + + if (conn->le_tx_phy & HCI_LE_SET_PHY_CODED) + phys |= BT_PHY_LE_CODED_TX; + + if (conn->le_rx_phy & HCI_LE_SET_PHY_CODED) + phys |= BT_PHY_LE_CODED_RX; + + break; + } + + hci_dev_unlock(conn->hdev); + + return phys; +} diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 390a9afab647..9fb47b2b13c9 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -499,6 +499,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, struct l2cap_chan *chan = l2cap_pi(sk)->chan; struct bt_security sec; struct bt_power pwr; + u32 phys; int len, err = 0; BT_DBG("sk %p", sk); @@ -603,6 +604,18 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, err = -EFAULT; break; + case BT_PHY: + if (sk->sk_state == BT_CONNECTED) { + err = -ENOTCONN; + break; + } + + phys = hci_conn_get_phy(chan->conn->hcon); + + if (put_user(phys, (u32 __user *) optval)) + err = -EFAULT; + break; + default: err = -ENOPROTOOPT; break; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index b91d6b440fdf..29ab3e12fb46 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -922,6 +922,7 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, struct sock *sk = sock->sk; int len, err = 0; struct bt_voice voice; + u32 phys; BT_DBG("sk %p", sk); @@ -956,6 +957,18 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, break; + case BT_PHY: + if (sk->sk_state == BT_CONNECTED) { + err = -ENOTCONN; + break; + } + + phys = hci_conn_get_phy(sco_pi(sk)->conn->hcon); + + if (put_user(phys, (u32 __user *) optval)) + err = -EFAULT; + break; + default: err = -ENOPROTOOPT; break; -- cgit v1.2.3 From fe98d0ff5d5c43ee179e801275bb37641d398c6e Mon Sep 17 00:00:00 2001 From: Jianxin Pan Date: Wed, 15 Jan 2020 19:30:28 +0800 Subject: firmware: meson_sm: Add secure power domain support The Amlogic Meson A1/C1 Secure Monitor implements calls to control power domain. Signed-off-by: Jianxin Pan Signed-off-by: Kevin Hilman Link: https://lore.kernel.org/r/1579087831-94965-2-git-send-email-jianxin.pan@amlogic.com --- drivers/firmware/meson/meson_sm.c | 2 ++ include/linux/firmware/meson/meson_sm.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 1d5b4d74f96d..2854b56f6e0b 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -44,6 +44,8 @@ static const struct meson_sm_chip gxbb_chip = { CMD(SM_EFUSE_WRITE, 0x82000031), CMD(SM_EFUSE_USER_MAX, 0x82000033), CMD(SM_GET_CHIP_ID, 0x82000044), + CMD(SM_A1_PWRC_SET, 0x82000093), + CMD(SM_A1_PWRC_GET, 0x82000095), { /* sentinel */ }, }, }; diff --git a/include/linux/firmware/meson/meson_sm.h b/include/linux/firmware/meson/meson_sm.h index 6669e2a1d5fd..95b0da2326a9 100644 --- a/include/linux/firmware/meson/meson_sm.h +++ b/include/linux/firmware/meson/meson_sm.h @@ -12,6 +12,8 @@ enum { SM_EFUSE_WRITE, SM_EFUSE_USER_MAX, SM_GET_CHIP_ID, + SM_A1_PWRC_SET, + SM_A1_PWRC_GET, }; struct meson_sm_firmware; -- cgit v1.2.3 From 165b5fb294e878f00015b7beb91cb00e36e4f8b8 Mon Sep 17 00:00:00 2001 From: Jianxin Pan Date: Wed, 15 Jan 2020 19:30:29 +0800 Subject: dt-bindings: power: add Amlogic secure power domains bindings Add the bindings for the Amlogic Secure power domains, controlling the secure power domains. The bindings targets the Amlogic A1 and C1 compatible SoCs, in which the power domain registers are in secure world. Signed-off-by: Jianxin Pan Signed-off-by: Kevin Hilman Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1579087831-94965-3-git-send-email-jianxin.pan@amlogic.com --- .../bindings/power/amlogic,meson-sec-pwrc.yaml | 40 ++++++++++++++++++++++ include/dt-bindings/power/meson-a1-power.h | 32 +++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml create mode 100644 include/dt-bindings/power/meson-a1-power.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml new file mode 100644 index 000000000000..af32209218bb --- /dev/null +++ b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0+ OR MIT) +# Copyright (c) 2019 Amlogic, Inc +# Author: Jianxin Pan +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/power/amlogic,meson-sec-pwrc.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Amlogic Meson Secure Power Domains + +maintainers: + - Jianxin Pan + +description: |+ + Secure Power Domains used in Meson A1/C1 SoCs, and should be the child node + of secure-monitor. + +properties: + compatible: + enum: + - amlogic,meson-a1-pwrc + + "#power-domain-cells": + const: 1 + +required: + - compatible + - "#power-domain-cells" + +examples: + - | + secure-monitor { + compatible = "amlogic,meson-gxbb-sm"; + + pwrc: power-controller { + compatible = "amlogic,meson-a1-pwrc"; + #power-domain-cells = <1>; + }; + } + diff --git a/include/dt-bindings/power/meson-a1-power.h b/include/dt-bindings/power/meson-a1-power.h new file mode 100644 index 000000000000..6cf50bfb8ccf --- /dev/null +++ b/include/dt-bindings/power/meson-a1-power.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: (GPL-2.0+ or MIT) */ +/* + * Copyright (c) 2019 Amlogic, Inc. + * Author: Jianxin Pan + */ + +#ifndef _DT_BINDINGS_MESON_A1_POWER_H +#define _DT_BINDINGS_MESON_A1_POWER_H + +#define PWRC_DSPA_ID 8 +#define PWRC_DSPB_ID 9 +#define PWRC_UART_ID 10 +#define PWRC_DMC_ID 11 +#define PWRC_I2C_ID 12 +#define PWRC_PSRAM_ID 13 +#define PWRC_ACODEC_ID 14 +#define PWRC_AUDIO_ID 15 +#define PWRC_OTP_ID 16 +#define PWRC_DMA_ID 17 +#define PWRC_SD_EMMC_ID 18 +#define PWRC_RAMA_ID 19 +#define PWRC_RAMB_ID 20 +#define PWRC_IR_ID 21 +#define PWRC_SPICC_ID 22 +#define PWRC_SPIFC_ID 23 +#define PWRC_USB_ID 24 +#define PWRC_NIC_ID 25 +#define PWRC_PDMIN_ID 26 +#define PWRC_RSA_ID 27 +#define PWRC_MAX_ID 28 + +#endif -- cgit v1.2.3 From c67f3df88ffca45531a12214e8faffbdab1fa422 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 13 Feb 2020 17:20:20 -0600 Subject: of: Drop struct of_pci_range.pci_space field There's no more users of struct of_pci_range.pci_space field, so remove it. Signed-off-by: Rob Herring --- drivers/of/address.c | 1 - include/linux/of_address.h | 1 - 2 files changed, 2 deletions(-) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index 846045a48395..5d608d7c10d6 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -736,7 +736,6 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, if (!parser->range || parser->range + parser->np > parser->end) return NULL; - range->pci_space = be32_to_cpup(parser->range); range->flags = of_bus_pci_get_flags(parser->range); range->pci_addr = of_read_number(parser->range + 1, ns); if (parser->dma) diff --git a/include/linux/of_address.h b/include/linux/of_address.h index eac7ab109df4..8d12bf18e80b 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -16,7 +16,6 @@ struct of_pci_range_parser { }; struct of_pci_range { - u32 pci_space; u64 pci_addr; u64 cpu_addr; u64 size; -- cgit v1.2.3 From bc5e522ec47174770a75df0a76d90f9ebb20132e Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 6 Feb 2020 14:01:05 +0000 Subject: of/address: Rework of_pci_range parsing for non-PCI buses The only PCI specific part of of_pci_range_parser_one() is the handling of the 3rd address cell. Rework it to work on regular 1 and 2 cell addresses. Use defines and a union to avoid a treewide renaming of the parsing helpers and struct. Signed-off-by: Rob Herring --- drivers/of/address.c | 33 +++++++++++++++++++++------------ include/linux/of_address.h | 12 +++++++++--- 2 files changed, 30 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index 5d608d7c10d6..6d33f849f114 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -694,12 +694,12 @@ EXPORT_SYMBOL(of_get_address); static int parser_init(struct of_pci_range_parser *parser, struct device_node *node, const char *name) { - const int na = 3, ns = 2; int rlen; parser->node = node; parser->pna = of_n_addr_cells(node); - parser->np = parser->pna + na + ns; + parser->na = of_bus_n_addr_cells(node); + parser->ns = of_bus_n_size_cells(node); parser->dma = !strcmp(name, "dma-ranges"); parser->range = of_get_property(node, name, &rlen); @@ -724,20 +724,28 @@ int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, return parser_init(parser, node, "dma-ranges"); } EXPORT_SYMBOL_GPL(of_pci_dma_range_parser_init); +#define of_dma_range_parser_init of_pci_dma_range_parser_init struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, struct of_pci_range *range) { - const int na = 3, ns = 2; + int na = parser->na; + int ns = parser->ns; + int np = parser->pna + na + ns; if (!range) return NULL; - if (!parser->range || parser->range + parser->np > parser->end) + if (!parser->range || parser->range + np > parser->end) return NULL; - range->flags = of_bus_pci_get_flags(parser->range); - range->pci_addr = of_read_number(parser->range + 1, ns); + if (parser->na == 3) + range->flags = of_bus_pci_get_flags(parser->range); + else + range->flags = 0; + + range->pci_addr = of_read_number(parser->range, na); + if (parser->dma) range->cpu_addr = of_translate_dma_address(parser->node, parser->range + na); @@ -746,15 +754,16 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, parser->range + na); range->size = of_read_number(parser->range + parser->pna + na, ns); - parser->range += parser->np; + parser->range += np; /* Now consume following elements while they are contiguous */ - while (parser->range + parser->np <= parser->end) { - u32 flags; + while (parser->range + np <= parser->end) { + u32 flags = 0; u64 pci_addr, cpu_addr, size; - flags = of_bus_pci_get_flags(parser->range); - pci_addr = of_read_number(parser->range + 1, ns); + if (parser->na == 3) + flags = of_bus_pci_get_flags(parser->range); + pci_addr = of_read_number(parser->range, na); if (parser->dma) cpu_addr = of_translate_dma_address(parser->node, parser->range + na); @@ -770,7 +779,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, break; range->size += size; - parser->range += parser->np; + parser->range += np; } return range; diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 8d12bf18e80b..763022ed3456 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -10,20 +10,27 @@ struct of_pci_range_parser { struct device_node *node; const __be32 *range; const __be32 *end; - int np; + int na; + int ns; int pna; bool dma; }; +#define of_range_parser of_pci_range_parser struct of_pci_range { - u64 pci_addr; + union { + u64 pci_addr; + u64 bus_addr; + }; u64 cpu_addr; u64 size; u32 flags; }; +#define of_range of_pci_range #define for_each_of_pci_range(parser, range) \ for (; of_pci_range_parser_one(parser, range);) +#define for_each_of_range for_each_of_pci_range /* Translate a DMA address from device space to CPU space */ extern u64 of_translate_dma_address(struct device_node *dev, @@ -142,4 +149,3 @@ static inline int of_pci_range_to_resource(struct of_pci_range *range, #endif /* CONFIG_OF_ADDRESS && CONFIG_PCI */ #endif /* __OF_ADDRESS_H */ - -- cgit v1.2.3 From 54a2a15a56648998ac6ca103f33fe5cdb672ae7a Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 14 Feb 2020 10:48:03 -0800 Subject: ACPICA: Fix a couple of typos Squashed ACPICA commit e93a53d4d312a83a3ec72aa9cfb12d781b4fefca and df52c574572344cd9095b66a0f580d51249deb2a Submitted by: ehaouas@noos.fr Link: https://github.com/acpica/acpica/commit/e93a53d4 Signed-off-by: Bob Moore Signed-off-by: Erik Kaneda Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/utobject.c | 2 +- include/acpi/actbl1.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index 3e60bdac2200..bbec04c291d2 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -44,7 +44,7 @@ acpi_ut_get_element_length(u8 object_type, * * NOTE: We always allocate the worst-case object descriptor because * these objects are cached, and we want them to be - * one-size-satisifies-any-request. This in itself may not be + * one-size-satisfies-any-request. This in itself may not be * the most memory efficient, but the efficiency of the object * cache should more than make up for this! * diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 02d06b79e1cd..43549547ed3e 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -862,7 +862,7 @@ enum acpi_erst_instructions { /* Command status return values */ enum acpi_erst_command_status { - ACPI_ERST_SUCESS = 0, + ACPI_ERST_SUCCESS = 0, ACPI_ERST_NO_SPACE = 1, ACPI_ERST_NOT_AVAILABLE = 2, ACPI_ERST_FAILURE = 3, -- cgit v1.2.3 From 8c79fa6c44deac8042bd747527fea06a32738158 Mon Sep 17 00:00:00 2001 From: Jungseung Lee Date: Mon, 13 Jan 2020 14:59:05 +0900 Subject: mtd: spi-nor: introduce SR_BP_SHIFT define The shift variable of SR_BP is conclusive because the first bit of SR_BP is fixed on all known flashes. Replace ffs operation with SR_BP_SHIFT. Signed-off-by: Jungseung Lee Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 11 +++++------ include/linux/mtd/spi-nor.h | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 864ed6f49e87..b5ef17b2897a 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -1767,7 +1767,6 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, struct mtd_info *mtd = &nor->mtd; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - int shift = ffs(mask) - 1; int pow; if (nor->flags & SNOR_F_HAS_SR_TB_BIT6) @@ -1778,7 +1777,7 @@ static void stm_get_locked_range(struct spi_nor *nor, u8 sr, loff_t *ofs, *ofs = 0; *len = 0; } else { - pow = ((sr & mask) ^ mask) >> shift; + pow = ((sr & mask) ^ mask) >> SR_BP_SHIFT; *len = mtd->size >> pow; if (nor->flags & SNOR_F_HAS_SR_TB && sr & tb_mask) *ofs = 0; @@ -1860,7 +1859,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - u8 shift = ffs(mask) - 1, pow, val; + u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; @@ -1909,7 +1908,7 @@ static int stm_lock(struct spi_nor *nor, loff_t ofs, uint64_t len) * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len)) */ pow = ilog2(mtd->size) - ilog2(lock_len); - val = mask - (pow << shift); + val = mask - (pow << SR_BP_SHIFT); if (val & ~mask) return -EINVAL; /* Don't "lock" with no region! */ @@ -1946,7 +1945,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) int ret, status_old, status_new; u8 mask = SR_BP2 | SR_BP1 | SR_BP0; u8 tb_mask = SR_TB_BIT5; - u8 shift = ffs(mask) - 1, pow, val; + u8 pow, val; loff_t lock_len; bool can_be_top = true, can_be_bottom = nor->flags & SNOR_F_HAS_SR_TB; bool use_top; @@ -1997,7 +1996,7 @@ static int stm_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len) if (lock_len == 0) { val = 0; /* fully unlocked */ } else { - val = mask - (pow << shift); + val = mask - (pow << SR_BP_SHIFT); /* Some power-of-two sizes are not supported */ if (val & ~mask) return -EINVAL; diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 5abd91cc6dfa..61be6ed33097 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -137,6 +137,8 @@ #define SR1_QUAD_EN_BIT6 BIT(6) +#define SR_BP_SHIFT 2 + /* Enhanced Volatile Configuration Register bits */ #define EVCR_QUAD_EN_MICRON BIT(7) /* Micron Quad I/O */ -- cgit v1.2.3 From d71151a39c97d551378a441c089508b0bca48210 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Tue, 11 Feb 2020 12:52:49 +0800 Subject: ptp_qoriq: drop the code of alarm The alarm function hadn't been supported by PTP clock driver. The recommended solution PHC + phc2sys + nanosleep provides best performance. So drop the code of alarm in ptp_qoriq driver. Signed-off-by: Yangbo Lu Signed-off-by: David S. Miller --- drivers/ptp/ptp_qoriq.c | 29 +---------------------------- include/linux/fsl/ptp_qoriq.h | 2 -- 2 files changed, 1 insertion(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/ptp/ptp_qoriq.c b/drivers/ptp/ptp_qoriq.c index b27c46ebfc8f..c09c16be0edf 100644 --- a/drivers/ptp/ptp_qoriq.c +++ b/drivers/ptp/ptp_qoriq.c @@ -131,8 +131,7 @@ irqreturn_t ptp_qoriq_isr(int irq, void *priv) struct ptp_qoriq *ptp_qoriq = priv; struct ptp_qoriq_registers *regs = &ptp_qoriq->regs; struct ptp_clock_event event; - u64 ns; - u32 ack = 0, lo, hi, mask, val, irqs; + u32 ack = 0, mask, val, irqs; spin_lock(&ptp_qoriq->lock); @@ -153,32 +152,6 @@ irqreturn_t ptp_qoriq_isr(int irq, void *priv) extts_clean_up(ptp_qoriq, 1, true); } - if (irqs & ALM2) { - ack |= ALM2; - if (ptp_qoriq->alarm_value) { - event.type = PTP_CLOCK_ALARM; - event.index = 0; - event.timestamp = ptp_qoriq->alarm_value; - ptp_clock_event(ptp_qoriq->clock, &event); - } - if (ptp_qoriq->alarm_interval) { - ns = ptp_qoriq->alarm_value + ptp_qoriq->alarm_interval; - hi = ns >> 32; - lo = ns & 0xffffffff; - ptp_qoriq->write(®s->alarm_regs->tmr_alarm2_l, lo); - ptp_qoriq->write(®s->alarm_regs->tmr_alarm2_h, hi); - ptp_qoriq->alarm_value = ns; - } else { - spin_lock(&ptp_qoriq->lock); - mask = ptp_qoriq->read(®s->ctrl_regs->tmr_temask); - mask &= ~ALM2EN; - ptp_qoriq->write(®s->ctrl_regs->tmr_temask, mask); - spin_unlock(&ptp_qoriq->lock); - ptp_qoriq->alarm_value = 0; - ptp_qoriq->alarm_interval = 0; - } - } - if (irqs & PP1) { ack |= PP1; event.type = PTP_CLOCK_PPS; diff --git a/include/linux/fsl/ptp_qoriq.h b/include/linux/fsl/ptp_qoriq.h index b0b743563f43..75884563059f 100644 --- a/include/linux/fsl/ptp_qoriq.h +++ b/include/linux/fsl/ptp_qoriq.h @@ -149,8 +149,6 @@ struct ptp_qoriq { bool extts_fifo_support; int irq; int phc_index; - u64 alarm_interval; /* for periodic alarm */ - u64 alarm_value; u32 tclk_period; /* nanoseconds */ u32 tmr_prsc; u32 tmr_add; -- cgit v1.2.3 From c8856c051454909e5059df4e81c77b9c366c5515 Mon Sep 17 00:00:00 2001 From: Arjun Roy Date: Fri, 14 Feb 2020 15:30:49 -0800 Subject: tcp-zerocopy: Return inq along with tcp receive zerocopy. This patchset is intended to reduce the number of extra system calls imposed by TCP receive zerocopy. For ping-pong RPC style workloads, this patchset has demonstrated a system call reduction of about 30% when coupled with userspace changes. For applications using edge-triggered epoll, returning inq along with the result of tcp receive zerocopy could remove the need to call recvmsg()=-EAGAIN after a successful zerocopy. Generally speaking, since normally we would need to perform a recvmsg() call for every successful small RPC read via TCP receive zerocopy, returning inq can reduce the number of system calls performed by approximately half. Signed-off-by: Arjun Roy Signed-off-by: Eric Dumazet Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index fd9eb8f6bcae..548f480b9c66 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -345,5 +345,6 @@ struct tcp_zerocopy_receive { __u64 address; /* in: address of mapping */ __u32 length; /* in/out: number of bytes to map/mapped */ __u32 recv_skip_hint; /* out: amount of bytes to skip */ + __u32 inq; /* out: amount of bytes in read queue */ }; #endif /* _UAPI_LINUX_TCP_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index eb2d80519f8e..a697f1455f8d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3667,13 +3667,26 @@ static int do_tcp_getsockopt(struct sock *sk, int level, if (get_user(len, optlen)) return -EFAULT; - if (len != sizeof(zc)) + if (len < offsetofend(struct tcp_zerocopy_receive, length)) return -EINVAL; + if (len > sizeof(zc)) + len = sizeof(zc); if (copy_from_user(&zc, optval, len)) return -EFAULT; lock_sock(sk); err = tcp_zerocopy_receive(sk, &zc); release_sock(sk); + switch (len) { + case sizeof(zc): + case offsetofend(struct tcp_zerocopy_receive, inq): + goto zerocopy_rcv_inq; + case offsetofend(struct tcp_zerocopy_receive, length): + default: + goto zerocopy_rcv_out; + } +zerocopy_rcv_inq: + zc.inq = tcp_inq_hint(sk); +zerocopy_rcv_out: if (!err && copy_to_user(optval, &zc, len)) err = -EFAULT; return err; -- cgit v1.2.3 From 33946518d493cdf10aedb4a483f1aa41948a3dab Mon Sep 17 00:00:00 2001 From: Arjun Roy Date: Fri, 14 Feb 2020 15:30:50 -0800 Subject: tcp-zerocopy: Return sk_err (if set) along with tcp receive zerocopy. This patchset is intended to reduce the number of extra system calls imposed by TCP receive zerocopy. For ping-pong RPC style workloads, this patchset has demonstrated a system call reduction of about 30% when coupled with userspace changes. For applications using epoll, returning sk_err along with the result of tcp receive zerocopy could remove the need to call recvmsg()=-EAGAIN after a spurious wakeup. Consider a multi-threaded application using epoll. A thread may awaken with EPOLLIN but another thread may already be reading. The spuriously-awoken thread does not necessarily know that another thread 'won'; rather, it may be possible that it was woken up due to the presence of an error if there is no data. A zerocopy read receiving 0 bytes thus would need to be followed up by recvmsg to be sure. Instead, we return sk_err directly with zerocopy, so the application can avoid this extra system call. Signed-off-by: Arjun Roy Signed-off-by: Eric Dumazet Signed-off-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 548f480b9c66..1a7fc856e237 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -346,5 +346,6 @@ struct tcp_zerocopy_receive { __u32 length; /* in/out: number of bytes to map/mapped */ __u32 recv_skip_hint; /* out: amount of bytes to skip */ __u32 inq; /* out: amount of bytes in read queue */ + __s32 err; /* out: socket error */ }; #endif /* _UAPI_LINUX_TCP_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a697f1455f8d..1b685485a5b5 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3676,14 +3676,20 @@ static int do_tcp_getsockopt(struct sock *sk, int level, lock_sock(sk); err = tcp_zerocopy_receive(sk, &zc); release_sock(sk); + if (len == sizeof(zc)) + goto zerocopy_rcv_sk_err; switch (len) { - case sizeof(zc): + case offsetofend(struct tcp_zerocopy_receive, err): + goto zerocopy_rcv_sk_err; case offsetofend(struct tcp_zerocopy_receive, inq): goto zerocopy_rcv_inq; case offsetofend(struct tcp_zerocopy_receive, length): default: goto zerocopy_rcv_out; } +zerocopy_rcv_sk_err: + if (!err) + zc.err = sock_error(sk); zerocopy_rcv_inq: zc.inq = tcp_inq_hint(sk); zerocopy_rcv_out: -- cgit v1.2.3 From 744676e777207f4992ba4cc728a8a71352963c5b Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Sat, 15 Feb 2020 14:20:56 +0100 Subject: openvswitch: add TTL decrement action New action to decrement TTL instead of setting it to a fixed value. This action will decrement the TTL and, in case of expired TTL, drop it or execute an action passed via a nested attribute. The default TTL expired action is to drop the packet. Supports both IPv4 and IPv6 via the ttl and hop_limit fields, respectively. Tested with a corresponding change in the userspace: # ovs-dpctl dump-flows in_port(2),eth(),eth_type(0x0800), packets:0, bytes:0, used:never, actions:dec_ttl{ttl<=1 action:(drop)},1 in_port(1),eth(),eth_type(0x0800), packets:0, bytes:0, used:never, actions:dec_ttl{ttl<=1 action:(drop)},2 in_port(1),eth(),eth_type(0x0806), packets:0, bytes:0, used:never, actions:2 in_port(2),eth(),eth_type(0x0806), packets:0, bytes:0, used:never, actions:1 # ping -c1 192.168.0.2 -t 42 IP (tos 0x0, ttl 41, id 61647, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > 192.168.0.2: ICMP echo request, id 386, seq 1, length 64 # ping -c1 192.168.0.2 -t 120 IP (tos 0x0, ttl 119, id 62070, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.0.1 > 192.168.0.2: ICMP echo request, id 388, seq 1, length 64 # ping -c1 192.168.0.2 -t 1 # Co-developed-by: Bindiya Kurle Signed-off-by: Bindiya Kurle Signed-off-by: Matteo Croce Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 7 ++++ net/openvswitch/actions.c | 67 ++++++++++++++++++++++++++++++++++++++ net/openvswitch/flow_netlink.c | 70 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index ae2bff14e7e1..9b14519e74d9 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -958,6 +958,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_CLONE, /* Nested OVS_CLONE_ATTR_*. */ OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*. */ OVS_ACTION_ATTR_ADD_MPLS, /* struct ovs_action_add_mpls. */ + OVS_ACTION_ATTR_DEC_TTL, /* Nested OVS_DEC_TTL_ATTR_*. */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ @@ -1050,4 +1051,10 @@ struct ovs_zone_limit { __u32 count; }; +enum ovs_dec_ttl_attr { + OVS_DEC_TTL_ATTR_UNSPEC, + OVS_DEC_TTL_ATTR_ACTION, /* Nested struct nlattr */ + __OVS_DEC_TTL_ATTR_MAX +}; + #endif /* _LINUX_OPENVSWITCH_H */ diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 7fbfe2adfffa..fc0efd8833c8 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -964,6 +964,25 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, return ovs_dp_upcall(dp, skb, key, &upcall, cutlen); } +static int dec_ttl_exception_handler(struct datapath *dp, struct sk_buff *skb, + struct sw_flow_key *key, + const struct nlattr *attr, bool last) +{ + /* The first action is always 'OVS_DEC_TTL_ATTR_ARG'. */ + struct nlattr *dec_ttl_arg = nla_data(attr); + int rem = nla_len(attr); + + if (nla_len(dec_ttl_arg)) { + struct nlattr *actions = nla_next(dec_ttl_arg, &rem); + + if (actions) + return clone_execute(dp, skb, key, 0, actions, rem, + last, false); + } + consume_skb(skb); + return 0; +} + /* When 'last' is true, sample() should always consume the 'skb'. * Otherwise, sample() should keep 'skb' intact regardless what * actions are executed within sample(). @@ -1180,6 +1199,45 @@ static int execute_check_pkt_len(struct datapath *dp, struct sk_buff *skb, nla_len(actions), last, clone_flow_key); } +static int execute_dec_ttl(struct sk_buff *skb, struct sw_flow_key *key) +{ + int err; + + if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *nh; + + err = skb_ensure_writable(skb, skb_network_offset(skb) + + sizeof(*nh)); + if (unlikely(err)) + return err; + + nh = ipv6_hdr(skb); + + if (nh->hop_limit <= 1) + return -EHOSTUNREACH; + + key->ip.ttl = --nh->hop_limit; + } else { + struct iphdr *nh; + u8 old_ttl; + + err = skb_ensure_writable(skb, skb_network_offset(skb) + + sizeof(*nh)); + if (unlikely(err)) + return err; + + nh = ip_hdr(skb); + if (nh->ttl <= 1) + return -EHOSTUNREACH; + + old_ttl = nh->ttl--; + csum_replace2(&nh->check, htons(old_ttl << 8), + htons(nh->ttl << 8)); + key->ip.ttl = nh->ttl; + } + return 0; +} + /* Execute a list of actions against 'skb'. */ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, @@ -1365,6 +1423,15 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, break; } + + case OVS_ACTION_ATTR_DEC_TTL: + err = execute_dec_ttl(skb, key); + if (err == -EHOSTUNREACH) { + err = dec_ttl_exception_handler(dp, skb, key, + a, true); + return err; + } + break; } if (unlikely(err)) { diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 7da4230627f5..1ccd5e092bca 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -80,6 +80,7 @@ static bool actions_may_change_flow(const struct nlattr *actions) case OVS_ACTION_ATTR_METER: case OVS_ACTION_ATTR_CHECK_PKT_LEN: case OVS_ACTION_ATTR_ADD_MPLS: + case OVS_ACTION_ATTR_DEC_TTL: default: return true; } @@ -2495,6 +2496,39 @@ static int validate_and_copy_sample(struct net *net, const struct nlattr *attr, return 0; } +static int validate_and_copy_dec_ttl(struct net *net, + const struct nlattr *attr, + const struct sw_flow_key *key, + struct sw_flow_actions **sfa, + __be16 eth_type, __be16 vlan_tci, + u32 mpls_label_count, bool log) +{ + int start, err; + u32 nested = true; + + if (!nla_len(attr)) + return ovs_nla_add_action(sfa, OVS_ACTION_ATTR_DEC_TTL, + NULL, 0, log); + + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_DEC_TTL, log); + if (start < 0) + return start; + + err = ovs_nla_add_action(sfa, OVS_DEC_TTL_ATTR_ACTION, &nested, + sizeof(nested), log); + + if (err) + return err; + + err = __ovs_nla_copy_actions(net, attr, key, sfa, eth_type, + vlan_tci, mpls_label_count, log); + if (err) + return err; + + add_nested_action_end(*sfa, start); + return 0; +} + static int validate_and_copy_clone(struct net *net, const struct nlattr *attr, const struct sw_flow_key *key, @@ -3007,6 +3041,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_CLONE] = (u32)-1, [OVS_ACTION_ATTR_CHECK_PKT_LEN] = (u32)-1, [OVS_ACTION_ATTR_ADD_MPLS] = sizeof(struct ovs_action_add_mpls), + [OVS_ACTION_ATTR_DEC_TTL] = (u32)-1, }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -3267,6 +3302,15 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, break; } + case OVS_ACTION_ATTR_DEC_TTL: + err = validate_and_copy_dec_ttl(net, a, key, sfa, + eth_type, vlan_tci, + mpls_label_count, log); + if (err) + return err; + skip_copy = true; + break; + default: OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; @@ -3438,6 +3482,26 @@ out: return err; } +static int dec_ttl_action_to_attr(const struct nlattr *attr, + struct sk_buff *skb) +{ + int err = 0, rem = nla_len(attr); + struct nlattr *start; + + start = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_DEC_TTL); + + if (!start) + return -EMSGSIZE; + + err = ovs_nla_put_actions(nla_data(attr), rem, skb); + if (err) + nla_nest_cancel(skb, start); + else + nla_nest_end(skb, start); + + return err; +} + static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) { const struct nlattr *ovs_key = nla_data(a); @@ -3538,6 +3602,12 @@ int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb) return err; break; + case OVS_ACTION_ATTR_DEC_TTL: + err = dec_ttl_action_to_attr(a, skb); + if (err) + return err; + break; + default: if (nla_put(skb, type, nla_len(a), nla_data(a))) return -EMSGSIZE; -- cgit v1.2.3 From 8062e2333f8f7dcd5627e22b99e18d1cbb53eedb Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Feb 2020 23:57:36 +0000 Subject: net: linkmode: make linkmode_test_bit() take const pointer linkmode_test_bit() does not modify the address; test_bit() is also declared const volatile for the same reason. There's no need for linkmode_test_bit() to be any different, and allows implementation of helpers that take a const linkmode pointer. Reviewed-by: Andrew Lunn Signed-off-by: Russell King Signed-off-by: David S. Miller --- include/linux/linkmode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h index fe740031339d..8e5b352e44f2 100644 --- a/include/linux/linkmode.h +++ b/include/linux/linkmode.h @@ -71,7 +71,7 @@ static inline void linkmode_change_bit(int nr, volatile unsigned long *addr) __change_bit(nr, addr); } -static inline int linkmode_test_bit(int nr, volatile unsigned long *addr) +static inline int linkmode_test_bit(int nr, const volatile unsigned long *addr) { return test_bit(nr, addr); } -- cgit v1.2.3 From a87ae8a963bde755b0962bcc18db83d611f63e7a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Feb 2020 15:49:27 +0000 Subject: net: add helpers to resolve negotiated flow control Add a couple of helpers to resolve negotiated flow control. Two helpers are provided: - linkmode_resolve_pause() which takes the link partner and local advertisements, and decodes whether we should enable TX or RX pause at the MAC. This is useful outside of phylib, e.g. in phylink. - phy_get_pause(), which returns the TX/RX enablement status for the current negotiation results of the PHY. This allows us to centralise the flow control resolution, rather than spreading it around. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/Makefile | 3 ++- drivers/net/phy/linkmode.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy_device.c | 26 ++++++++++++++++++++++++++ include/linux/linkmode.h | 4 ++++ include/linux/phy.h | 3 +++ 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 drivers/net/phy/linkmode.c (limited to 'include') diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index fe5badf13b65..d523fd5670e4 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 # Makefile for Linux PHY drivers and MDIO bus drivers -libphy-y := phy.o phy-c45.o phy-core.o phy_device.o +libphy-y := phy.o phy-c45.o phy-core.o phy_device.o \ + linkmode.o mdio-bus-y += mdio_bus.o mdio_device.o ifdef CONFIG_MDIO_DEVICE diff --git a/drivers/net/phy/linkmode.c b/drivers/net/phy/linkmode.c new file mode 100644 index 000000000000..969918795228 --- /dev/null +++ b/drivers/net/phy/linkmode.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include + +/** + * linkmode_resolve_pause - resolve the allowable pause modes + * @local_adv: local advertisement in ethtool format + * @partner_adv: partner advertisement in ethtool format + * @tx_pause: pointer to bool to indicate whether transmit pause should be + * enabled. + * @rx_pause: pointer to bool to indicate whether receive pause should be + * enabled. + * + * Flow control is resolved according to our and the link partners + * advertisements using the following drawn from the 802.3 specs: + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 0 X 0 X Disabled + * 0 1 1 0 Disabled + * 0 1 1 1 TX + * 1 0 0 X Disabled + * 1 X 1 X TX+RX + * 1 1 0 1 RX + */ +void linkmode_resolve_pause(const unsigned long *local_adv, + const unsigned long *partner_adv, + bool *tx_pause, bool *rx_pause) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(m); + + linkmode_and(m, local_adv, partner_adv); + if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, m)) { + *tx_pause = true; + *rx_pause = true; + } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, m)) { + *tx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + partner_adv); + *rx_pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, + local_adv); + } else { + *tx_pause = false; + *rx_pause = false; + } +} +EXPORT_SYMBOL_GPL(linkmode_resolve_pause); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 6a5056e0ae77..f5a7a077ec1f 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2409,6 +2409,32 @@ bool phy_validate_pause(struct phy_device *phydev, } EXPORT_SYMBOL(phy_validate_pause); +/** + * phy_get_pause - resolve negotiated pause modes + * @phydev: phy_device struct + * @tx_pause: pointer to bool to indicate whether transmit pause should be + * enabled. + * @rx_pause: pointer to bool to indicate whether receive pause should be + * enabled. + * + * Resolve and return the flow control modes according to the negotiation + * result. This includes checking that we are operating in full duplex mode. + * See linkmode_resolve_pause() for further details. + */ +void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause) +{ + if (phydev->duplex != DUPLEX_FULL) { + *tx_pause = false; + *rx_pause = false; + return; + } + + return linkmode_resolve_pause(phydev->advertising, + phydev->lp_advertising, + tx_pause, rx_pause); +} +EXPORT_SYMBOL(phy_get_pause); + static bool phy_drv_supports_irq(struct phy_driver *phydrv) { return phydrv->config_intr && phydrv->ack_interrupt; diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h index 8e5b352e44f2..9ec210f31d06 100644 --- a/include/linux/linkmode.h +++ b/include/linux/linkmode.h @@ -88,4 +88,8 @@ static inline int linkmode_subset(const unsigned long *src1, return bitmap_subset(src1, src2, __ETHTOOL_LINK_MODE_MASK_NBITS); } +void linkmode_resolve_pause(const unsigned long *local_adv, + const unsigned long *partner_adv, + bool *tx_pause, bool *rx_pause); + #endif /* __LINKMODE_H */ diff --git a/include/linux/phy.h b/include/linux/phy.h index c570e162e05e..80f8b2158271 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1257,6 +1257,9 @@ void phy_set_sym_pause(struct phy_device *phydev, bool rx, bool tx, void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx); bool phy_validate_pause(struct phy_device *phydev, struct ethtool_pauseparam *pp); +void phy_get_pause(struct phy_device *phydev, bool *tx_pause, bool *rx_pause); +void phy_resolve_pause(unsigned long *local_adv, unsigned long *partner_adv, + bool *tx_pause, bool *rx_pause); int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask, int (*run)(struct phy_device *)); -- cgit v1.2.3 From 45c767faef151899ac1a5e14a59c0e0a5bdba27b Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Feb 2020 15:49:32 +0000 Subject: net: add linkmode helper for setting flow control advertisement Add a linkmode helper to set the flow control advertisement in an ethtool linkmode mask according to the tx/rx capabilities. This implementation is moved from phylib, and documented with an analysis of its shortcomings. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/linkmode.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ drivers/net/phy/phy_device.c | 17 +-------------- include/linux/linkmode.h | 2 ++ 3 files changed, 54 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/linkmode.c b/drivers/net/phy/linkmode.c index 969918795228..f60560fe3499 100644 --- a/drivers/net/phy/linkmode.c +++ b/drivers/net/phy/linkmode.c @@ -42,3 +42,54 @@ void linkmode_resolve_pause(const unsigned long *local_adv, } } EXPORT_SYMBOL_GPL(linkmode_resolve_pause); + +/** + * linkmode_set_pause - set the pause mode advertisement + * @advertisement: advertisement in ethtool format + * @tx: boolean from ethtool struct ethtool_pauseparam tx_pause member + * @rx: boolean from ethtool struct ethtool_pauseparam rx_pause member + * + * Configure the advertised Pause and Asym_Pause bits according to the + * capabilities of provided in @tx and @rx. + * + * We convert as follows: + * tx rx Pause AsymDir + * 0 0 0 0 + * 0 1 1 1 + * 1 0 0 1 + * 1 1 1 0 + * + * Note: this translation from ethtool tx/rx notation to the advertisement + * is actually very problematical. Here are some examples: + * + * For tx=0 rx=1, meaning transmit is unsupported, receive is supported: + * + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 1 1 1 0 TX + RX - but we have no TX support. + * 1 1 0 1 Only this gives RX only + * + * For tx=1 rx=1, meaning we have the capability to transmit and receive + * pause frames: + * + * Local device Link partner + * Pause AsymDir Pause AsymDir Result + * 1 0 0 1 Disabled - but since we do support tx and rx, + * this should resolve to RX only. + * + * Hence, asking for: + * rx=1 tx=0 gives Pause+AsymDir advertisement, but we may end up + * resolving to tx+rx pause or only rx pause depending on + * the partners advertisement. + * rx=0 tx=1 gives AsymDir only, which will only give tx pause if + * the partners advertisement allows it. + * rx=1 tx=1 gives Pause only, which will only allow tx+rx pause + * if the other end also advertises Pause. + */ +void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx) +{ + linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT, advertisement, rx); + linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, advertisement, + rx ^ tx); +} +EXPORT_SYMBOL_GPL(linkmode_set_pause); diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index f5a7a077ec1f..2a973265de80 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2361,22 +2361,7 @@ void phy_set_asym_pause(struct phy_device *phydev, bool rx, bool tx) __ETHTOOL_DECLARE_LINK_MODE_MASK(oldadv); linkmode_copy(oldadv, phydev->advertising); - - linkmode_clear_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->advertising); - linkmode_clear_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); - - if (rx) { - linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, - phydev->advertising); - linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); - } - - if (tx) - linkmode_change_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, - phydev->advertising); + linkmode_set_pause(phydev->advertising, tx, rx); if (!linkmode_equal(oldadv, phydev->advertising) && phydev->autoneg) diff --git a/include/linux/linkmode.h b/include/linux/linkmode.h index 9ec210f31d06..c664c27a29a0 100644 --- a/include/linux/linkmode.h +++ b/include/linux/linkmode.h @@ -92,4 +92,6 @@ void linkmode_resolve_pause(const unsigned long *local_adv, const unsigned long *partner_adv, bool *tx_pause, bool *rx_pause); +void linkmode_set_pause(unsigned long *advertisement, bool tx, bool rx); + #endif /* __LINKMODE_H */ -- cgit v1.2.3 From 4e5aeb4157c879a021e6d92373dc7e4684ebd8c0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Feb 2020 15:49:53 +0000 Subject: net: phylink: resolve fixed link flow control Resolve the fixed link flow control using the recently introduced linkmode_resolve_pause() helper, which we use in phylink_get_fixed_state() only when operating in full duplex mode. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/phy/phylink.c | 70 +++++++++++++++++++++-------------------------- include/linux/phylink.h | 8 ++---- 2 files changed, 34 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index e65e9c9dc759..c29648b90ce7 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -181,9 +181,11 @@ static int phylink_parse_fixedlink(struct phylink *pl, /* We treat the "pause" and "asym-pause" terminology as * defining the link partner's ability. */ if (fwnode_property_read_bool(fixed_node, "pause")) - pl->link_config.pause |= MLO_PAUSE_SYM; + __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + pl->link_config.lp_advertising); if (fwnode_property_read_bool(fixed_node, "asym-pause")) - pl->link_config.pause |= MLO_PAUSE_ASYM; + __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + pl->link_config.lp_advertising); if (ret == 0) { desc = fwnode_gpiod_get_index(fixed_node, "link", 0, @@ -215,9 +217,11 @@ static int phylink_parse_fixedlink(struct phylink *pl, DUPLEX_FULL : DUPLEX_HALF; pl->link_config.speed = prop[2]; if (prop[3]) - pl->link_config.pause |= MLO_PAUSE_SYM; + __set_bit(ETHTOOL_LINK_MODE_Pause_BIT, + pl->link_config.lp_advertising); if (prop[4]) - pl->link_config.pause |= MLO_PAUSE_ASYM; + __set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, + pl->link_config.lp_advertising); } } @@ -351,6 +355,22 @@ static void phylink_apply_manual_flow(struct phylink *pl, state->pause = pl->link_config.pause; } +static void phylink_resolve_flow(struct phylink_link_state *state) +{ + bool tx_pause, rx_pause; + + state->pause = MLO_PAUSE_NONE; + if (state->duplex == DUPLEX_FULL) { + linkmode_resolve_pause(state->advertising, + state->lp_advertising, + &tx_pause, &rx_pause); + if (tx_pause) + state->pause |= MLO_PAUSE_TX; + if (rx_pause) + state->pause |= MLO_PAUSE_RX; + } +} + static void phylink_mac_config(struct phylink *pl, const struct phylink_link_state *state) { @@ -399,44 +419,16 @@ static void phylink_mac_pcs_get_state(struct phylink *pl, /* The fixed state is... fixed except for the link state, * which may be determined by a GPIO or a callback. */ -static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state) +static void phylink_get_fixed_state(struct phylink *pl, + struct phylink_link_state *state) { *state = pl->link_config; if (pl->get_fixed_state) pl->get_fixed_state(pl->netdev, state); else if (pl->link_gpio) state->link = !!gpiod_get_value_cansleep(pl->link_gpio); -} -/* Flow control is resolved according to our and the link partners - * advertisements using the following drawn from the 802.3 specs: - * Local device Link partner - * Pause AsymDir Pause AsymDir Result - * 1 X 1 X TX+RX - * 0 1 1 1 TX - * 1 1 0 1 RX - */ -static void phylink_resolve_flow(struct phylink *pl, - struct phylink_link_state *state) -{ - int new_pause = 0; - int pause = 0; - - if (phylink_test(pl->link_config.advertising, Pause)) - pause |= MLO_PAUSE_SYM; - if (phylink_test(pl->link_config.advertising, Asym_Pause)) - pause |= MLO_PAUSE_ASYM; - - pause &= state->pause; - - if (pause & MLO_PAUSE_SYM) - new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX; - else if (pause & MLO_PAUSE_ASYM) - new_pause = state->pause & MLO_PAUSE_SYM ? - MLO_PAUSE_TX : MLO_PAUSE_RX; - - state->pause &= ~MLO_PAUSE_TXRX_MASK; - state->pause |= new_pause; + phylink_resolve_flow(state); } static const char *phylink_pause_to_str(int pause) @@ -1393,8 +1385,7 @@ int phylink_ethtool_set_pauseparam(struct phylink *pl, !pause->autoneg && pause->rx_pause != pause->tx_pause) return -EINVAL; - config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK); - + config->pause = 0; if (pause->autoneg) config->pause |= MLO_PAUSE_AN; if (pause->rx_pause) @@ -1505,13 +1496,14 @@ static int phylink_mii_emul_read(unsigned int reg, struct phylink_link_state *state) { struct fixed_phy_status fs; + unsigned long *lpa = state->lp_advertising; int val; fs.link = state->link; fs.speed = state->speed; fs.duplex = state->duplex; - fs.pause = state->pause & MLO_PAUSE_SYM; - fs.asym_pause = state->pause & MLO_PAUSE_ASYM; + fs.pause = test_bit(ETHTOOL_LINK_MODE_Pause_BIT, lpa); + fs.asym_pause = test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, lpa); val = swphy_read_reg(reg, &fs); if (reg == MII_BMSR) { diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 523209e70947..0d6073c2b2b7 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -12,12 +12,10 @@ struct net_device; enum { MLO_PAUSE_NONE, - MLO_PAUSE_ASYM = BIT(0), - MLO_PAUSE_SYM = BIT(1), - MLO_PAUSE_RX = BIT(2), - MLO_PAUSE_TX = BIT(3), + MLO_PAUSE_RX = BIT(0), + MLO_PAUSE_TX = BIT(1), MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX, - MLO_PAUSE_AN = BIT(4), + MLO_PAUSE_AN = BIT(2), MLO_AN_PHY = 0, /* Conventional PHY */ MLO_AN_FIXED, /* Fixed-link mode */ -- cgit v1.2.3 From b70486f94bb4820e84491089da5e30d29e774b0d Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 15 Feb 2020 15:50:09 +0000 Subject: net: phylink: clarify flow control settings in documentation Clarify the expected flow control settings operation in the phylink documentation for each negotiation mode. Signed-off-by: Russell King Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- include/linux/phylink.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 0d6073c2b2b7..812357c03df4 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -152,13 +152,20 @@ void mac_pcs_get_state(struct phylink_config *config, * guaranteed to be correct, and so any mac_config() implementation must * never reference these fields. * + * In all negotiation modes, as defined by @mode, @state->pause indicates the + * pause settings which should be applied as follows. If %MLO_PAUSE_AN is not + * set, %MLO_PAUSE_TX and %MLO_PAUSE_RX indicate whether the MAC should send + * pause frames and/or act on received pause frames respectively. Otherwise, + * the results of in-band negotiation/status from the MAC PCS should be used + * to control the MAC pause mode settings. + * * The action performed depends on the currently selected mode: * * %MLO_AN_FIXED, %MLO_AN_PHY: - * Configure the specified @state->speed, @state->duplex and - * @state->pause (%MLO_PAUSE_TX / %MLO_PAUSE_RX) modes over a link - * specified by @state->interface. @state->advertising may be used, - * but is not required. Other members of @state must be ignored. + * Configure the specified @state->speed and @state->duplex over a link + * specified by @state->interface. @state->advertising may be used, but + * is not required. Pause modes as above. Other members of @state must + * be ignored. * * Valid state members: interface, speed, duplex, pause, advertising. * @@ -170,11 +177,14 @@ void mac_pcs_get_state(struct phylink_config *config, * mac_pcs_get_state() callback. Changes in link state must be made * by calling phylink_mac_change(). * + * Interface mode specific details are mentioned below. + * * If in 802.3z mode, the link speed is fixed, dependent on the - * @state->interface. Duplex is negotiated, and pause is advertised - * according to @state->an_enabled, @state->pause and - * @state->advertising flags. Beware of MACs which only support full - * duplex at gigabit and higher speeds. + * @state->interface. Duplex and pause modes are negotiated via + * the in-band configuration word. Advertised pause modes are set + * according to the @state->an_enabled and @state->advertising + * flags. Beware of MACs which only support full duplex at gigabit + * and higher speeds. * * If in Cisco SGMII mode, the link speed and duplex mode are passed * in the serial bitstream 16-bit configuration word, and the MAC -- cgit v1.2.3 From 9c07ae6983d434841bb19bea0a75bd0fd925a75c Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 17 Feb 2020 13:49:38 +0800 Subject: clk: imx8mm: Add CLKO2 support Add CLKO2 support, which is useful for debugging purposes. Signed-off-by: Fabio Estevam Reviewed-by: Abel Vesa Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mm.c | 3 +++ include/dt-bindings/clock/imx8mm-clock.h | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 523a5b15ae7d..6dbdc1a1b5eb 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -285,6 +285,8 @@ static const char *imx8mm_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", } static const char *imx8mm_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "dummy", "sys_pll1_200m", "audio_pll2_out", "sys_pll2_500m", "vpu_pll", "sys_pll1_80m", }; +static const char *imx8mm_clko2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_400m", "sys_pll2_166m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "osc_32k", }; static struct clk_hw_onecell_data *clk_hw_data; static struct clk_hw **hws; @@ -511,6 +513,7 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) hws[IMX8MM_CLK_WDOG] = imx8m_clk_hw_composite("wdog", imx8mm_wdog_sels, base + 0xb900); hws[IMX8MM_CLK_WRCLK] = imx8m_clk_hw_composite("wrclk", imx8mm_wrclk_sels, base + 0xb980); hws[IMX8MM_CLK_CLKO1] = imx8m_clk_hw_composite("clko1", imx8mm_clko1_sels, base + 0xba00); + hws[IMX8MM_CLK_CLKO2] = imx8m_clk_hw_composite("clko2", imx8mm_clko2_sels, base + 0xba80); hws[IMX8MM_CLK_DSI_CORE] = imx8m_clk_hw_composite("dsi_core", imx8mm_dsi_core_sels, base + 0xbb00); hws[IMX8MM_CLK_DSI_PHY_REF] = imx8m_clk_hw_composite("dsi_phy_ref", imx8mm_dsi_phy_sels, base + 0xbb80); hws[IMX8MM_CLK_DSI_DBI] = imx8m_clk_hw_composite("dsi_dbi", imx8mm_dsi_dbi_sels, base + 0xbc00); diff --git a/include/dt-bindings/clock/imx8mm-clock.h b/include/dt-bindings/clock/imx8mm-clock.h index 038c28d349e8..dbfee6579d6c 100644 --- a/include/dt-bindings/clock/imx8mm-clock.h +++ b/include/dt-bindings/clock/imx8mm-clock.h @@ -270,6 +270,8 @@ #define IMX8MM_CLK_GPU3D_CORE 248 #define IMX8MM_CLK_GPU2D_CORE 249 -#define IMX8MM_CLK_END 250 +#define IMX8MM_CLK_CLKO2 250 + +#define IMX8MM_CLK_END 251 #endif -- cgit v1.2.3 From 5eb40257047fb11085d582b7b9ccd0bffe900726 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 17 Feb 2020 11:01:35 +0800 Subject: clk: imx8mn: Fix incorrect clock defines IMX8MN_CLK_I2C4 and IMX8MN_CLK_UART1's index definitions are incorrect, fix them. Fixes: 1e80936a42e1 ("dt-bindings: imx: Add clock binding doc for i.MX8MN") Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- include/dt-bindings/clock/imx8mn-clock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/clock/imx8mn-clock.h b/include/dt-bindings/clock/imx8mn-clock.h index 0f2b8423ce1d..65ac6eb6c733 100644 --- a/include/dt-bindings/clock/imx8mn-clock.h +++ b/include/dt-bindings/clock/imx8mn-clock.h @@ -122,8 +122,8 @@ #define IMX8MN_CLK_I2C1 105 #define IMX8MN_CLK_I2C2 106 #define IMX8MN_CLK_I2C3 107 -#define IMX8MN_CLK_I2C4 118 -#define IMX8MN_CLK_UART1 119 +#define IMX8MN_CLK_I2C4 108 +#define IMX8MN_CLK_UART1 109 #define IMX8MN_CLK_UART2 110 #define IMX8MN_CLK_UART3 111 #define IMX8MN_CLK_UART4 112 -- cgit v1.2.3 From e5377ab2882eeb10aa32044b1a8ea48b44c9db42 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 Jan 2020 23:24:06 -0800 Subject: dt-bindings: clock: tegra: Add IDs for OSC clocks Tegra has OSC, OSC_DIV2 and OSC_DIV4 clocks from OSC pads which are the possible parents of Tegra PMC clocks clk_out_1, clk_out_2, and clk_out_3 for Tegra30 through Tegra210. So, this patch adds ids for these clocks. Tested-by: Dmitry Osipenko Reviewed-by: Dmitry Osipenko Acked-by: Rob Herring Signed-off-by: Sowjanya Komatineni Signed-off-by: Thierry Reding --- include/dt-bindings/clock/tegra114-car.h | 4 +++- include/dt-bindings/clock/tegra124-car-common.h | 4 +++- include/dt-bindings/clock/tegra210-car.h | 4 +++- include/dt-bindings/clock/tegra30-car.h | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/clock/tegra114-car.h b/include/dt-bindings/clock/tegra114-car.h index bb5c2c999c05..df59aaf5bf34 100644 --- a/include/dt-bindings/clock/tegra114-car.h +++ b/include/dt-bindings/clock/tegra114-car.h @@ -228,6 +228,8 @@ #define TEGRA114_CLK_CLK_M 201 #define TEGRA114_CLK_CLK_M_DIV2 202 #define TEGRA114_CLK_CLK_M_DIV4 203 +#define TEGRA114_CLK_OSC_DIV2 202 +#define TEGRA114_CLK_OSC_DIV4 203 #define TEGRA114_CLK_PLL_REF 204 #define TEGRA114_CLK_PLL_C 205 #define TEGRA114_CLK_PLL_C_OUT1 206 @@ -274,7 +276,7 @@ #define TEGRA114_CLK_CLK_OUT_2 246 #define TEGRA114_CLK_CLK_OUT_3 247 #define TEGRA114_CLK_BLINK 248 -/* 249 */ +#define TEGRA114_CLK_OSC 249 /* 250 */ /* 251 */ #define TEGRA114_CLK_XUSB_HOST_SRC 252 diff --git a/include/dt-bindings/clock/tegra124-car-common.h b/include/dt-bindings/clock/tegra124-car-common.h index 0c4f5be0a742..2a9acd592bff 100644 --- a/include/dt-bindings/clock/tegra124-car-common.h +++ b/include/dt-bindings/clock/tegra124-car-common.h @@ -227,6 +227,8 @@ #define TEGRA124_CLK_CLK_M 201 #define TEGRA124_CLK_CLK_M_DIV2 202 #define TEGRA124_CLK_CLK_M_DIV4 203 +#define TEGRA124_CLK_OSC_DIV2 202 +#define TEGRA124_CLK_OSC_DIV4 203 #define TEGRA124_CLK_PLL_REF 204 #define TEGRA124_CLK_PLL_C 205 #define TEGRA124_CLK_PLL_C_OUT1 206 @@ -273,7 +275,7 @@ #define TEGRA124_CLK_CLK_OUT_2 246 #define TEGRA124_CLK_CLK_OUT_3 247 #define TEGRA124_CLK_BLINK 248 -/* 249 */ +#define TEGRA124_CLK_OSC 249 /* 250 */ /* 251 */ #define TEGRA124_CLK_XUSB_HOST_SRC 252 diff --git a/include/dt-bindings/clock/tegra210-car.h b/include/dt-bindings/clock/tegra210-car.h index 44f60623f99b..7a8f10b9a66d 100644 --- a/include/dt-bindings/clock/tegra210-car.h +++ b/include/dt-bindings/clock/tegra210-car.h @@ -262,6 +262,8 @@ #define TEGRA210_CLK_CLK_M 233 #define TEGRA210_CLK_CLK_M_DIV2 234 #define TEGRA210_CLK_CLK_M_DIV4 235 +#define TEGRA210_CLK_OSC_DIV2 234 +#define TEGRA210_CLK_OSC_DIV4 235 #define TEGRA210_CLK_PLL_REF 236 #define TEGRA210_CLK_PLL_C 237 #define TEGRA210_CLK_PLL_C_OUT1 238 @@ -355,7 +357,7 @@ #define TEGRA210_CLK_PLL_A_OUT_ADSP 323 #define TEGRA210_CLK_PLL_A_OUT0_OUT_ADSP 324 /* 325 */ -/* 326 */ +#define TEGRA210_CLK_OSC 326 /* 327 */ /* 328 */ /* 329 */ diff --git a/include/dt-bindings/clock/tegra30-car.h b/include/dt-bindings/clock/tegra30-car.h index 3c90f1535551..7b542c10fc27 100644 --- a/include/dt-bindings/clock/tegra30-car.h +++ b/include/dt-bindings/clock/tegra30-car.h @@ -196,6 +196,8 @@ #define TEGRA30_CLK_CLK_M 171 #define TEGRA30_CLK_CLK_M_DIV2 172 #define TEGRA30_CLK_CLK_M_DIV4 173 +#define TEGRA30_CLK_OSC_DIV2 172 +#define TEGRA30_CLK_OSC_DIV4 173 #define TEGRA30_CLK_PLL_REF 174 #define TEGRA30_CLK_PLL_C 175 #define TEGRA30_CLK_PLL_C_OUT1 176 @@ -243,7 +245,7 @@ #define TEGRA30_CLK_HCLK 217 #define TEGRA30_CLK_PCLK 218 /* 219 */ -/* 220 */ +#define TEGRA30_CLK_OSC 220 /* 221 */ /* 222 */ /* 223 */ -- cgit v1.2.3 From f85fa3198dfc4e359cc6efa58854853b0824bae8 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 Jan 2020 23:24:12 -0800 Subject: dt-bindings: soc: tegra-pmc: Add Tegra PMC clock bindings Tegra PMC has 3 clocks clk_out_1, clk_out_2, and clk_out_3. This patch documents PMC clock bindings and adds a header defining Tegra PMC clock ids. Tested-by: Dmitry Osipenko Reviewed-by: Dmitry Osipenko Reviewed-by: Rob Herring Signed-off-by: Sowjanya Komatineni Signed-off-by: Thierry Reding --- .../devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml | 12 ++++++++++++ include/dt-bindings/soc/tegra-pmc.h | 15 +++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 include/dt-bindings/soc/tegra-pmc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml index 3ff34b348141..5b5c42a00264 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml @@ -40,6 +40,15 @@ properties: Must contain an entry for each entry in clock-names. See ../clocks/clocks-bindings.txt for details. + '#clock-cells': + const: 1 + description: + Tegra PMC has clk_out_1, clk_out_2, and clk_out_3. + Consumer of PMC clock should specify the desired clock by having + the clock ID in its "clocks" phandle cell with pmc clock provider. + See include/dt-bindings/soc/tegra-pmc.h for the list of Tegra PMC + clock IDs. + '#interrupt-cells': const: 2 description: @@ -296,6 +305,7 @@ required: - reg - clock-names - clocks + - '#clock-cells' dependencies: "nvidia,suspend-mode": ["nvidia,core-pwr-off-time", "nvidia,cpu-pwr-off-time"] @@ -307,12 +317,14 @@ examples: #include #include + #include tegra_pmc: pmc@7000e400 { compatible = "nvidia,tegra210-pmc"; reg = <0x0 0x7000e400 0x0 0x400>; clocks = <&tegra_car TEGRA210_CLK_PCLK>, <&clk32k_in>; clock-names = "pclk", "clk32k_in"; + #clock-cells = <1>; nvidia,invert-interrupt; nvidia,suspend-mode = <0>; diff --git a/include/dt-bindings/soc/tegra-pmc.h b/include/dt-bindings/soc/tegra-pmc.h new file mode 100644 index 000000000000..f7c866404456 --- /dev/null +++ b/include/dt-bindings/soc/tegra-pmc.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. + */ + +#ifndef _DT_BINDINGS_SOC_TEGRA_PMC_H +#define _DT_BINDINGS_SOC_TEGRA_PMC_H + +#define TEGRA_PMC_CLK_OUT_1 0 +#define TEGRA_PMC_CLK_OUT_2 1 +#define TEGRA_PMC_CLK_OUT_3 2 + +#define TEGRA_PMC_CLK_MAX 3 + +#endif /* _DT_BINDINGS_SOC_TEGRA_PMC_H */ -- cgit v1.2.3 From cd88f16792011a90aa9cda12233f136a528acab3 Mon Sep 17 00:00:00 2001 From: Sowjanya Komatineni Date: Mon, 13 Jan 2020 23:24:14 -0800 Subject: dt-bindings: soc: tegra-pmc: Add ID for Tegra PMC 32 kHz blink clock Tegra PMC has blink functionality that allows 32 kHz clock out to blink pin of the Tegra. This patch adds id for this blink clock to use for enabling or disabling blink output through device tree. Tested-by: Dmitry Osipenko Reviewed-by: Dmitry Osipenko Acked-by: Rob Herring Signed-off-by: Sowjanya Komatineni Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml | 2 ++ include/dt-bindings/soc/tegra-pmc.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml index 5b5c42a00264..f17bb353f65e 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml @@ -44,6 +44,8 @@ properties: const: 1 description: Tegra PMC has clk_out_1, clk_out_2, and clk_out_3. + PMC also has blink control which allows 32Khz clock output to + Tegra blink pad. Consumer of PMC clock should specify the desired clock by having the clock ID in its "clocks" phandle cell with pmc clock provider. See include/dt-bindings/soc/tegra-pmc.h for the list of Tegra PMC diff --git a/include/dt-bindings/soc/tegra-pmc.h b/include/dt-bindings/soc/tegra-pmc.h index f7c866404456..a99a457471ee 100644 --- a/include/dt-bindings/soc/tegra-pmc.h +++ b/include/dt-bindings/soc/tegra-pmc.h @@ -9,7 +9,8 @@ #define TEGRA_PMC_CLK_OUT_1 0 #define TEGRA_PMC_CLK_OUT_2 1 #define TEGRA_PMC_CLK_OUT_3 2 +#define TEGRA_PMC_CLK_BLINK 3 -#define TEGRA_PMC_CLK_MAX 3 +#define TEGRA_PMC_CLK_MAX 4 #endif /* _DT_BINDINGS_SOC_TEGRA_PMC_H */ -- cgit v1.2.3 From 672ef0e5684048a00aeb923b10131275ea688543 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:02:54 +0000 Subject: EDAC: Store error type in struct edac_raw_error_desc Store the error type in struct edac_raw_error_desc. This makes the type parameter of edac_raw_mc_handle_error() obsolete. [ kernel-doc typo ] Reported-by: kbuild test robot Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Reviewed-by: Mauro Carvalho Chehab Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-4-rrichter@marvell.com --- drivers/edac/edac_mc.c | 10 +++++----- drivers/edac/edac_mc.h | 4 +--- drivers/edac/ghes_edac.c | 11 +++++------ include/linux/edac.h | 2 ++ 4 files changed, 13 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 25130789e996..8d8c0997d41a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -1084,8 +1084,7 @@ static void edac_ue_error(struct mem_ctl_info *mci, edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); } -void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, - struct mem_ctl_info *mci, +void edac_raw_mc_handle_error(struct mem_ctl_info *mci, struct edac_raw_error_desc *e) { char detail[80]; @@ -1100,14 +1099,14 @@ void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, /* Report the error via the trace interface */ if (IS_ENABLED(CONFIG_RAS)) - trace_mc_event(type, e->msg, e->label, e->error_count, + trace_mc_event(e->type, e->msg, e->label, e->error_count, mci->mc_idx, e->top_layer, e->mid_layer, e->low_layer, (e->page_frame_number << PAGE_SHIFT) | e->offset_in_page, grain_bits, e->syndrome, e->other_detail); /* Memory type dependent details about the error */ - if (type == HW_EVENT_ERR_CORRECTED) { + if (e->type == HW_EVENT_ERR_CORRECTED) { snprintf(detail, sizeof(detail), "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", e->page_frame_number, e->offset_in_page, @@ -1152,6 +1151,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, /* Fills the error report buffer */ memset(e, 0, sizeof (*e)); e->error_count = error_count; + e->type = type; e->top_layer = top_layer; e->mid_layer = mid_layer; e->low_layer = low_layer; @@ -1282,6 +1282,6 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, if (p > e->location) *(p - 1) = '\0'; - edac_raw_mc_handle_error(type, mci, e); + edac_raw_mc_handle_error(mci, e); } EXPORT_SYMBOL_GPL(edac_mc_handle_error); diff --git a/drivers/edac/edac_mc.h b/drivers/edac/edac_mc.h index 02aac5c61d00..5d78be774f9e 100644 --- a/drivers/edac/edac_mc.h +++ b/drivers/edac/edac_mc.h @@ -212,7 +212,6 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, * edac_raw_mc_handle_error() - Reports a memory event to userspace without * doing anything to discover the error location. * - * @type: severity of the error (CE/UE/Fatal) * @mci: a struct mem_ctl_info pointer * @e: error description * @@ -220,8 +219,7 @@ extern int edac_mc_find_csrow_by_page(struct mem_ctl_info *mci, * only be called directly when the hardware error come directly from BIOS, * like in the case of APEI GHES driver. */ -void edac_raw_mc_handle_error(const enum hw_event_mc_err_type type, - struct mem_ctl_info *mci, +void edac_raw_mc_handle_error(struct mem_ctl_info *mci, struct edac_raw_error_desc *e); /** diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index b99080d8a10c..7c3e5264a41e 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -201,7 +201,6 @@ static void ghes_edac_dmidecode(const struct dmi_header *dh, void *arg) void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) { - enum hw_event_mc_err_type type; struct edac_raw_error_desc *e; struct mem_ctl_info *mci; struct ghes_edac_pvt *pvt; @@ -240,17 +239,17 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) switch (sev) { case GHES_SEV_CORRECTED: - type = HW_EVENT_ERR_CORRECTED; + e->type = HW_EVENT_ERR_CORRECTED; break; case GHES_SEV_RECOVERABLE: - type = HW_EVENT_ERR_UNCORRECTED; + e->type = HW_EVENT_ERR_UNCORRECTED; break; case GHES_SEV_PANIC: - type = HW_EVENT_ERR_FATAL; + e->type = HW_EVENT_ERR_FATAL; break; default: case GHES_SEV_NO: - type = HW_EVENT_ERR_INFO; + e->type = HW_EVENT_ERR_INFO; } edac_dbg(1, "error validation_bits: 0x%08llx\n", @@ -442,7 +441,7 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) if (p > pvt->other_detail) *(p - 1) = '\0'; - edac_raw_mc_handle_error(type, mci, e); + edac_raw_mc_handle_error(mci, e); unlock: spin_unlock_irqrestore(&ghes_lock, flags); diff --git a/include/linux/edac.h b/include/linux/edac.h index cc31b9742684..6703eb492cd2 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -442,6 +442,7 @@ struct errcount_attribute_data { * struct edac_raw_error_desc - Raw error report structure * @grain: minimum granularity for an error report, in bytes * @error_count: number of errors of the same type + * @type: severity of the error (CE/UE/Fatal) * @top_layer: top layer of the error (layer[0]) * @mid_layer: middle layer of the error (layer[1]) * @low_layer: low layer of the error (layer[2]) @@ -462,6 +463,7 @@ struct edac_raw_error_desc { long grain; u16 error_count; + enum hw_event_mc_err_type type; int top_layer; int mid_layer; int low_layer; -- cgit v1.2.3 From 67792cf9583c7816667c6b90007b5840f1b471f4 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 23 Jan 2020 09:03:02 +0000 Subject: EDAC/mc: Remove enable_per_layer_report function argument Many functions carry the enable_per_layer_report argument. This is a bool value indicating the error information contains some location data where the error occurred. This can easily being determined by checking the pos[] array for values. Negative values indicate there is no location available. So if the top layer is negative, the error location is unknown. Just check if the top layer is negative and remove enable_per_layer_report as function argument and also from struct edac_raw_error_desc. [ bp: Reflow comments to 80 columns, while at it. ] Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-8-rrichter@marvell.com --- drivers/edac/edac_mc.c | 40 ++++++++++++++++++---------------------- drivers/edac/ghes_edac.c | 5 +---- include/linux/edac.h | 3 --- 3 files changed, 19 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 7a1445feb59d..1867f11a3438 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -946,7 +946,6 @@ const char *edac_layer_name[] = { EXPORT_SYMBOL_GPL(edac_layer_name); static void edac_inc_ce_error(struct mem_ctl_info *mci, - bool enable_per_layer_report, const int pos[EDAC_MAX_LAYERS], const u16 count) { @@ -954,7 +953,7 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, mci->ce_mc += count; - if (!enable_per_layer_report) { + if (pos[0] < 0) { mci->ce_noinfo_count += count; return; } @@ -971,7 +970,6 @@ static void edac_inc_ce_error(struct mem_ctl_info *mci, } static void edac_inc_ue_error(struct mem_ctl_info *mci, - bool enable_per_layer_report, const int pos[EDAC_MAX_LAYERS], const u16 count) { @@ -979,7 +977,7 @@ static void edac_inc_ue_error(struct mem_ctl_info *mci, mci->ue_mc += count; - if (!enable_per_layer_report) { + if (pos[0] < 0) { mci->ue_noinfo_count += count; return; } @@ -1003,7 +1001,6 @@ static void edac_ce_error(struct mem_ctl_info *mci, const char *label, const char *detail, const char *other_detail, - const bool enable_per_layer_report, const unsigned long page_frame_number, const unsigned long offset_in_page, long grain) @@ -1026,7 +1023,7 @@ static void edac_ce_error(struct mem_ctl_info *mci, error_count, msg, msg_aux, label, location, detail); } - edac_inc_ce_error(mci, enable_per_layer_report, pos, error_count); + edac_inc_ce_error(mci, pos, error_count); if (mci->scrub_mode == SCRUB_SW_SRC) { /* @@ -1056,8 +1053,7 @@ static void edac_ue_error(struct mem_ctl_info *mci, const char *location, const char *label, const char *detail, - const char *other_detail, - const bool enable_per_layer_report) + const char *other_detail) { char *msg_aux = ""; @@ -1086,7 +1082,7 @@ static void edac_ue_error(struct mem_ctl_info *mci, msg, msg_aux, label, location, detail); } - edac_inc_ue_error(mci, enable_per_layer_report, pos, error_count); + edac_inc_ue_error(mci, pos, error_count); } static void edac_inc_csrow(struct edac_raw_error_desc *e, int row, int chan) @@ -1136,16 +1132,16 @@ void edac_raw_mc_handle_error(struct edac_raw_error_desc *e) "page:0x%lx offset:0x%lx grain:%ld syndrome:0x%lx", e->page_frame_number, e->offset_in_page, e->grain, e->syndrome); - edac_ce_error(mci, e->error_count, pos, e->msg, e->location, e->label, - detail, e->other_detail, e->enable_per_layer_report, + edac_ce_error(mci, e->error_count, pos, e->msg, e->location, + e->label, detail, e->other_detail, e->page_frame_number, e->offset_in_page, e->grain); } else { snprintf(detail, sizeof(detail), "page:0x%lx offset:0x%lx grain:%ld", e->page_frame_number, e->offset_in_page, e->grain); - edac_ue_error(mci, e->error_count, pos, e->msg, e->location, e->label, - detail, e->other_detail, e->enable_per_layer_report); + edac_ue_error(mci, e->error_count, pos, e->msg, e->location, + e->label, detail, e->other_detail); } @@ -1170,6 +1166,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, int pos[EDAC_MAX_LAYERS] = { top_layer, mid_layer, low_layer }; int i, n_labels = 0; struct edac_raw_error_desc *e = &mci->error_desc; + bool any_memory = true; edac_dbg(3, "MC%d\n", mci->mc_idx); @@ -1187,10 +1184,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, e->other_detail = other_detail; /* - * Check if the event report is consistent and if the memory - * location is known. If it is known, enable_per_layer_report will be - * true, the DIMM(s) label info will be filled and the per-layer - * error counters will be incremented. + * Check if the event report is consistent and if the memory location is + * known. If it is, the DIMM(s) label info will be filled and the + * per-layer error counters will be incremented. */ for (i = 0; i < mci->n_layers; i++) { if (pos[i] >= (int)mci->layers[i].size) { @@ -1208,7 +1204,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, pos[i] = -1; } if (pos[i] >= 0) - e->enable_per_layer_report = true; + any_memory = false; } /* @@ -1239,9 +1235,9 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, /* * If the error is memory-controller wide, there's no need to - * seek for the affected DIMMs because the whole - * channel/memory controller/... may be affected. - * Also, don't show errors for empty DIMM slots. + * seek for the affected DIMMs because the whole channel/memory + * controller/... may be affected. Also, don't show errors for + * empty DIMM slots. */ if (!dimm->nr_pages) continue; @@ -1277,7 +1273,7 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, chan = -2; } - if (!e->enable_per_layer_report) + if (any_memory) strcpy(e->label, "any memory"); else if (!*e->label) strcpy(e->label, "unknown memory"); diff --git a/drivers/edac/ghes_edac.c b/drivers/edac/ghes_edac.c index bef8a428c429..cb3dab56a875 100644 --- a/drivers/edac/ghes_edac.c +++ b/drivers/edac/ghes_edac.c @@ -355,11 +355,8 @@ void ghes_edac_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) mem_err->mem_dev_handle); index = get_dimm_smbios_index(mci, mem_err->mem_dev_handle); - if (index >= 0) { + if (index >= 0) e->top_layer = index; - e->enable_per_layer_report = true; - } - } if (p > e->location) *(p - 1) = '\0'; diff --git a/include/linux/edac.h b/include/linux/edac.h index 6703eb492cd2..815f246e0abd 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -454,8 +454,6 @@ struct errcount_attribute_data { * @location: location of the error * @label: label of the affected DIMM(s) * @other_detail: other driver-specific detail about the error - * @enable_per_layer_report: if false, the error affects all layers - * (typically, a memory controller error) */ struct edac_raw_error_desc { char location[LOCATION_SIZE]; @@ -472,7 +470,6 @@ struct edac_raw_error_desc { unsigned long syndrome; const char *msg; const char *other_detail; - bool enable_per_layer_report; }; /* MEMORY controller information structure -- cgit v1.2.3 From 4aa92c86463273b673e4170c60cb78e2625781eb Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Mon, 17 Feb 2020 12:30:23 +0100 Subject: EDAC/mc: Remove per layer counters Looking at how mci->{ue,ce}_per_layer[EDAC_MAX_LAYERS] is used, it turns out that only the leaves in the memory hierarchy are consumed (in sysfs), but not the intermediate layers, e.g.: count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][dimm->idx]; These unused counters only add complexity, remove them. The error counter values are directly stored in struct dimm_info now. Signed-off-by: Robert Richter Signed-off-by: Borislav Petkov Acked-by: Aristeu Rozanski Link: https://lkml.kernel.org/r/20200123090210.26933-11-rrichter@marvell.com --- drivers/edac/edac_mc.c | 67 +++++++++++--------------------------------- drivers/edac/edac_mc_sysfs.c | 20 ++++++------- include/linux/edac.h | 4 ++- 3 files changed, 27 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 80629c702a4f..75ede27bdf6a 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c @@ -451,11 +451,9 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, { struct mem_ctl_info *mci; struct edac_mc_layer *layer; - u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; - unsigned int idx, size, tot_dimms = 1, count = 1; - unsigned int tot_csrows = 1, tot_channels = 1, tot_errcount = 0; + unsigned int idx, size, tot_dimms = 1; + unsigned int tot_csrows = 1, tot_channels = 1; void *pvt, *ptr = NULL; - int i; bool per_rank = false; if (WARN_ON(n_layers > EDAC_MAX_LAYERS || n_layers == 0)) @@ -482,19 +480,10 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, * stringent as what the compiler would provide if we could simply * hardcode everything into a single struct. */ - mci = edac_align_ptr(&ptr, sizeof(*mci), 1); - layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); - for (i = 0; i < n_layers; i++) { - count *= layers[i].size; - edac_dbg(4, "errcount layer %d size %d\n", i, count); - ce_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); - ue_per_layer[i] = edac_align_ptr(&ptr, sizeof(u32), count); - tot_errcount += 2 * count; - } - - edac_dbg(4, "allocating %d error counters\n", tot_errcount); - pvt = edac_align_ptr(&ptr, sz_pvt, 1); - size = ((unsigned long)pvt) + sz_pvt; + mci = edac_align_ptr(&ptr, sizeof(*mci), 1); + layer = edac_align_ptr(&ptr, sizeof(*layer), n_layers); + pvt = edac_align_ptr(&ptr, sz_pvt, 1); + size = ((unsigned long)pvt) + sz_pvt; edac_dbg(1, "allocating %u bytes for mci data (%d %s, %d csrows/channels)\n", size, @@ -513,10 +502,6 @@ struct mem_ctl_info *edac_mc_alloc(unsigned int mc_num, * rather than an imaginary chunk of memory located at address 0. */ layer = (struct edac_mc_layer *)(((char *)mci) + ((unsigned long)layer)); - for (i = 0; i < n_layers; i++) { - mci->ce_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ce_per_layer[i])); - mci->ue_per_layer[i] = (u32 *)((char *)mci + ((unsigned long)ue_per_layer[i])); - } pvt = sz_pvt ? (((char *)mci) + ((unsigned long)pvt)) : NULL; /* setup index and various internal pointers */ @@ -949,48 +934,28 @@ static void edac_inc_ce_error(struct edac_raw_error_desc *e) { int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; struct mem_ctl_info *mci = error_desc_to_mci(e); - int i, index = 0; + struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]); mci->ce_mc += e->error_count; - if (pos[0] < 0) { + if (dimm) + dimm->ce_count += e->error_count; + else mci->ce_noinfo_count += e->error_count; - return; - } - - for (i = 0; i < mci->n_layers; i++) { - if (pos[i] < 0) - break; - index += pos[i]; - mci->ce_per_layer[i][index] += e->error_count; - - if (i < mci->n_layers - 1) - index *= mci->layers[i + 1].size; - } } static void edac_inc_ue_error(struct edac_raw_error_desc *e) { int pos[EDAC_MAX_LAYERS] = { e->top_layer, e->mid_layer, e->low_layer }; struct mem_ctl_info *mci = error_desc_to_mci(e); - int i, index = 0; + struct dimm_info *dimm = edac_get_dimm(mci, pos[0], pos[1], pos[2]); mci->ue_mc += e->error_count; - if (pos[0] < 0) { + if (dimm) + dimm->ue_count += e->error_count; + else mci->ue_noinfo_count += e->error_count; - return; - } - - for (i = 0; i < mci->n_layers; i++) { - if (pos[i] < 0) - break; - index += pos[i]; - mci->ue_per_layer[i][index] += e->error_count; - - if (i < mci->n_layers - 1) - index *= mci->layers[i + 1].size; - } } static void edac_ce_error(struct edac_raw_error_desc *e) @@ -1143,8 +1108,8 @@ void edac_mc_handle_error(const enum hw_event_mc_err_type type, /* * Check if the event report is consistent and if the memory location is - * known. If it is, the DIMM(s) label info will be filled and the - * per-layer error counters will be incremented. + * known. If it is, the DIMM(s) label info will be filled and the DIMM's + * error counters will be incremented. */ for (i = 0; i < mci->n_layers; i++) { if (pos[i] >= (int)mci->layers[i].size) { diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 11e1b436f116..4e6aca595133 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -551,10 +551,8 @@ static ssize_t dimmdev_ce_count_show(struct device *dev, char *data) { struct dimm_info *dimm = to_dimm(dev); - u32 count; - count = dimm->mci->ce_per_layer[dimm->mci->n_layers-1][dimm->idx]; - return sprintf(data, "%u\n", count); + return sprintf(data, "%u\n", dimm->ce_count); } static ssize_t dimmdev_ue_count_show(struct device *dev, @@ -562,10 +560,8 @@ static ssize_t dimmdev_ue_count_show(struct device *dev, char *data) { struct dimm_info *dimm = to_dimm(dev); - u32 count; - count = dimm->mci->ue_per_layer[dimm->mci->n_layers-1][dimm->idx]; - return sprintf(data, "%u\n", count); + return sprintf(data, "%u\n", dimm->ue_count); } /* dimm/rank attribute files */ @@ -661,7 +657,9 @@ static ssize_t mci_reset_counters_store(struct device *dev, const char *data, size_t count) { struct mem_ctl_info *mci = to_mci(dev); - int cnt, row, chan, i; + struct dimm_info *dimm; + int row, chan; + mci->ue_mc = 0; mci->ce_mc = 0; mci->ue_noinfo_count = 0; @@ -677,11 +675,9 @@ static ssize_t mci_reset_counters_store(struct device *dev, ri->channels[chan]->ce_count = 0; } - cnt = 1; - for (i = 0; i < mci->n_layers; i++) { - cnt *= mci->layers[i].size; - memset(mci->ce_per_layer[i], 0, cnt * sizeof(u32)); - memset(mci->ue_per_layer[i], 0, cnt * sizeof(u32)); + mci_for_each_dimm(mci, dimm) { + dimm->ue_count = 0; + dimm->ce_count = 0; } mci->start_time = jiffies; diff --git a/include/linux/edac.h b/include/linux/edac.h index 815f246e0abd..0f20b986b0ab 100644 --- a/include/linux/edac.h +++ b/include/linux/edac.h @@ -383,6 +383,9 @@ struct dimm_info { unsigned int csrow, cschannel; /* Points to the old API data */ u16 smbios_handle; /* Handle for SMBIOS type 17 */ + + u32 ce_count; + u32 ue_count; }; /** @@ -559,7 +562,6 @@ struct mem_ctl_info { */ u32 ce_noinfo_count, ue_noinfo_count; u32 ue_mc, ce_mc; - u32 *ce_per_layer[EDAC_MAX_LAYERS], *ue_per_layer[EDAC_MAX_LAYERS]; struct completion complete; -- cgit v1.2.3 From 3bd142a46b561a12408e8db78cc6d62eb1c6b84e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:53 +0100 Subject: clocksource: Cleanup struct clocksource and documentation Reformat the struct definition, add missing member documentation. No functional change. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124402.825471920@linutronix.de --- include/linux/clocksource.h | 87 ++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index b21db536fd52..2c4574b517d2 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -32,9 +32,19 @@ struct module; * Provides mostly state-free accessors to the underlying hardware. * This is the structure used for system time. * - * @name: ptr to clocksource name - * @list: list head for registration - * @rating: rating value for selection (higher is better) + * @read: Returns a cycle value, passes clocksource as argument + * @mask: Bitmask for two's complement + * subtraction of non 64 bit counters + * @mult: Cycle to nanosecond multiplier + * @shift: Cycle to nanosecond divisor (power of two) + * @max_idle_ns: Maximum idle time permitted by the clocksource (nsecs) + * @maxadj: Maximum adjustment value to mult (~11%) + * @archdata: Optional arch-specific data + * @max_cycles: Maximum safe cycle value which won't overflow on + * multiplication + * @name: Pointer to clocksource name + * @list: List head for registration (internal) + * @rating: Rating value for selection (higher is better) * To avoid rating inflation the following * list should give you a guide as to how * to assign your clocksource a rating @@ -49,27 +59,23 @@ struct module; * 400-499: Perfect * The ideal clocksource. A must-use where * available. - * @read: returns a cycle value, passes clocksource as argument - * @enable: optional function to enable the clocksource - * @disable: optional function to disable the clocksource - * @mask: bitmask for two's complement - * subtraction of non 64 bit counters - * @mult: cycle to nanosecond multiplier - * @shift: cycle to nanosecond divisor (power of two) - * @max_idle_ns: max idle time permitted by the clocksource (nsecs) - * @maxadj: maximum adjustment value to mult (~11%) - * @max_cycles: maximum safe cycle value which won't overflow on multiplication - * @flags: flags describing special properties - * @archdata: arch-specific data - * @suspend: suspend function for the clocksource, if necessary - * @resume: resume function for the clocksource, if necessary + * @flags: Flags describing special properties + * @enable: Optional function to enable the clocksource + * @disable: Optional function to disable the clocksource + * @suspend: Optional suspend function for the clocksource + * @resume: Optional resume function for the clocksource * @mark_unstable: Optional function to inform the clocksource driver that * the watchdog marked the clocksource unstable - * @owner: module reference, must be set by clocksource in modules + * @tick_stable: Optional function called periodically from the watchdog + * code to provide stable syncrhonization points + * @wd_list: List head to enqueue into the watchdog list (internal) + * @cs_last: Last clocksource value for clocksource watchdog + * @wd_last: Last watchdog value corresponding to @cs_last + * @owner: Module reference, must be set by clocksource in modules * * Note: This struct is not used in hotpathes of the timekeeping code * because the timekeeper caches the hot path fields in its own data - * structure, so no line cache alignment is required, + * structure, so no cache line alignment is required, * * The pointer to the clocksource itself is handed to the read * callback. If you need extra information there you can wrap struct @@ -78,35 +84,36 @@ struct module; * structure. */ struct clocksource { - u64 (*read)(struct clocksource *cs); - u64 mask; - u32 mult; - u32 shift; - u64 max_idle_ns; - u32 maxadj; + u64 (*read)(struct clocksource *cs); + u64 mask; + u32 mult; + u32 shift; + u64 max_idle_ns; + u32 maxadj; #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA struct arch_clocksource_data archdata; #endif - u64 max_cycles; - const char *name; - struct list_head list; - int rating; - int (*enable)(struct clocksource *cs); - void (*disable)(struct clocksource *cs); - unsigned long flags; - void (*suspend)(struct clocksource *cs); - void (*resume)(struct clocksource *cs); - void (*mark_unstable)(struct clocksource *cs); - void (*tick_stable)(struct clocksource *cs); + u64 max_cycles; + const char *name; + struct list_head list; + int rating; + unsigned long flags; + + int (*enable)(struct clocksource *cs); + void (*disable)(struct clocksource *cs); + void (*suspend)(struct clocksource *cs); + void (*resume)(struct clocksource *cs); + void (*mark_unstable)(struct clocksource *cs); + void (*tick_stable)(struct clocksource *cs); /* private: */ #ifdef CONFIG_CLOCKSOURCE_WATCHDOG /* Watchdog related data, used by the framework */ - struct list_head wd_list; - u64 cs_last; - u64 wd_last; + struct list_head wd_list; + u64 cs_last; + u64 wd_last; #endif - struct module *owner; + struct module *owner; }; /* -- cgit v1.2.3 From 5d51bee725cc1497352d6b0b604e42a90c680540 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:55 +0100 Subject: clocksource: Add common vdso clock mode storage All architectures which use the generic VDSO code have their own storage for the VDSO clock mode. That's pointless and just requires duplicate code. Provide generic storage for it. The new Kconfig symbol is intermediate and will be removed once all architectures are converted over. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.028046322@linutronix.de --- include/linux/clocksource.h | 12 +++++++++++- kernel/time/clocksource.c | 9 +++++++++ kernel/time/vsyscall.c | 10 ++++++++-- lib/vdso/Kconfig | 3 +++ lib/vdso/gettimeofday.c | 13 +++++++++++-- 5 files changed, 42 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 2c4574b517d2..6d5ed1b4d24d 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -23,10 +23,19 @@ struct clocksource; struct module; -#ifdef CONFIG_ARCH_CLOCKSOURCE_DATA +#if defined(CONFIG_ARCH_CLOCKSOURCE_DATA) || \ + defined(CONFIG_GENERIC_VDSO_CLOCK_MODE) #include #endif +enum vdso_clock_mode { + VDSO_CLOCKMODE_NONE, +#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE + VDSO_ARCH_CLOCKMODES, +#endif + VDSO_CLOCKMODE_MAX, +}; + /** * struct clocksource - hardware abstraction for a free running counter * Provides mostly state-free accessors to the underlying hardware. @@ -97,6 +106,7 @@ struct clocksource { const char *name; struct list_head list; int rating; + enum vdso_clock_mode vdso_clock_mode; unsigned long flags; int (*enable)(struct clocksource *cs); diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index 428beb69426a..7cb09c4cf21c 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -928,6 +928,15 @@ int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq) clocksource_arch_init(cs); +#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE + if (cs->vdso_clock_mode < 0 || + cs->vdso_clock_mode >= VDSO_CLOCKMODE_MAX) { + pr_warn("clocksource %s registered with invalid VDSO mode %d. Disabling VDSO support.\n", + cs->name, cs->vdso_clock_mode); + cs->vdso_clock_mode = VDSO_CLOCKMODE_NONE; + } +#endif + /* Initialize mult/shift and max_idle_ns */ __clocksource_update_freq_scale(cs, scale, freq); diff --git a/kernel/time/vsyscall.c b/kernel/time/vsyscall.c index 9577c89179cd..f9a5178c69bb 100644 --- a/kernel/time/vsyscall.c +++ b/kernel/time/vsyscall.c @@ -71,13 +71,19 @@ void update_vsyscall(struct timekeeper *tk) { struct vdso_data *vdata = __arch_get_k_vdso_data(); struct vdso_timestamp *vdso_ts; + s32 clock_mode; u64 nsec; /* copy vsyscall data */ vdso_write_begin(vdata); - vdata[CS_HRES_COARSE].clock_mode = __arch_get_clock_mode(tk); - vdata[CS_RAW].clock_mode = __arch_get_clock_mode(tk); +#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE + clock_mode = tk->tkr_mono.clock->vdso_clock_mode; +#else + clock_mode = __arch_get_clock_mode(tk); +#endif + vdata[CS_HRES_COARSE].clock_mode = clock_mode; + vdata[CS_RAW].clock_mode = clock_mode; /* CLOCK_REALTIME also required for time() */ vdso_ts = &vdata[CS_HRES_COARSE].basetime[CLOCK_REALTIME]; diff --git a/lib/vdso/Kconfig b/lib/vdso/Kconfig index d883ac299508..d9f43c84fcc6 100644 --- a/lib/vdso/Kconfig +++ b/lib/vdso/Kconfig @@ -30,4 +30,7 @@ config GENERIC_VDSO_TIME_NS Selected by architectures which support time namespaces in the VDSO +config GENERIC_VDSO_CLOCK_MODE + bool + endif diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 5804e4e168e7..3f2d8b859130 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -64,10 +65,14 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, do { seq = vdso_read_begin(vd); + if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + vd->clock_mode == VDSO_CLOCKMODE_NONE) + return -1; cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (unlikely((s64)cycles < 0)) + if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + unlikely((s64)cycles < 0)) return -1; ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); @@ -132,10 +137,14 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, } smp_rmb(); + if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + vd->clock_mode == VDSO_CLOCKMODE_NONE) + return -1; cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (unlikely((s64)cycles < 0)) + if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && + unlikely((s64)cycles < 0)) return -1; ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); -- cgit v1.2.3 From 10fa9512769fa3b15ea29f4f331f4604c17b4b2c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Feb 2020 12:20:58 +0100 Subject: usb: audio-v2: Add uac2_effect_unit_descriptor definition The UAC2 Effect Unit Descriptor has a slightly different definition from other similar ones like Processing Unit or Extension Unit. Define it here so that it can be used in USB-audio driver in a later patch. Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20200213112059.18745-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- include/linux/usb/audio-v2.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ba4b3e3327ff..cb9900b34b67 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -156,6 +156,18 @@ struct uac2_feature_unit_descriptor { __u8 bmaControls[0]; /* variable length */ } __attribute__((packed)); +/* 4.7.2.10 Effect Unit Descriptor */ + +struct uac2_effect_unit_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDescriptorSubtype; + __u8 bUnitID; + __le16 wEffectType; + __u8 bSourceID; + __u8 bmaControls[]; /* variable length */ +} __attribute__((packed)); + /* 4.9.2 Class-Specific AS Interface Descriptor */ struct uac2_as_header_descriptor { -- cgit v1.2.3 From b2ca916ce392a9d4cea3489a3efb2b627b839eaf Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 16 Feb 2020 12:00:48 -0800 Subject: ACPI: NUMA: Up-level "map to online node" functionality The acpi_map_pxm_to_online_node() helper is used to find the closest online node to a given proximity domain. This is used to map devices in a proximity domain with no online memory or cpus to the closest online node and populate a device's 'numa_node' property. The numa_node property allows applications to be migrated "close" to a resource. In preparation for providing a generic facility to optionally map an address range to its closest online node, or the node the range would represent were it to be onlined (target_node), up-level the core of acpi_map_pxm_to_online_node() to a generic mm/numa helper. Cc: Michal Hocko Acked-by: Rafael J. Wysocki Reviewed-by: Ingo Molnar Signed-off-by: Dan Williams Link: https://lore.kernel.org/r/158188324802.894464.13128795207831894206.stgit@dwillia2-desk3.amr.corp.intel.com --- drivers/acpi/numa/srat.c | 41 ----------------------------------------- include/linux/acpi.h | 23 ++++++++++++++++++++++- include/linux/numa.h | 9 +++++++++ mm/mempolicy.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/acpi/numa/srat.c b/drivers/acpi/numa/srat.c index eadbf90e65d1..47b4969d9b93 100644 --- a/drivers/acpi/numa/srat.c +++ b/drivers/acpi/numa/srat.c @@ -72,47 +72,6 @@ int acpi_map_pxm_to_node(int pxm) } EXPORT_SYMBOL(acpi_map_pxm_to_node); -/** - * acpi_map_pxm_to_online_node - Map proximity ID to online node - * @pxm: ACPI proximity ID - * - * This is similar to acpi_map_pxm_to_node(), but always returns an online - * node. When the mapped node from a given proximity ID is offline, it - * looks up the node distance table and returns the nearest online node. - * - * ACPI device drivers, which are called after the NUMA initialization has - * completed in the kernel, can call this interface to obtain their device - * NUMA topology from ACPI tables. Such drivers do not have to deal with - * offline nodes. A node may be offline when a device proximity ID is - * unique, SRAT memory entry does not exist, or NUMA is disabled, ex. - * "numa=off" on x86. - */ -int acpi_map_pxm_to_online_node(int pxm) -{ - int node, min_node; - - node = acpi_map_pxm_to_node(pxm); - - if (node == NUMA_NO_NODE) - node = 0; - - min_node = node; - if (!node_online(node)) { - int min_dist = INT_MAX, dist, n; - - for_each_online_node(n) { - dist = node_distance(node, n); - if (dist < min_dist) { - min_dist = dist; - min_node = n; - } - } - } - - return min_node; -} -EXPORT_SYMBOL(acpi_map_pxm_to_online_node); - static void __init acpi_table_print_srat_entry(struct acpi_subtable_header *header) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 0f24d701fbdc..3839363081f3 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -416,9 +416,30 @@ extern void acpi_osi_setup(char *str); extern bool acpi_osi_is_win8(void); #ifdef CONFIG_ACPI_NUMA -int acpi_map_pxm_to_online_node(int pxm); int acpi_map_pxm_to_node(int pxm); int acpi_get_node(acpi_handle handle); + +/** + * acpi_map_pxm_to_online_node - Map proximity ID to online node + * @pxm: ACPI proximity ID + * + * This is similar to acpi_map_pxm_to_node(), but always returns an online + * node. When the mapped node from a given proximity ID is offline, it + * looks up the node distance table and returns the nearest online node. + * + * ACPI device drivers, which are called after the NUMA initialization has + * completed in the kernel, can call this interface to obtain their device + * NUMA topology from ACPI tables. Such drivers do not have to deal with + * offline nodes. A node may be offline when a device proximity ID is + * unique, SRAT memory entry does not exist, or NUMA is disabled, ex. + * "numa=off" on x86. + */ +static inline int acpi_map_pxm_to_online_node(int pxm) +{ + int node = acpi_map_pxm_to_node(pxm); + + return numa_map_to_online_node(node); +} #else static inline int acpi_map_pxm_to_online_node(int pxm) { diff --git a/include/linux/numa.h b/include/linux/numa.h index 110b0e5d0fb0..20f4e44b186c 100644 --- a/include/linux/numa.h +++ b/include/linux/numa.h @@ -13,4 +13,13 @@ #define NUMA_NO_NODE (-1) +#ifdef CONFIG_NUMA +int numa_map_to_online_node(int node); +#else +static inline int numa_map_to_online_node(int node) +{ + return NUMA_NO_NODE; +} +#endif + #endif /* _LINUX_NUMA_H */ diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 977c641f78cf..756d6e5bb59f 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -127,6 +127,36 @@ static struct mempolicy default_policy = { static struct mempolicy preferred_node_policy[MAX_NUMNODES]; +/** + * numa_map_to_online_node - Find closest online node + * @nid: Node id to start the search + * + * Lookup the next closest node by distance if @nid is not online. + */ +int numa_map_to_online_node(int node) +{ + int min_node; + + if (node == NUMA_NO_NODE) + node = 0; + + min_node = node; + if (!node_online(node)) { + int min_dist = INT_MAX, dist, n; + + for_each_online_node(n) { + dist = node_distance(node, n); + if (dist < min_dist) { + min_dist = dist; + min_node = n; + } + } + } + + return min_node; +} +EXPORT_SYMBOL_GPL(numa_map_to_online_node); + struct mempolicy *get_task_policy(struct task_struct *p) { struct mempolicy *pol = p->mempolicy; -- cgit v1.2.3 From 1e5d8e1e47afde23e3249aed25d7d124feff5c1c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 16 Feb 2020 12:01:04 -0800 Subject: x86/mm: Introduce CONFIG_NUMA_KEEP_MEMINFO Currently x86 numa_meminfo is marked __initdata in the CONFIG_MEMORY_HOTPLUG=n case. In support of a new facility to allow drivers to map reserved memory to a 'target_node' (phys_to_target_node()), add support for removing the __initdata designation for those users. Both memory hotplug and phys_to_target_node() users select CONFIG_NUMA_KEEP_MEMINFO to tell the arch to maintain its physical address to NUMA mapping infrastructure post init. Cc: Dave Hansen Cc: Andy Lutomirski Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Cc: Andrew Morton Cc: David Hildenbrand Cc: Michal Hocko Reviewed-by: Ingo Molnar Signed-off-by: Dan Williams Reviewed-by: Thomas Gleixner Link: https://lore.kernel.org/r/158188326422.894464.15742054998046628934.stgit@dwillia2-desk3.amr.corp.intel.com --- arch/x86/mm/numa.c | 6 +----- include/linux/numa.h | 7 +++++++ mm/Kconfig | 5 +++++ 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index 99f7a68738f0..2450b21cc28a 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -25,11 +25,7 @@ nodemask_t numa_nodes_parsed __initdata; struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data); -static struct numa_meminfo numa_meminfo -#ifndef CONFIG_MEMORY_HOTPLUG -__initdata -#endif -; +static struct numa_meminfo numa_meminfo __initdata_or_meminfo; static int numa_distance_cnt; static u8 *numa_distance; diff --git a/include/linux/numa.h b/include/linux/numa.h index 20f4e44b186c..5773cd2613fc 100644 --- a/include/linux/numa.h +++ b/include/linux/numa.h @@ -13,6 +13,13 @@ #define NUMA_NO_NODE (-1) +/* optionally keep NUMA memory info available post init */ +#ifdef CONFIG_NUMA_KEEP_MEMINFO +#define __initdata_or_meminfo +#else +#define __initdata_or_meminfo __initdata +#endif + #ifdef CONFIG_NUMA int numa_map_to_online_node(int node); #else diff --git a/mm/Kconfig b/mm/Kconfig index ab80933be65f..328268473fec 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -139,6 +139,10 @@ config HAVE_FAST_GUP config ARCH_KEEP_MEMBLOCK bool +# Keep arch NUMA mapping infrastructure post-init. +config NUMA_KEEP_MEMINFO + bool + config MEMORY_ISOLATION bool @@ -154,6 +158,7 @@ config MEMORY_HOTPLUG bool "Allow for memory hot-add" depends on SPARSEMEM || X86_64_ACPI_NUMA depends on ARCH_ENABLE_MEMORY_HOTPLUG + select NUMA_KEEP_MEMINFO if NUMA config MEMORY_HOTPLUG_SPARSE def_bool y -- cgit v1.2.3 From f86fd32db706613fe8d0104057efa6e83e0d7e8f Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:38:59 +0100 Subject: lib/vdso: Cleanup clock mode storage leftovers Now that all architectures are converted to use the generic storage the helpers and conditionals can be removed. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.470699892@linutronix.de --- arch/arm/mm/Kconfig | 1 - arch/arm64/Kconfig | 1 - arch/mips/Kconfig | 1 - arch/x86/Kconfig | 1 - include/asm-generic/vdso/vsyscall.h | 7 ------- include/linux/clocksource.h | 4 ++-- kernel/time/vsyscall.c | 4 ---- lib/vdso/Kconfig | 3 --- lib/vdso/gettimeofday.c | 17 +++++------------ 9 files changed, 7 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig index 865e888bb84f..65e4482e3849 100644 --- a/arch/arm/mm/Kconfig +++ b/arch/arm/mm/Kconfig @@ -900,7 +900,6 @@ config VDSO select GENERIC_TIME_VSYSCALL select GENERIC_VDSO_32 select GENERIC_GETTIMEOFDAY - select GENERIC_VDSO_CLOCK_MODE help Place in the process address space an ELF shared object providing fast implementations of gettimeofday and diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7809d4976269..c6c32fb7f546 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -110,7 +110,6 @@ config ARM64 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY - select GENERIC_VDSO_CLOCK_MODE select HANDLE_DOMAIN_IRQ select HARDIRQS_SW_RESEND select HAVE_PCI diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 23b5c0578776..654369a7209d 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -37,7 +37,6 @@ config MIPS select GENERIC_SCHED_CLOCK if !CAVIUM_OCTEON_SOC select GENERIC_SMP_IDLE_THREAD select GENERIC_TIME_VSYSCALL - select GENERIC_VDSO_CLOCK_MODE select GUP_GET_PTE_LOW_HIGH if CPU_MIPS32 && PHYS_ADDR_T_64BIT select HANDLE_DOMAIN_IRQ select HAVE_ARCH_COMPILER_H diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 698e9c835cc5..8b995db3d10f 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -125,7 +125,6 @@ config X86 select GENERIC_STRNLEN_USER select GENERIC_TIME_VSYSCALL select GENERIC_GETTIMEOFDAY - select GENERIC_VDSO_CLOCK_MODE select GENERIC_VDSO_TIME_NS select GUP_GET_PTE_LOW_HIGH if X86_PAE select HARDLOCKUP_CHECK_TIMESTAMP if X86_64 diff --git a/include/asm-generic/vdso/vsyscall.h b/include/asm-generic/vdso/vsyscall.h index cec543d9e87b..4a28797495d7 100644 --- a/include/asm-generic/vdso/vsyscall.h +++ b/include/asm-generic/vdso/vsyscall.h @@ -18,13 +18,6 @@ static __always_inline bool __arch_update_vdso_data(void) } #endif /* __arch_update_vdso_data */ -#ifndef __arch_get_clock_mode -static __always_inline int __arch_get_clock_mode(struct timekeeper *tk) -{ - return 0; -} -#endif /* __arch_get_clock_mode */ - #ifndef __arch_update_vsyscall static __always_inline void __arch_update_vsyscall(struct vdso_data *vdata, struct timekeeper *tk) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 6d5ed1b4d24d..7fefe0b21a14 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -24,13 +24,13 @@ struct clocksource; struct module; #if defined(CONFIG_ARCH_CLOCKSOURCE_DATA) || \ - defined(CONFIG_GENERIC_VDSO_CLOCK_MODE) + defined(CONFIG_GENERIC_GETTIMEOFDAY) #include #endif enum vdso_clock_mode { VDSO_CLOCKMODE_NONE, -#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE +#ifdef CONFIG_GENERIC_GETTIMEOFDAY VDSO_ARCH_CLOCKMODES, #endif VDSO_CLOCKMODE_MAX, diff --git a/kernel/time/vsyscall.c b/kernel/time/vsyscall.c index f9a5178c69bb..d31a5ef4ade5 100644 --- a/kernel/time/vsyscall.c +++ b/kernel/time/vsyscall.c @@ -77,11 +77,7 @@ void update_vsyscall(struct timekeeper *tk) /* copy vsyscall data */ vdso_write_begin(vdata); -#ifdef CONFIG_GENERIC_VDSO_CLOCK_MODE clock_mode = tk->tkr_mono.clock->vdso_clock_mode; -#else - clock_mode = __arch_get_clock_mode(tk); -#endif vdata[CS_HRES_COARSE].clock_mode = clock_mode; vdata[CS_RAW].clock_mode = clock_mode; diff --git a/lib/vdso/Kconfig b/lib/vdso/Kconfig index d9f43c84fcc6..d883ac299508 100644 --- a/lib/vdso/Kconfig +++ b/lib/vdso/Kconfig @@ -30,7 +30,4 @@ config GENERIC_VDSO_TIME_NS Selected by architectures which support time namespaces in the VDSO -config GENERIC_VDSO_CLOCK_MODE - bool - endif diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 3f2d8b859130..00f8d1f1405b 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -65,16 +65,13 @@ static int do_hres_timens(const struct vdso_data *vdns, clockid_t clk, do { seq = vdso_read_begin(vd); - if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - vd->clock_mode == VDSO_CLOCKMODE_NONE) + + if (unlikely(vd->clock_mode == VDSO_CLOCKMODE_NONE)) return -1; + cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - unlikely((s64)cycles < 0)) - return -1; - ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); ns >>= vd->shift; sec = vdso_ts->sec; @@ -137,16 +134,12 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, } smp_rmb(); - if (IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - vd->clock_mode == VDSO_CLOCKMODE_NONE) + if (unlikely(vd->clock_mode == VDSO_CLOCKMODE_NONE)) return -1; + cycles = __arch_get_hw_counter(vd->clock_mode); ns = vdso_ts->nsec; last = vd->cycle_last; - if (!IS_ENABLED(CONFIG_GENERIC_VDSO_CLOCK_MODE) && - unlikely((s64)cycles < 0)) - return -1; - ns += vdso_calc_delta(cycles, last, vd->mask, vd->mult); ns >>= vd->shift; sec = vdso_ts->sec; -- cgit v1.2.3 From c7a18100bdffdff440c7291db6e80863fab0461e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:39:00 +0100 Subject: lib/vdso: Avoid highres update if clocksource is not VDSO capable If the current clocksource is not VDSO capable there is no point in updating the high resolution parts of the VDSO data. Replace the architecture specific check with a check for a VDSO capable clocksource and skip the update if there is none. Signed-off-by: Thomas Gleixner Tested-by: Vincenzo Frascino Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.563379423@linutronix.de --- arch/arm/include/asm/vdso/vsyscall.h | 7 ------- include/asm-generic/vdso/vsyscall.h | 7 ------- kernel/time/vsyscall.c | 6 +++--- 3 files changed, 3 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/vdso/vsyscall.h b/arch/arm/include/asm/vdso/vsyscall.h index 002f9edd8a2b..47e41ae8ccd0 100644 --- a/arch/arm/include/asm/vdso/vsyscall.h +++ b/arch/arm/include/asm/vdso/vsyscall.h @@ -21,13 +21,6 @@ struct vdso_data *__arm_get_k_vdso_data(void) } #define __arch_get_k_vdso_data __arm_get_k_vdso_data -static __always_inline -bool __arm_update_vdso_data(void) -{ - return cntvct_ok; -} -#define __arch_update_vdso_data __arm_update_vdso_data - static __always_inline void __arm_sync_vdso_data(struct vdso_data *vdata) { diff --git a/include/asm-generic/vdso/vsyscall.h b/include/asm-generic/vdso/vsyscall.h index 4a28797495d7..c835607f78ae 100644 --- a/include/asm-generic/vdso/vsyscall.h +++ b/include/asm-generic/vdso/vsyscall.h @@ -11,13 +11,6 @@ static __always_inline struct vdso_data *__arch_get_k_vdso_data(void) } #endif /* __arch_get_k_vdso_data */ -#ifndef __arch_update_vdso_data -static __always_inline bool __arch_update_vdso_data(void) -{ - return true; -} -#endif /* __arch_update_vdso_data */ - #ifndef __arch_update_vsyscall static __always_inline void __arch_update_vsyscall(struct vdso_data *vdata, struct timekeeper *tk) diff --git a/kernel/time/vsyscall.c b/kernel/time/vsyscall.c index d31a5ef4ade5..54ce6eb2ca36 100644 --- a/kernel/time/vsyscall.c +++ b/kernel/time/vsyscall.c @@ -105,10 +105,10 @@ void update_vsyscall(struct timekeeper *tk) WRITE_ONCE(vdata[CS_HRES_COARSE].hrtimer_res, hrtimer_resolution); /* - * Architectures can opt out of updating the high resolution part - * of the VDSO. + * If the current clocksource is not VDSO capable, then spare the + * update of the high reolution parts. */ - if (__arch_update_vdso_data()) + if (clock_mode != VDSO_CLOCKMODE_NONE) update_vdso_data(vdata, tk); __arch_update_vsyscall(vdata, tk); -- cgit v1.2.3 From 2d6b01bd88ccabba06d342ef80eaab6b39d12497 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 7 Feb 2020 13:39:01 +0100 Subject: lib/vdso: Move VCLOCK_TIMENS to vdso_clock_modes Move the time namespace indicator clock mode to the other ones for consistency sake. Signed-off-by: Thomas Gleixner Reviewed-by: Vincenzo Frascino Link: https://lkml.kernel.org/r/20200207124403.656097274@linutronix.de --- include/linux/clocksource.h | 3 +++ include/vdso/datapage.h | 2 -- kernel/time/namespace.c | 7 ++++--- lib/vdso/gettimeofday.c | 18 ++++++++++-------- 4 files changed, 17 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 7fefe0b21a14..02e3282719bd 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -34,6 +34,9 @@ enum vdso_clock_mode { VDSO_ARCH_CLOCKMODES, #endif VDSO_CLOCKMODE_MAX, + + /* Indicator for time namespace VDSO */ + VDSO_CLOCKMODE_TIMENS = INT_MAX }; /** diff --git a/include/vdso/datapage.h b/include/vdso/datapage.h index c5f347cc5e55..30c4cb0428d1 100644 --- a/include/vdso/datapage.h +++ b/include/vdso/datapage.h @@ -21,8 +21,6 @@ #define CS_RAW 1 #define CS_BASES (CS_RAW + 1) -#define VCLOCK_TIMENS UINT_MAX - /** * struct vdso_timestamp - basetime per clock_id * @sec: seconds diff --git a/kernel/time/namespace.c b/kernel/time/namespace.c index 12858507d75a..e6ba064ce773 100644 --- a/kernel/time/namespace.c +++ b/kernel/time/namespace.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -172,8 +173,8 @@ static struct timens_offset offset_from_ts(struct timespec64 off) * for vdso_data->clock_mode is a non-issue. The task is spin waiting for the * update to finish and for 'seq' to become even anyway. * - * Timens page has vdso_data->clock_mode set to VCLOCK_TIMENS which enforces - * the time namespace handling path. + * Timens page has vdso_data->clock_mode set to VDSO_CLOCKMODE_TIMENS which + * enforces the time namespace handling path. */ static void timens_setup_vdso_data(struct vdso_data *vdata, struct time_namespace *ns) @@ -183,7 +184,7 @@ static void timens_setup_vdso_data(struct vdso_data *vdata, struct timens_offset boottime = offset_from_ts(ns->offsets.boottime); vdata->seq = 1; - vdata->clock_mode = VCLOCK_TIMENS; + vdata->clock_mode = VDSO_CLOCKMODE_TIMENS; offset[CLOCK_MONOTONIC] = monotonic; offset[CLOCK_MONOTONIC_RAW] = monotonic; offset[CLOCK_MONOTONIC_COARSE] = monotonic; diff --git a/lib/vdso/gettimeofday.c b/lib/vdso/gettimeofday.c index 00f8d1f1405b..a76ac8d17c5f 100644 --- a/lib/vdso/gettimeofday.c +++ b/lib/vdso/gettimeofday.c @@ -116,10 +116,10 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, do { /* - * Open coded to handle VCLOCK_TIMENS. Time namespace + * Open coded to handle VDSO_CLOCKMODE_TIMENS. Time namespace * enabled tasks have a special VVAR page installed which * has vd->seq set to 1 and vd->clock_mode set to - * VCLOCK_TIMENS. For non time namespace affected tasks + * VDSO_CLOCKMODE_TIMENS. For non time namespace affected tasks * this does not affect performance because if vd->seq is * odd, i.e. a concurrent update is in progress the extra * check for vd->clock_mode is just a few extra @@ -128,7 +128,7 @@ static __always_inline int do_hres(const struct vdso_data *vd, clockid_t clk, */ while (unlikely((seq = READ_ONCE(vd->seq)) & 1)) { if (IS_ENABLED(CONFIG_TIME_NS) && - vd->clock_mode == VCLOCK_TIMENS) + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) return do_hres_timens(vd, clk, ts); cpu_relax(); } @@ -200,12 +200,12 @@ static __always_inline int do_coarse(const struct vdso_data *vd, clockid_t clk, do { /* - * Open coded to handle VCLOCK_TIMENS. See comment in + * Open coded to handle VDSO_CLOCK_TIMENS. See comment in * do_hres(). */ while ((seq = READ_ONCE(vd->seq)) & 1) { if (IS_ENABLED(CONFIG_TIME_NS) && - vd->clock_mode == VCLOCK_TIMENS) + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) return do_coarse_timens(vd, clk, ts); cpu_relax(); } @@ -292,7 +292,7 @@ __cvdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz) if (unlikely(tz != NULL)) { if (IS_ENABLED(CONFIG_TIME_NS) && - vd->clock_mode == VCLOCK_TIMENS) + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) vd = __arch_get_timens_vdso_data(); tz->tz_minuteswest = vd[CS_HRES_COARSE].tz_minuteswest; @@ -308,7 +308,8 @@ static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time const struct vdso_data *vd = __arch_get_vdso_data(); __kernel_old_time_t t; - if (IS_ENABLED(CONFIG_TIME_NS) && vd->clock_mode == VCLOCK_TIMENS) + if (IS_ENABLED(CONFIG_TIME_NS) && + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) vd = __arch_get_timens_vdso_data(); t = READ_ONCE(vd[CS_HRES_COARSE].basetime[CLOCK_REALTIME].sec); @@ -332,7 +333,8 @@ int __cvdso_clock_getres_common(clockid_t clock, struct __kernel_timespec *res) if (unlikely((u32) clock >= MAX_CLOCKS)) return -1; - if (IS_ENABLED(CONFIG_TIME_NS) && vd->clock_mode == VCLOCK_TIMENS) + if (IS_ENABLED(CONFIG_TIME_NS) && + vd->clock_mode == VDSO_CLOCKMODE_TIMENS) vd = __arch_get_timens_vdso_data(); /* -- cgit v1.2.3 From 7a47281439ba00b11fc098f36695522184ce5a82 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 17 Feb 2020 12:12:09 +0200 Subject: net: sched: lock action when translating it to flow_action infra In order to remove dependency on rtnl lock, take action's tcfa_lock when constructing its representation as flow_action_entry structure. Refactor tcf_sample_get_group() to assume that caller holds tcf_lock and don't take it manually. This callback is only called from flow_action infra representation translator which now calls it with tcf_lock held, so this refactoring is necessary to prevent deadlock. Allocate memory with GFP_ATOMIC flag for ip_tunnel_info copy because tcf_tunnel_info_copy() is only called from flow_action representation infra code with tcf_lock spinlock taken. Signed-off-by: Vlad Buslov Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/tc_act/tc_tunnel_key.h | 2 +- net/sched/act_sample.c | 2 -- net/sched/cls_api.c | 17 +++++++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_tunnel_key.h b/include/net/tc_act/tc_tunnel_key.h index 0689d9bcdf84..2b3df076e5b6 100644 --- a/include/net/tc_act/tc_tunnel_key.h +++ b/include/net/tc_act/tc_tunnel_key.h @@ -69,7 +69,7 @@ tcf_tunnel_info_copy(const struct tc_action *a) if (tun) { size_t tun_size = sizeof(*tun) + tun->options_len; struct ip_tunnel_info *tun_copy = kmemdup(tun, tun_size, - GFP_KERNEL); + GFP_ATOMIC); return tun_copy; } diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c index ce948c1e24dc..5e2df590bb58 100644 --- a/net/sched/act_sample.c +++ b/net/sched/act_sample.c @@ -267,14 +267,12 @@ tcf_sample_get_group(const struct tc_action *a, struct tcf_sample *s = to_sample(a); struct psample_group *group; - spin_lock_bh(&s->tcf_lock); group = rcu_dereference_protected(s->psample_group, lockdep_is_held(&s->tcf_lock)); if (group) { psample_group_take(group); *destructor = tcf_psample_group_put; } - spin_unlock_bh(&s->tcf_lock); return group; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index c2cdd0fc2e70..610505117780 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3435,7 +3435,7 @@ static void tcf_sample_get_group(struct flow_action_entry *entry, int tc_setup_flow_action(struct flow_action *flow_action, const struct tcf_exts *exts, bool rtnl_held) { - const struct tc_action *act; + struct tc_action *act; int i, j, k, err = 0; if (!exts) @@ -3449,6 +3449,7 @@ int tc_setup_flow_action(struct flow_action *flow_action, struct flow_action_entry *entry; entry = &flow_action->entries[j]; + spin_lock_bh(&act->tcfa_lock); if (is_tcf_gact_ok(act)) { entry->id = FLOW_ACTION_ACCEPT; } else if (is_tcf_gact_shot(act)) { @@ -3489,13 +3490,13 @@ int tc_setup_flow_action(struct flow_action *flow_action, break; default: err = -EOPNOTSUPP; - goto err_out; + goto err_out_locked; } } else if (is_tcf_tunnel_set(act)) { entry->id = FLOW_ACTION_TUNNEL_ENCAP; err = tcf_tunnel_encap_get_tunnel(entry, act); if (err) - goto err_out; + goto err_out_locked; } else if (is_tcf_tunnel_release(act)) { entry->id = FLOW_ACTION_TUNNEL_DECAP; } else if (is_tcf_pedit(act)) { @@ -3509,7 +3510,7 @@ int tc_setup_flow_action(struct flow_action *flow_action, break; default: err = -EOPNOTSUPP; - goto err_out; + goto err_out_locked; } entry->mangle.htype = tcf_pedit_htype(act, k); entry->mangle.mask = tcf_pedit_mask(act, k); @@ -3560,15 +3561,16 @@ int tc_setup_flow_action(struct flow_action *flow_action, entry->mpls_mangle.ttl = tcf_mpls_ttl(act); break; default: - goto err_out; + goto err_out_locked; } } else if (is_tcf_skbedit_ptype(act)) { entry->id = FLOW_ACTION_PTYPE; entry->ptype = tcf_skbedit_ptype(act); } else { err = -EOPNOTSUPP; - goto err_out; + goto err_out_locked; } + spin_unlock_bh(&act->tcfa_lock); if (!is_tcf_pedit(act)) j++; @@ -3582,6 +3584,9 @@ err_out: tc_cleanup_flow_action(flow_action); return err; +err_out_locked: + spin_unlock_bh(&act->tcfa_lock); + goto err_out; } EXPORT_SYMBOL(tc_setup_flow_action); -- cgit v1.2.3 From 487e9589cc0ebebde4d1c4764aab271e5680a6b6 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 17 Feb 2020 12:12:10 +0200 Subject: net: sched: refactor police action helpers to require tcf_lock In order to remove rtnl lock dependency from flow_action representation translator, change rcu_dereference_bh_rtnl() to rcu_dereference_protected() in police action helpers that provide external access to rate and burst values. This is safe to do because the functions are not called from anywhere else outside flow_action infrastructure which was modified to obtain tcf_lock when accessing action data in one of previous patches in the series. Signed-off-by: Vlad Buslov Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/tc_act/tc_police.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_police.h b/include/net/tc_act/tc_police.h index cfdc7cb82cad..f098ad4424be 100644 --- a/include/net/tc_act/tc_police.h +++ b/include/net/tc_act/tc_police.h @@ -54,7 +54,8 @@ static inline u64 tcf_police_rate_bytes_ps(const struct tc_action *act) struct tcf_police *police = to_police(act); struct tcf_police_params *params; - params = rcu_dereference_bh_rtnl(police->params); + params = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); return params->rate.rate_bytes_ps; } @@ -63,7 +64,8 @@ static inline s64 tcf_police_tcfp_burst(const struct tc_action *act) struct tcf_police *police = to_police(act); struct tcf_police_params *params; - params = rcu_dereference_bh_rtnl(police->params); + params = rcu_dereference_protected(police->params, + lockdep_is_held(&police->tcf_lock)); return params->tcfp_burst; } -- cgit v1.2.3 From 107f2d50916500985b9fffd7c77d8c14809f9802 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 17 Feb 2020 12:12:11 +0200 Subject: net: sched: refactor ct action helpers to require tcf_lock In order to remove rtnl lock dependency from flow_action representation translator, change rtnl_dereference() to rcu_dereference_protected() in ct action helpers that provide external access to zone and action values. This is safe to do because the functions are not called from anywhere else outside flow_action infrastructure which was modified to obtain tcf_lock when accessing action data in one of previous patches in the series. Signed-off-by: Vlad Buslov Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/tc_act/tc_ct.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h index bdc20ab3b88d..a8b156402873 100644 --- a/include/net/tc_act/tc_ct.h +++ b/include/net/tc_act/tc_ct.h @@ -33,8 +33,10 @@ struct tcf_ct { }; #define to_ct(a) ((struct tcf_ct *)a) -#define to_ct_params(a) ((struct tcf_ct_params *) \ - rtnl_dereference((to_ct(a)->params))) +#define to_ct_params(a) \ + ((struct tcf_ct_params *) \ + rcu_dereference_protected(to_ct(a)->params, \ + lockdep_is_held(&a->tcfa_lock))) static inline uint16_t tcf_ct_zone(const struct tc_action *a) { -- cgit v1.2.3 From b15e7a6e8d31d6abe2d98929d60ad3a0e6ae4de1 Mon Sep 17 00:00:00 2001 From: Vlad Buslov Date: Mon, 17 Feb 2020 12:12:12 +0200 Subject: net: sched: don't take rtnl lock during flow_action setup Refactor tc_setup_flow_action() function not to use rtnl lock and remove 'rtnl_held' argument that is no longer needed. Signed-off-by: Vlad Buslov Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 2 +- net/sched/cls_api.c | 8 +------- net/sched/cls_flower.c | 6 ++---- net/sched/cls_matchall.c | 4 ++-- 4 files changed, 6 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index a972244ab193..53946b509b51 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -509,7 +509,7 @@ tcf_match_indev(struct sk_buff *skb, int ifindex) } int tc_setup_flow_action(struct flow_action *flow_action, - const struct tcf_exts *exts, bool rtnl_held); + const struct tcf_exts *exts); void tc_cleanup_flow_action(struct flow_action *flow_action); int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type, diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 610505117780..13c33eaf1ca1 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3433,7 +3433,7 @@ static void tcf_sample_get_group(struct flow_action_entry *entry, } int tc_setup_flow_action(struct flow_action *flow_action, - const struct tcf_exts *exts, bool rtnl_held) + const struct tcf_exts *exts) { struct tc_action *act; int i, j, k, err = 0; @@ -3441,9 +3441,6 @@ int tc_setup_flow_action(struct flow_action *flow_action, if (!exts) return 0; - if (!rtnl_held) - rtnl_lock(); - j = 0; tcf_exts_for_each_action(i, act, exts) { struct flow_action_entry *entry; @@ -3577,9 +3574,6 @@ int tc_setup_flow_action(struct flow_action *flow_action, } err_out: - if (!rtnl_held) - rtnl_unlock(); - if (err) tc_cleanup_flow_action(flow_action); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 7e54d2ab5254..726fc9c5910f 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -449,8 +449,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, cls_flower.rule->match.key = &f->mkey; cls_flower.classid = f->res.classid; - err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts, - rtnl_held); + err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts); if (err) { kfree(cls_flower.rule); if (skip_sw) { @@ -2000,8 +1999,7 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, cls_flower.rule->match.mask = &f->mask->key; cls_flower.rule->match.key = &f->mkey; - err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts, - true); + err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts); if (err) { kfree(cls_flower.rule); if (tc_skip_sw(f->flags)) { diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 610a0b728161..a34b36adb9b7 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -97,7 +97,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, cls_mall.command = TC_CLSMATCHALL_REPLACE; cls_mall.cookie = cookie; - err = tc_setup_flow_action(&cls_mall.rule->action, &head->exts, true); + err = tc_setup_flow_action(&cls_mall.rule->action, &head->exts); if (err) { kfree(cls_mall.rule); mall_destroy_hw_filter(tp, head, cookie, NULL); @@ -302,7 +302,7 @@ static int mall_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb, TC_CLSMATCHALL_REPLACE : TC_CLSMATCHALL_DESTROY; cls_mall.cookie = (unsigned long)head; - err = tc_setup_flow_action(&cls_mall.rule->action, &head->exts, true); + err = tc_setup_flow_action(&cls_mall.rule->action, &head->exts); if (err) { kfree(cls_mall.rule); if (add && tc_skip_sw(head->flags)) { -- cgit v1.2.3 From 73ec6d2748dc35db2b32cf3c182a27c4a0837b9b Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 10 Feb 2020 09:31:54 -0800 Subject: scsi: fc: Update Descriptor definition and add RDF and Link Integrity FPINs Update the FC headers for the RDF ELS and populate out the FPIN ELS and the Link integrity FPIN payload. RDF is used to register for diagnostic events. FPIN is how the fabric reports a diagnostic event. Specifically, this patch: - Adds the formal definition of TLV descriptors that are now used in a lot of the FC spec. The simplistic fc_fn_desc structure, basically no more than the tlv definition, is removed. - Small tlv helper functions are added as defines. - The list of known Descriptor tags (identifying the TLV) is expanded and a name initializer introduced. - The LSRI descriptor, returned in many new ELS response payloads is added. - The RDF ELS code is added, and the RDF request response structures added. - The FPIN els definition is corrected. - A full definition of a Link Integrity Notification descriptor is added, [mkp: rolled in kbuild warning fix] Link: https://lore.kernel.org/r/20200210173155.547-2-jsmart2021@gmail.com Reported-by: kbuild test robot Signed-off-by: James Smart Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- include/uapi/scsi/fc/fc_els.h | 211 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 196 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/uapi/scsi/fc/fc_els.h b/include/uapi/scsi/fc/fc_els.h index 76f627f0d13b..66318c44acd7 100644 --- a/include/uapi/scsi/fc/fc_els.h +++ b/include/uapi/scsi/fc/fc_els.h @@ -9,6 +9,7 @@ #define _FC_ELS_H_ #include +#include /* * Fibre Channel Switch - Enhanced Link Services definitions. @@ -40,6 +41,7 @@ enum fc_els_cmd { ELS_REC = 0x13, /* read exchange concise */ ELS_SRR = 0x14, /* sequence retransmission request */ ELS_FPIN = 0x16, /* Fabric Performance Impact Notification */ + ELS_RDF = 0x19, /* Register Diagnostic Functions */ ELS_PRLI = 0x20, /* process login */ ELS_PRLO = 0x21, /* process logout */ ELS_SCN = 0x22, /* state change notification */ @@ -108,6 +110,7 @@ enum fc_els_cmd { [ELS_REC] = "REC", \ [ELS_SRR] = "SRR", \ [ELS_FPIN] = "FPIN", \ + [ELS_RDF] = "RDF", \ [ELS_PRLI] = "PRLI", \ [ELS_PRLO] = "PRLO", \ [ELS_SCN] = "SCN", \ @@ -207,6 +210,99 @@ enum fc_els_rjt_explan { /* TBD - above definitions incomplete */ }; +/* + * Link Service TLV Descriptor Tag Values + */ +enum fc_ls_tlv_dtag { + ELS_DTAG_LS_REQ_INFO = 0x00000001, + /* Link Service Request Information Descriptor */ + ELS_DTAG_LNK_INTEGRITY = 0x00020001, + /* Link Integrity Notification Descriptor */ + ELS_DTAG_DELIVERY = 0x00020002, + /* Delivery Notification Descriptor */ + ELS_DTAG_PEER_CONGEST = 0x00020003, + /* Peer Congestion Notification Descriptor */ + ELS_DTAG_CONGESTION = 0x00020004, + /* Congestion Notification Descriptor */ + ELS_DTAG_FPIN_REGISTER = 0x00030001, + /* FPIN Registration Descriptor */ +}; + +/* + * Initializer useful for decoding table. + * Please keep this in sync with the above definitions. + */ +#define FC_LS_TLV_DTAG_INIT { \ + { ELS_DTAG_LS_REQ_INFO, "Link Service Request Information" }, \ + { ELS_DTAG_LNK_INTEGRITY, "Link Integrity Notification" }, \ + { ELS_DTAG_DELIVERY, "Delivery Notification Present" }, \ + { ELS_DTAG_PEER_CONGEST, "Peer Congestion Notification" }, \ + { ELS_DTAG_CONGESTION, "Congestion Notification" }, \ + { ELS_DTAG_FPIN_REGISTER, "FPIN Registration" }, \ +} + + +/* + * Generic Link Service TLV Descriptor format + * + * This structure, as it defines no payload, will also be referred to + * as the "tlv header" - which contains the tag and len fields. + */ +struct fc_tlv_desc { + __be32 desc_tag; /* Notification Descriptor Tag */ + __be32 desc_len; /* Length of Descriptor (in bytes). + * Size of descriptor excluding + * desc_tag and desc_len fields. + */ + __u8 desc_value[0]; /* Descriptor Value */ +}; + +/* Descriptor tag and len fields are considered the mandatory header + * for a descriptor + */ +#define FC_TLV_DESC_HDR_SZ sizeof(struct fc_tlv_desc) + +/* + * Macro, used when initializing payloads, to return the descriptor length. + * Length is size of descriptor minus the tag and len fields. + */ +#define FC_TLV_DESC_LENGTH_FROM_SZ(desc) \ + (sizeof(desc) - FC_TLV_DESC_HDR_SZ) + +/* Macro, used on received payloads, to return the descriptor length */ +#define FC_TLV_DESC_SZ_FROM_LENGTH(tlv) \ + (__be32_to_cpu((tlv)->desc_len) + FC_TLV_DESC_HDR_SZ) + +/* + * This helper is used to walk descriptors in a descriptor list. + * Given the address of the current descriptor, which minimally contains a + * tag and len field, calculate the address of the next descriptor based + * on the len field. + */ +static inline void *fc_tlv_next_desc(void *desc) +{ + struct fc_tlv_desc *tlv = desc; + + return (desc + FC_TLV_DESC_SZ_FROM_LENGTH(tlv)); +} + + +/* + * Link Service Request Information Descriptor + */ +struct fc_els_lsri_desc { + __be32 desc_tag; /* descriptor tag (0x0000 0001) */ + __be32 desc_len; /* Length of Descriptor (in bytes) (4). + * Size of descriptor excluding + * desc_tag and desc_len fields. + */ + struct { + __u8 cmd; /* ELS cmd byte */ + __u8 bytes[3]; /* bytes 1..3 */ + } rqst_w0; /* Request word 0 */ +}; + + /* * Common service parameters (N ports). */ @@ -819,24 +915,61 @@ enum fc_els_clid_ic { }; +enum fc_fpin_li_event_types { + FPIN_LI_UNKNOWN = 0x0, + FPIN_LI_LINK_FAILURE = 0x1, + FPIN_LI_LOSS_OF_SYNC = 0x2, + FPIN_LI_LOSS_OF_SIG = 0x3, + FPIN_LI_PRIM_SEQ_ERR = 0x4, + FPIN_LI_INVALID_TX_WD = 0x5, + FPIN_LI_INVALID_CRC = 0x6, + FPIN_LI_DEVICE_SPEC = 0xF, +}; + /* - * Fabric Notification Descriptor Tag values + * Initializer useful for decoding table. + * Please keep this in sync with the above definitions. */ -enum fc_fn_dtag { - ELS_FN_DTAG_LNK_INTEGRITY = 0x00020001, /* Link Integrity */ - ELS_FN_DTAG_PEER_CONGEST = 0x00020003, /* Peer Congestion */ - ELS_FN_DTAG_CONGESTION = 0x00020004, /* Congestion */ -}; +#define FC_FPIN_LI_EVT_TYPES_INIT { \ + { FPIN_LI_UNKNOWN, "Unknown" }, \ + { FPIN_LI_LINK_FAILURE, "Link Failure" }, \ + { FPIN_LI_LOSS_OF_SYNC, "Loss of Synchronization" }, \ + { FPIN_LI_LOSS_OF_SIG, "Loss of Signal" }, \ + { FPIN_LI_PRIM_SEQ_ERR, "Primitive Sequence Protocol Error" }, \ + { FPIN_LI_INVALID_TX_WD, "Invalid Transmission Word" }, \ + { FPIN_LI_INVALID_CRC, "Invalid CRC" }, \ + { FPIN_LI_DEVICE_SPEC, "Device Specific" }, \ +} + /* - * Fabric Notification Descriptor + * Link Integrity Notification Descriptor */ -struct fc_fn_desc { - __be32 fn_desc_tag; /* Notification Descriptor Tag */ - __be32 fn_desc_value_len; /* Length of Descriptor Value field - * (in bytes) - */ - __u8 fn_desc_value[0]; /* Descriptor Value */ +struct fc_fn_li_desc { + __be32 desc_tag; /* Descriptor Tag (0x00020001) */ + __be32 desc_len; /* Length of Descriptor (in bytes). + * Size of descriptor excluding + * desc_tag and desc_len fields. + */ + __be64 detecting_wwpn; /* Port Name that detected event */ + __be64 attached_wwpn; /* Port Name of device attached to + * detecting Port Name + */ + __be16 event_type; /* see enum fc_fpin_li_event_types */ + __be16 event_modifier; /* Implementation specific value + * describing the event type + */ + __be32 event_threshold;/* duration in ms of the link + * integrity detection cycle + */ + __be32 event_count; /* minimum number of event + * occurrences during the event + * threshold to caause the LI event + */ + __be32 pname_count; /* number of portname_list elements */ + __be64 pname_list[0]; /* list of N_Port_Names accessible + * through the attached port + */ }; /* @@ -845,8 +978,56 @@ struct fc_fn_desc { struct fc_els_fpin { __u8 fpin_cmd; /* command (0x16) */ __u8 fpin_zero[3]; /* specified as zero - part of cmd */ - __be32 fpin_desc_cnt; /* count of descriptors */ - struct fc_fn_desc fpin_desc[0]; /* Descriptor list */ + __be32 desc_len; /* Length of Descriptor List (in bytes). + * Size of ELS excluding fpin_cmd, + * fpin_zero and desc_len fields. + */ + struct fc_tlv_desc fpin_desc[0]; /* Descriptor list */ +}; + +/* Diagnostic Function Descriptor - FPIN Registration */ +struct fc_df_desc_fpin_reg { + __be32 desc_tag; /* FPIN Registration (0x00030001) */ + __be32 desc_len; /* Length of Descriptor (in bytes). + * Size of descriptor excluding + * desc_tag and desc_len fields. + */ + __be32 count; /* Number of desc_tags elements */ + __be32 desc_tags[0]; /* Array of Descriptor Tags. + * Each tag indicates a function + * supported by the N_Port (request) + * or by the N_Port and Fabric + * Controller (reply; may be a subset + * of the request). + * See ELS_FN_DTAG_xxx for tag values. + */ }; +/* + * ELS_RDF - Register Diagnostic Functions + */ +struct fc_els_rdf { + __u8 fpin_cmd; /* command (0x19) */ + __u8 fpin_zero[3]; /* specified as zero - part of cmd */ + __be32 desc_len; /* Length of Descriptor List (in bytes). + * Size of ELS excluding fpin_cmd, + * fpin_zero and desc_len fields. + */ + struct fc_tlv_desc desc[0]; /* Descriptor list */ +}; + +/* + * ELS RDF LS_ACC Response. + */ +struct fc_els_rdf_resp { + struct fc_els_ls_acc acc_hdr; + __be32 desc_list_len; /* Length of response (in + * bytes). Excludes acc_hdr + * and desc_list_len fields. + */ + struct fc_els_lsri_desc lsri; + struct fc_tlv_desc desc[0]; /* Supported Descriptor list */ +}; + + #endif /* _FC_ELS_H_ */ -- cgit v1.2.3 From d8fab4815a371e8013e1a769c31da1bcaf618b01 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Fri, 14 Feb 2020 15:30:01 +0100 Subject: net/mlx5: fix spelling mistake "reserverd" -> "reserved" The reserved member should be named reserved. Signed-off-by: Alexandre Belloni Signed-off-by: Leon Romanovsky --- include/linux/mlx5/mlx5_ifc_fpga.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mlx5/mlx5_ifc_fpga.h b/include/linux/mlx5/mlx5_ifc_fpga.h index 37e065a80a43..07d77323f78a 100644 --- a/include/linux/mlx5/mlx5_ifc_fpga.h +++ b/include/linux/mlx5/mlx5_ifc_fpga.h @@ -608,7 +608,7 @@ struct mlx5_ifc_tls_cmd_bits { struct mlx5_ifc_tls_resp_bits { u8 syndrome[0x20]; u8 stream_id[0x20]; - u8 reserverd[0x40]; + u8 reserved[0x40]; }; #define MLX5_TLS_COMMAND_SIZE (0x100) -- cgit v1.2.3 From 5d30f92e7631286b8617777c5400c8eadcae50a1 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 16 Feb 2020 12:01:09 -0800 Subject: x86/NUMA: Provide a range-to-target_node lookup facility The DEV_DAX_KMEM facility is a generic mechanism to allow device-dax instances, fronting performance-differentiated-memory like pmem, to be added to the System RAM pool. The NUMA node for that hot-added memory is derived from the device-dax instance's 'target_node' attribute. Recall that the 'target_node' is the ACPI-PXM-to-node translation for memory when it comes online whereas the 'numa_node' attribute of the device represents the closest online cpu node. Presently useful target_node information from the ACPI SRAT is discarded with the expectation that "Reserved" memory will never be onlined. Now, DEV_DAX_KMEM violates that assumption, there is a need to retain the translation. Move, rather than discard, numa_memblk data to a secondary array that memory_add_physaddr_to_target_node() may consider at a later point in time. Cc: Dave Hansen Cc: Andy Lutomirski Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Cc: Andrew Morton Cc: David Hildenbrand Cc: Michal Hocko Reported-by: kbuild test robot Reviewed-by: Ingo Molnar Signed-off-by: Dan Williams Reviewed-by: Thomas Gleixner Link: https://lore.kernel.org/r/158188326978.894464.217282995221175417.stgit@dwillia2-desk3.amr.corp.intel.com --- arch/x86/mm/numa.c | 61 +++++++++++++++++++++++++++++++++++++++++++--------- include/linux/numa.h | 14 +++++++++++- 2 files changed, 64 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/x86/mm/numa.c b/arch/x86/mm/numa.c index 2450b21cc28a..59ba008504dc 100644 --- a/arch/x86/mm/numa.c +++ b/arch/x86/mm/numa.c @@ -26,6 +26,7 @@ struct pglist_data *node_data[MAX_NUMNODES] __read_mostly; EXPORT_SYMBOL(node_data); static struct numa_meminfo numa_meminfo __initdata_or_meminfo; +static struct numa_meminfo numa_reserved_meminfo __initdata_or_meminfo; static int numa_distance_cnt; static u8 *numa_distance; @@ -164,6 +165,19 @@ void __init numa_remove_memblk_from(int idx, struct numa_meminfo *mi) (mi->nr_blks - idx) * sizeof(mi->blk[0])); } +/** + * numa_move_tail_memblk - Move a numa_memblk from one numa_meminfo to another + * @dst: numa_meminfo to append block to + * @idx: Index of memblk to remove + * @src: numa_meminfo to remove memblk from + */ +static void __init numa_move_tail_memblk(struct numa_meminfo *dst, int idx, + struct numa_meminfo *src) +{ + dst->blk[dst->nr_blks++] = src->blk[idx]; + numa_remove_memblk_from(idx, src); +} + /** * numa_add_memblk - Add one numa_memblk to numa_meminfo * @nid: NUMA node ID of the new memblk @@ -233,14 +247,19 @@ int __init numa_cleanup_meminfo(struct numa_meminfo *mi) for (i = 0; i < mi->nr_blks; i++) { struct numa_memblk *bi = &mi->blk[i]; - /* make sure all blocks are inside the limits */ + /* move / save reserved memory ranges */ + if (!memblock_overlaps_region(&memblock.memory, + bi->start, bi->end - bi->start)) { + numa_move_tail_memblk(&numa_reserved_meminfo, i--, mi); + continue; + } + + /* make sure all non-reserved blocks are inside the limits */ bi->start = max(bi->start, low); bi->end = min(bi->end, high); - /* and there's no empty or non-exist block */ - if (bi->start >= bi->end || - !memblock_overlaps_region(&memblock.memory, - bi->start, bi->end - bi->start)) + /* and there's no empty block */ + if (bi->start >= bi->end) numa_remove_memblk_from(i--, mi); } @@ -877,16 +896,38 @@ EXPORT_SYMBOL(cpumask_of_node); #endif /* !CONFIG_DEBUG_PER_CPU_MAPS */ -#ifdef CONFIG_MEMORY_HOTPLUG -int memory_add_physaddr_to_nid(u64 start) +#ifdef CONFIG_NUMA_KEEP_MEMINFO +static int meminfo_to_nid(struct numa_meminfo *mi, u64 start) { - struct numa_meminfo *mi = &numa_meminfo; - int nid = mi->blk[0].nid; int i; for (i = 0; i < mi->nr_blks; i++) if (mi->blk[i].start <= start && mi->blk[i].end > start) - nid = mi->blk[i].nid; + return mi->blk[i].nid; + return NUMA_NO_NODE; +} + +int phys_to_target_node(phys_addr_t start) +{ + int nid = meminfo_to_nid(&numa_meminfo, start); + + /* + * Prefer online nodes, but if reserved memory might be + * hot-added continue the search with reserved ranges. + */ + if (nid != NUMA_NO_NODE) + return nid; + + return meminfo_to_nid(&numa_reserved_meminfo, start); +} +EXPORT_SYMBOL_GPL(phys_to_target_node); + +int memory_add_physaddr_to_nid(u64 start) +{ + int nid = meminfo_to_nid(&numa_meminfo, start); + + if (nid == NUMA_NO_NODE) + nid = numa_meminfo.blk[0].nid; return nid; } EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid); diff --git a/include/linux/numa.h b/include/linux/numa.h index 5773cd2613fc..a42df804679e 100644 --- a/include/linux/numa.h +++ b/include/linux/numa.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_NUMA_H #define _LINUX_NUMA_H - +#include #ifdef CONFIG_NODES_SHIFT #define NODES_SHIFT CONFIG_NODES_SHIFT @@ -21,12 +21,24 @@ #endif #ifdef CONFIG_NUMA +/* Generic implementation available */ int numa_map_to_online_node(int node); + +/* + * Optional architecture specific implementation, users need a "depends + * on $ARCH" + */ +int phys_to_target_node(phys_addr_t addr); #else static inline int numa_map_to_online_node(int node) { return NUMA_NO_NODE; } + +static inline int phys_to_target_node(phys_addr_t addr) +{ + return NUMA_NO_NODE; +} #endif #endif /* _LINUX_NUMA_H */ -- cgit v1.2.3 From 0be298a939b748256035f66716fca409dd26d0dc Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 20 Jan 2020 14:10:04 +0200 Subject: ARM: at91: pm: add pmc_version member to at91_pm_data This will be used to differentiate b/w different PLLs settings to be applied in the final/first steps of the suspend/resume process by doing PLL specific configurations. Signed-off-by: Claudiu Beznea Acked-by: Stephen Boyd Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/1579522208-19523-5-git-send-email-claudiu.beznea@microchip.com --- arch/arm/mach-at91/pm.c | 7 +++++++ arch/arm/mach-at91/pm.h | 1 + arch/arm/mach-at91/pm_data-offsets.c | 2 ++ arch/arm/mach-at91/pm_suspend.S | 4 ++++ include/linux/clk/at91_pmc.h | 3 +++ 5 files changed, 17 insertions(+) (limited to 'include') diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c index ae7b148febd9..074bde64064e 100644 --- a/arch/arm/mach-at91/pm.c +++ b/arch/arm/mach-at91/pm.c @@ -737,28 +737,34 @@ backup_default: struct pmc_info { unsigned long uhp_udp_mask; unsigned long mckr; + unsigned long version; }; static const struct pmc_info pmc_infos[] __initconst = { { .uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP, .mckr = 0x30, + .version = AT91_PMC_V1, }, { .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP, .mckr = 0x30, + .version = AT91_PMC_V1, }, { .uhp_udp_mask = AT91SAM926x_PMC_UHP, .mckr = 0x30, + .version = AT91_PMC_V1, }, { .uhp_udp_mask = 0, .mckr = 0x30, + .version = AT91_PMC_V1, }, { .uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP, .mckr = 0x28, + .version = AT91_PMC_V2, }, }; @@ -797,6 +803,7 @@ static void __init at91_pm_init(void (*pm_idle)(void)) pmc = of_id->data; soc_pm.data.uhp_udp_mask = pmc->uhp_udp_mask; soc_pm.data.pmc_mckr_offset = pmc->mckr; + soc_pm.data.pmc_version = pmc->version; if (pm_idle) arm_pm_idle = pm_idle; diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h index 6f7f4236865a..218e8d1a30fb 100644 --- a/arch/arm/mach-at91/pm.h +++ b/arch/arm/mach-at91/pm.h @@ -34,6 +34,7 @@ struct at91_pm_data { unsigned int standby_mode; unsigned int suspend_mode; unsigned int pmc_mckr_offset; + unsigned int pmc_version; }; #endif diff --git a/arch/arm/mach-at91/pm_data-offsets.c b/arch/arm/mach-at91/pm_data-offsets.c index dfcbe626865c..82089ff258c0 100644 --- a/arch/arm/mach-at91/pm_data-offsets.c +++ b/arch/arm/mach-at91/pm_data-offsets.c @@ -14,6 +14,8 @@ int main(void) DEFINE(PM_DATA_SFRBU, offsetof(struct at91_pm_data, sfrbu)); DEFINE(PM_DATA_PMC_MCKR_OFFSET, offsetof(struct at91_pm_data, pmc_mckr_offset)); + DEFINE(PM_DATA_PMC_VERSION, offsetof(struct at91_pm_data, + pmc_version)); return 0; } diff --git a/arch/arm/mach-at91/pm_suspend.S b/arch/arm/mach-at91/pm_suspend.S index 64460b4e0fc1..5fa0c2aa10f7 100644 --- a/arch/arm/mach-at91/pm_suspend.S +++ b/arch/arm/mach-at91/pm_suspend.S @@ -95,6 +95,8 @@ ENTRY(at91_pm_suspend_in_sram) str tmp1, .pm_mode ldr tmp1, [r0, #PM_DATA_PMC_MCKR_OFFSET] str tmp1, .mckr_offset + ldr tmp1, [r0, #PM_DATA_PMC_VERSION] + str tmp1, .pmc_version /* Both ldrne below are here to preload their address in the TLB */ ldr tmp1, [r0, #PM_DATA_SHDWC] str tmp1, .shdwc @@ -542,6 +544,8 @@ ENDPROC(at91_sramc_self_refresh) .word 0 .mckr_offset: .word 0 +.pmc_version: + .word 0 .saved_mckr: .word 0 .saved_pllar: diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index 390437887b46..f3d691fc5f29 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -12,6 +12,9 @@ #ifndef AT91_PMC_H #define AT91_PMC_H +#define AT91_PMC_V1 (1) /* PMC version 1 */ +#define AT91_PMC_V2 (2) /* PMC version 2 [SAM9X60] */ + #define AT91_PMC_SCER 0x00 /* System Clock Enable Register */ #define AT91_PMC_SCDR 0x04 /* System Clock Disable Register */ -- cgit v1.2.3 From e13208ab5d938e51e46ba44a1dec8073142c3d8c Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 20 Jan 2020 14:10:06 +0200 Subject: clk: at91: move sam9x60's PLL register offsets to PMC header Move SAM9X60's PLL register offsets to PMC header so that the definitions would also be available from arch/arm/mach-at91/pm_suspend.S. This is necessary to disable/enable PLLA for SAM9X60 on suspend/resume. Signed-off-by: Claudiu Beznea Acked-by: Stephen Boyd Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/1579522208-19523-7-git-send-email-claudiu.beznea@microchip.com --- drivers/clk/at91/clk-sam9x60-pll.c | 91 ++++++++++++++++---------------------- include/linux/clk/at91_pmc.h | 20 +++++++++ 2 files changed, 57 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c index dfb354a5ff18..e699803986e5 100644 --- a/drivers/clk/at91/clk-sam9x60-pll.c +++ b/drivers/clk/at91/clk-sam9x60-pll.c @@ -14,27 +14,8 @@ #include "pmc.h" -#define PMC_PLL_CTRL0 0xc -#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0) -#define PMC_PLL_CTRL0_ENPLL BIT(28) -#define PMC_PLL_CTRL0_ENPLLCK BIT(29) -#define PMC_PLL_CTRL0_ENLOCK BIT(31) - -#define PMC_PLL_CTRL1 0x10 -#define PMC_PLL_CTRL1_FRACR_MSK GENMASK(21, 0) -#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24) - -#define PMC_PLL_ACR 0x18 -#define PMC_PLL_ACR_DEFAULT_UPLL 0x12020010UL -#define PMC_PLL_ACR_DEFAULT_PLLA 0x00020010UL -#define PMC_PLL_ACR_UTMIVR BIT(12) -#define PMC_PLL_ACR_UTMIBG BIT(13) -#define PMC_PLL_ACR_LOOP_FILTER_MSK GENMASK(31, 24) - -#define PMC_PLL_UPDT 0x1c -#define PMC_PLL_UPDT_UPDATE BIT(8) - -#define PMC_PLL_ISR0 0xec +#define PMC_PLL_CTRL0_DIV_MSK GENMASK(7, 0) +#define PMC_PLL_CTRL1_MUL_MSK GENMASK(30, 24) #define PLL_DIV_MAX (FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1) #define UPLL_DIV 2 @@ -59,7 +40,7 @@ static inline bool sam9x60_pll_ready(struct regmap *regmap, int id) { unsigned int status; - regmap_read(regmap, PMC_PLL_ISR0, &status); + regmap_read(regmap, AT91_PMC_PLL_ISR0, &status); return !!(status & BIT(id)); } @@ -74,12 +55,12 @@ static int sam9x60_pll_prepare(struct clk_hw *hw) u32 val; spin_lock_irqsave(pll->lock, flags); - regmap_write(regmap, PMC_PLL_UPDT, pll->id); + regmap_write(regmap, AT91_PMC_PLL_UPDT, pll->id); - regmap_read(regmap, PMC_PLL_CTRL0, &val); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &val); div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, val); - regmap_read(regmap, PMC_PLL_CTRL1, &val); + regmap_read(regmap, AT91_PMC_PLL_CTRL1, &val); mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, val); if (sam9x60_pll_ready(regmap, pll->id) && @@ -88,39 +69,39 @@ static int sam9x60_pll_prepare(struct clk_hw *hw) return 0; } - /* Recommended value for PMC_PLL_ACR */ + /* Recommended value for AT91_PMC_PLL_ACR */ if (pll->characteristics->upll) - val = PMC_PLL_ACR_DEFAULT_UPLL; + val = AT91_PMC_PLL_ACR_DEFAULT_UPLL; else - val = PMC_PLL_ACR_DEFAULT_PLLA; - regmap_write(regmap, PMC_PLL_ACR, val); + val = AT91_PMC_PLL_ACR_DEFAULT_PLLA; + regmap_write(regmap, AT91_PMC_PLL_ACR, val); - regmap_write(regmap, PMC_PLL_CTRL1, + regmap_write(regmap, AT91_PMC_PLL_CTRL1, FIELD_PREP(PMC_PLL_CTRL1_MUL_MSK, pll->mul)); if (pll->characteristics->upll) { /* Enable the UTMI internal bandgap */ - val |= PMC_PLL_ACR_UTMIBG; - regmap_write(regmap, PMC_PLL_ACR, val); + val |= AT91_PMC_PLL_ACR_UTMIBG; + regmap_write(regmap, AT91_PMC_PLL_ACR, val); udelay(10); /* Enable the UTMI internal regulator */ - val |= PMC_PLL_ACR_UTMIVR; - regmap_write(regmap, PMC_PLL_ACR, val); + val |= AT91_PMC_PLL_ACR_UTMIVR; + regmap_write(regmap, AT91_PMC_PLL_ACR, val); udelay(10); } - regmap_update_bits(regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE); - regmap_write(regmap, PMC_PLL_CTRL0, - PMC_PLL_CTRL0_ENLOCK | PMC_PLL_CTRL0_ENPLL | - PMC_PLL_CTRL0_ENPLLCK | pll->div); + regmap_write(regmap, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL | + AT91_PMC_PLL_CTRL0_ENPLLCK | pll->div); - regmap_update_bits(regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE); while (!sam9x60_pll_ready(regmap, pll->id)) cpu_relax(); @@ -144,22 +125,24 @@ static void sam9x60_pll_unprepare(struct clk_hw *hw) spin_lock_irqsave(pll->lock, flags); - regmap_write(pll->regmap, PMC_PLL_UPDT, pll->id); + regmap_write(pll->regmap, AT91_PMC_PLL_UPDT, pll->id); - regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, - PMC_PLL_CTRL0_ENPLLCK, 0); + regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENPLLCK, 0); - regmap_update_bits(pll->regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE); - regmap_update_bits(pll->regmap, PMC_PLL_CTRL0, PMC_PLL_CTRL0_ENPLL, 0); + regmap_update_bits(pll->regmap, AT91_PMC_PLL_CTRL0, + AT91_PMC_PLL_CTRL0_ENPLL, 0); if (pll->characteristics->upll) - regmap_update_bits(pll->regmap, PMC_PLL_ACR, - PMC_PLL_ACR_UTMIBG | PMC_PLL_ACR_UTMIVR, 0); + regmap_update_bits(pll->regmap, AT91_PMC_PLL_ACR, + AT91_PMC_PLL_ACR_UTMIBG | + AT91_PMC_PLL_ACR_UTMIVR, 0); - regmap_update_bits(pll->regmap, PMC_PLL_UPDT, - PMC_PLL_UPDT_UPDATE, PMC_PLL_UPDT_UPDATE); + regmap_update_bits(pll->regmap, AT91_PMC_PLL_UPDT, + AT91_PMC_PLL_UPDT_UPDATE, AT91_PMC_PLL_UPDT_UPDATE); spin_unlock_irqrestore(pll->lock, flags); } @@ -316,10 +299,10 @@ sam9x60_clk_register_pll(struct regmap *regmap, spinlock_t *lock, pll->regmap = regmap; pll->lock = lock; - regmap_write(regmap, PMC_PLL_UPDT, id); - regmap_read(regmap, PMC_PLL_CTRL0, &pllr); + regmap_write(regmap, AT91_PMC_PLL_UPDT, id); + regmap_read(regmap, AT91_PMC_PLL_CTRL0, &pllr); pll->div = FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, pllr); - regmap_read(regmap, PMC_PLL_CTRL1, &pllr); + regmap_read(regmap, AT91_PMC_PLL_CTRL1, &pllr); pll->mul = FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, pllr); hw = &pll->hw; diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h index f3d691fc5f29..49a53a137610 100644 --- a/include/linux/clk/at91_pmc.h +++ b/include/linux/clk/at91_pmc.h @@ -33,16 +33,34 @@ #define AT91_PMC_HCK0 (1 << 16) /* AHB Clock (USB host) [AT91SAM9261 only] */ #define AT91_PMC_HCK1 (1 << 17) /* AHB Clock (LCD) [AT91SAM9261 only] */ +#define AT91_PMC_PLL_CTRL0 0x0C /* PLL Control Register 0 [for SAM9X60] */ +#define AT91_PMC_PLL_CTRL0_ENPLL (1 << 28) /* Enable PLL */ +#define AT91_PMC_PLL_CTRL0_ENPLLCK (1 << 29) /* Enable PLL clock for PMC */ +#define AT91_PMC_PLL_CTRL0_ENLOCK (1 << 31) /* Enable PLL lock */ + +#define AT91_PMC_PLL_CTRL1 0x10 /* PLL Control Register 1 [for SAM9X60] */ + #define AT91_PMC_PCER 0x10 /* Peripheral Clock Enable Register */ #define AT91_PMC_PCDR 0x14 /* Peripheral Clock Disable Register */ #define AT91_PMC_PCSR 0x18 /* Peripheral Clock Status Register */ +#define AT91_PMC_PLL_ACR 0x18 /* PLL Analog Control Register [for SAM9X60] */ +#define AT91_PMC_PLL_ACR_DEFAULT_UPLL 0x12020010UL /* Default PLL ACR value for UPLL */ +#define AT91_PMC_PLL_ACR_DEFAULT_PLLA 0x00020010UL /* Default PLL ACR value for PLLA */ +#define AT91_PMC_PLL_ACR_UTMIVR (1 << 12) /* UPLL Voltage regulator Control */ +#define AT91_PMC_PLL_ACR_UTMIBG (1 << 13) /* UPLL Bandgap Control */ + #define AT91_CKGR_UCKR 0x1C /* UTMI Clock Register [some SAM9] */ #define AT91_PMC_UPLLEN (1 << 16) /* UTMI PLL Enable */ #define AT91_PMC_UPLLCOUNT (0xf << 20) /* UTMI PLL Start-up Time */ #define AT91_PMC_BIASEN (1 << 24) /* UTMI BIAS Enable */ #define AT91_PMC_BIASCOUNT (0xf << 28) /* UTMI BIAS Start-up Time */ +#define AT91_PMC_PLL_UPDT 0x1C /* PMC PLL update register [for SAM9X60] */ +#define AT91_PMC_PLL_UPDT_UPDATE (1 << 8) /* Update PLL settings */ +#define AT91_PMC_PLL_UPDT_ID (1 << 0) /* PLL ID */ +#define AT91_PMC_PLL_UPDT_STUPTIM (0xff << 16) /* Startup time */ + #define AT91_CKGR_MOR 0x20 /* Main Oscillator Register [not on SAM9RL] */ #define AT91_PMC_MOSCEN (1 << 0) /* Main Oscillator Enable */ #define AT91_PMC_OSCBYPASS (1 << 1) /* Oscillator Bypass */ @@ -183,6 +201,8 @@ #define AT91_PMC_WPVS (0x1 << 0) /* Write Protect Violation Status */ #define AT91_PMC_WPVSRC (0xffff << 8) /* Write Protect Violation Source */ +#define AT91_PMC_PLL_ISR0 0xEC /* PLL Interrupt Status Register 0 [SAM9X60 only] */ + #define AT91_PMC_PCER1 0x100 /* Peripheral Clock Enable Register 1 [SAMA5 only]*/ #define AT91_PMC_PCDR1 0x104 /* Peripheral Clock Enable Register 1 */ #define AT91_PMC_PCSR1 0x108 /* Peripheral Clock Enable Register 1 */ -- cgit v1.2.3 From 386dd54b3a2eedb91aa6e465e7c3a57db04f3960 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 18 Feb 2020 15:39:16 +0100 Subject: ALSA: core: Expand DMA buffer information Update DMA buffer definition for snd_compr_runtime so it is represented similarly as in snd_pcm_runtime. While at it, modify snd_compr_set_runtime_buffer to account for newly added members. Signed-off-by: Cezary Rojewski Reviewed-by: Takashi Iwai Acked-by: Vinod Koul Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200218143924.10565-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/compress_driver.h | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index bc88d6f964da..00f633c0c3ba 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -23,7 +23,6 @@ struct snd_compr_ops; * struct snd_compr_runtime: runtime stream description * @state: stream state * @ops: pointer to DSP callbacks - * @dma_buffer_p: runtime dma buffer pointer * @buffer: pointer to kernel buffer, valid only when not in mmap mode or * DSP doesn't implement copy * @buffer_size: size of the above buffer @@ -34,11 +33,14 @@ struct snd_compr_ops; * @total_bytes_transferred: cumulative bytes transferred by offload DSP * @sleep: poll sleep * @private_data: driver private data pointer + * @dma_area: virtual buffer address + * @dma_addr: physical buffer address (not accessible from main CPU) + * @dma_bytes: size of DMA area + * @dma_buffer_p: runtime dma buffer pointer */ struct snd_compr_runtime { snd_pcm_state_t state; struct snd_compr_ops *ops; - struct snd_dma_buffer *dma_buffer_p; void *buffer; u64 buffer_size; u32 fragment_size; @@ -47,6 +49,11 @@ struct snd_compr_runtime { u64 total_bytes_transferred; wait_queue_head_t sleep; void *private_data; + + unsigned char *dma_area; + dma_addr_t dma_addr; + size_t dma_bytes; + struct snd_dma_buffer *dma_buffer_p; }; /** @@ -180,19 +187,29 @@ static inline void snd_compr_drain_notify(struct snd_compr_stream *stream) /** * snd_compr_set_runtime_buffer - Set the Compress runtime buffer - * @substream: compress substream to set + * @stream: compress stream to set * @bufp: the buffer information, NULL to clear * * Copy the buffer information to runtime buffer when @bufp is non-NULL. * Otherwise it clears the current buffer information. */ -static inline void snd_compr_set_runtime_buffer( - struct snd_compr_stream *substream, - struct snd_dma_buffer *bufp) +static inline void +snd_compr_set_runtime_buffer(struct snd_compr_stream *stream, + struct snd_dma_buffer *bufp) { - struct snd_compr_runtime *runtime = substream->runtime; - - runtime->dma_buffer_p = bufp; + struct snd_compr_runtime *runtime = stream->runtime; + + if (bufp) { + runtime->dma_buffer_p = bufp; + runtime->dma_area = bufp->area; + runtime->dma_addr = bufp->addr; + runtime->dma_bytes = bufp->bytes; + } else { + runtime->dma_buffer_p = NULL; + runtime->dma_area = NULL; + runtime->dma_addr = 0; + runtime->dma_bytes = 0; + } } int snd_compr_stop_error(struct snd_compr_stream *stream, -- cgit v1.2.3 From b9759ef2fd1acb0d3f3dce7991c44a4c5e9e68a3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 18 Feb 2020 15:39:17 +0100 Subject: ALSA: core: Implement compress page allocation and free routines Add simple malloc and free methods for memory management for compress streams. Based on snd_pcm_lib_malloc_pages and snd_pcm_lib_free_pages implementation. Signed-off-by: Divya Prakash Signed-off-by: Cezary Rojewski Reviewed-by: Takashi Iwai Acked-by: Vinod Koul Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200218143924.10565-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/compress_driver.h | 5 +++++ sound/core/compress_offload.c | 42 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) (limited to 'include') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 00f633c0c3ba..6ce8effa0b12 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -67,6 +67,7 @@ struct snd_compr_runtime { * @metadata_set: metadata set flag, true when set * @next_track: has userspace signal next track transition, true when set * @private_data: pointer to DSP private data + * @dma_buffer: allocated buffer if any */ struct snd_compr_stream { const char *name; @@ -78,6 +79,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; void *private_data; + struct snd_dma_buffer dma_buffer; }; /** @@ -212,6 +214,9 @@ snd_compr_set_runtime_buffer(struct snd_compr_stream *stream, } } +int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size); +int snd_compr_free_pages(struct snd_compr_stream *stream); + int snd_compr_stop_error(struct snd_compr_stream *stream, snd_pcm_state_t state); diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 9de1c9a0173e..509290f2efa8 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -488,6 +488,48 @@ out: } #endif /* !COMPR_CODEC_CAPS_OVERFLOW */ +int snd_compr_malloc_pages(struct snd_compr_stream *stream, size_t size) +{ + struct snd_dma_buffer *dmab; + int ret; + + if (snd_BUG_ON(!(stream) || !(stream)->runtime)) + return -EINVAL; + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + dmab->dev = stream->dma_buffer.dev; + ret = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev, size, dmab); + if (ret < 0) { + kfree(dmab); + return ret; + } + + snd_compr_set_runtime_buffer(stream, dmab); + stream->runtime->dma_bytes = size; + return 1; +} +EXPORT_SYMBOL(snd_compr_malloc_pages); + +int snd_compr_free_pages(struct snd_compr_stream *stream) +{ + struct snd_compr_runtime *runtime = stream->runtime; + + if (snd_BUG_ON(!(stream) || !(stream)->runtime)) + return -EINVAL; + if (runtime->dma_area == NULL) + return 0; + if (runtime->dma_buffer_p != &stream->dma_buffer) { + /* It's a newly allocated buffer. Release it now. */ + snd_dma_free_pages(runtime->dma_buffer_p); + kfree(runtime->dma_buffer_p); + } + + snd_compr_set_runtime_buffer(stream, NULL); + return 0; +} +EXPORT_SYMBOL(snd_compr_free_pages); + /* revisit this with snd_pcm_preallocate_xxx */ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, struct snd_compr_params *params) -- cgit v1.2.3 From 4a9ce6e4d9fb9c4acc44f647a68e59ea50ff1caf Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 18 Feb 2020 15:39:18 +0100 Subject: ASoC: SOF: Intel: Account for compress streams when servicing IRQs Update stream irq handler definition to correctly set hdac_stream current position when servicing stream interrupts for compress streams. Signed-off-by: Cezary Rojewski Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200218143924.10565-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/hdaudio.h | 2 ++ sound/soc/sof/intel/hda-stream.c | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index d4299e146d95..affedc2801c4 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -513,6 +513,7 @@ struct hdac_stream { struct snd_pcm_substream *substream; /* assigned substream, * set in PCM open */ + struct snd_compr_stream *cstream; unsigned int format_val; /* format value to be set in the * controller and the codec */ @@ -527,6 +528,7 @@ struct hdac_stream { bool locked:1; bool stripe:1; /* apply stripe control */ + u64 curr_pos; /* timestamp */ unsigned long start_wallclk; /* start + minimum wallclk */ unsigned long period_wallclk; /* wallclk for period */ diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index c0ab9bb2a797..7daa913dbde0 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -571,6 +571,22 @@ bool hda_dsp_check_stream_irq(struct snd_sof_dev *sdev) return ret; } +static void +hda_dsp_set_bytes_transferred(struct hdac_stream *hstream, u64 buffer_size) +{ + u64 prev_pos, pos, num_bytes; + + div64_u64_rem(hstream->curr_pos, buffer_size, &prev_pos); + pos = snd_hdac_stream_get_pos_posbuf(hstream); + + if (pos < prev_pos) + num_bytes = (buffer_size - prev_pos) + pos; + else + num_bytes = pos - prev_pos; + + hstream->curr_pos += num_bytes; +} + static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) { struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus); @@ -588,14 +604,19 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) snd_hdac_stream_writeb(s, SD_STS, sd_status); active = true; - if (!s->substream || + if ((!s->substream && !s->cstream) || !s->running || (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) continue; /* Inform ALSA only in case not do that with IPC */ - if (sof_hda->no_ipc_position) + if (s->substream && sof_hda->no_ipc_position) { snd_sof_pcm_period_elapsed(s->substream); + } else if (s->cstream) { + hda_dsp_set_bytes_transferred(s, + s->cstream->runtime->buffer_size); + snd_compr_fragment_elapsed(s->cstream); + } } } -- cgit v1.2.3 From f3b433e4699fa358ce5b7bd7688bebe36068c199 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 18 Feb 2020 15:39:19 +0100 Subject: ASoC: SOF: Implement Probe IPC API Add all required types and methods to support each and every request that driver could sent to firmware. Probe is one of SOF firmware features which allows for data extraction and injection directly from or to DMA stream. Exposes eight IPCs: - addition and removal of injection DMAs - addition and removal of probe points - info retrieval of injection DMAs and probe points - probe initialization and cleanup Signed-off-by: Cezary Rojewski Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200218143924.10565-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- include/sound/sof/header.h | 11 ++ sound/soc/sof/Kconfig | 8 ++ sound/soc/sof/Makefile | 1 + sound/soc/sof/intel/hda-ipc.c | 4 +- sound/soc/sof/probe.c | 286 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/probe.h | 85 +++++++++++++ 6 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 sound/soc/sof/probe.c create mode 100644 sound/soc/sof/probe.h (limited to 'include') diff --git a/include/sound/sof/header.h b/include/sound/sof/header.h index bf3edd9c08b4..b79479575cc8 100644 --- a/include/sound/sof/header.h +++ b/include/sound/sof/header.h @@ -51,6 +51,7 @@ #define SOF_IPC_GLB_TRACE_MSG SOF_GLB_TYPE(0x9U) #define SOF_IPC_GLB_GDB_DEBUG SOF_GLB_TYPE(0xAU) #define SOF_IPC_GLB_TEST_MSG SOF_GLB_TYPE(0xBU) +#define SOF_IPC_GLB_PROBE SOF_GLB_TYPE(0xCU) /* * DSP Command Message Types @@ -102,6 +103,16 @@ #define SOF_IPC_STREAM_VORBIS_PARAMS SOF_CMD_TYPE(0x010) #define SOF_IPC_STREAM_VORBIS_FREE SOF_CMD_TYPE(0x011) +/* probe */ +#define SOF_IPC_PROBE_INIT SOF_CMD_TYPE(0x001) +#define SOF_IPC_PROBE_DEINIT SOF_CMD_TYPE(0x002) +#define SOF_IPC_PROBE_DMA_ADD SOF_CMD_TYPE(0x003) +#define SOF_IPC_PROBE_DMA_INFO SOF_CMD_TYPE(0x004) +#define SOF_IPC_PROBE_DMA_REMOVE SOF_CMD_TYPE(0x005) +#define SOF_IPC_PROBE_POINT_ADD SOF_CMD_TYPE(0x006) +#define SOF_IPC_PROBE_POINT_INFO SOF_CMD_TYPE(0x007) +#define SOF_IPC_PROBE_POINT_REMOVE SOF_CMD_TYPE(0x008) + /* trace */ #define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001) #define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 827b0ec92522..65c3cfbcb812 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -41,6 +41,14 @@ config SND_SOC_SOF_OF required to enable i.MX8 devices. Say Y if you need this option. If unsure select "N". +config SND_SOC_SOF_DEBUG_PROBES + bool "SOF enable data probing" + help + This option enables the data probing feature that can be used to + gather data directly from specific points of the audio pipeline. + Say Y if you want to enable probes. + If unsure, select "N". + config SND_SOC_SOF_DEVELOPER_SUPPORT bool "SOF developer options support" depends on EXPERT diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 0a8bc72c28a5..18d7cab9046e 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,6 +2,7 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o utils.o sof-audio.o +snd-sof-$(CONFIG_SND_SOC_SOF_DEBUG_PROBES) += probe.o snd-sof-pci-objs := sof-pci-dev.o snd-sof-acpi-objs := sof-acpi-dev.o diff --git a/sound/soc/sof/intel/hda-ipc.c b/sound/soc/sof/intel/hda-ipc.c index 1837f66e361f..922052883b0a 100644 --- a/sound/soc/sof/intel/hda-ipc.c +++ b/sound/soc/sof/intel/hda-ipc.c @@ -106,7 +106,9 @@ void hda_dsp_ipc_get_reply(struct snd_sof_dev *sdev) ret = reply.error; } else { /* reply correct size ? */ - if (reply.hdr.size != msg->reply_size) { + if (reply.hdr.size != msg->reply_size && + /* getter payload is never known upfront */ + !(reply.hdr.cmd & SOF_IPC_GLB_PROBE)) { dev_err(sdev->dev, "error: reply expected %zu got %u bytes\n", msg->reply_size, reply.hdr.size); ret = -EINVAL; diff --git a/sound/soc/sof/probe.c b/sound/soc/sof/probe.c new file mode 100644 index 000000000000..2b2f3dcfc7e9 --- /dev/null +++ b/sound/soc/sof/probe.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2019-2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include "sof-priv.h" +#include "probe.h" + +/** + * sof_ipc_probe_init - initialize data probing + * @sdev: SOF sound device + * @stream_tag: Extractor stream tag + * @buffer_size: DMA buffer size to set for extractor + * + * Host chooses whether extraction is supported or not by providing + * valid stream tag to DSP. Once specified, stream described by that + * tag will be tied to DSP for extraction for the entire lifetime of + * probe. + * + * Probing is initialized only once and each INIT request must be + * matched by DEINIT call. + */ +int sof_ipc_probe_init(struct snd_sof_dev *sdev, + u32 stream_tag, size_t buffer_size) +{ + struct sof_ipc_probe_dma_add_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, dma, 1); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_INIT; + msg->num_elems = 1; + msg->dma[0].stream_tag = stream_tag; + msg->dma[0].dma_buffer_size = buffer_size; + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_init); + +/** + * sof_ipc_probe_deinit - cleanup after data probing + * @sdev: SOF sound device + * + * Host sends DEINIT request to free previously initialized probe + * on DSP side once it is no longer needed. DEINIT only when there + * are no probes connected and with all injectors detached. + */ +int sof_ipc_probe_deinit(struct snd_sof_dev *sdev) +{ + struct sof_ipc_cmd_hdr msg; + struct sof_ipc_reply reply; + + msg.size = sizeof(msg); + msg.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DEINIT; + + return sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, + &reply, sizeof(reply)); +} +EXPORT_SYMBOL(sof_ipc_probe_deinit); + +static int sof_ipc_probe_info(struct snd_sof_dev *sdev, unsigned int cmd, + void **params, size_t *num_params) +{ + struct sof_ipc_probe_info_params msg = {{{0}}}; + struct sof_ipc_probe_info_params *reply; + size_t bytes; + int ret; + + *params = NULL; + *num_params = 0; + + reply = kzalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); + if (!reply) + return -ENOMEM; + msg.rhdr.hdr.size = sizeof(msg); + msg.rhdr.hdr.cmd = SOF_IPC_GLB_PROBE | cmd; + + ret = sof_ipc_tx_message(sdev->ipc, msg.rhdr.hdr.cmd, &msg, + msg.rhdr.hdr.size, reply, SOF_IPC_MSG_MAX_SIZE); + if (ret < 0 || reply->rhdr.error < 0) + goto exit; + + if (!reply->num_elems) + goto exit; + + bytes = reply->num_elems * sizeof(reply->dma[0]); + *params = kmemdup(&reply->dma[0], bytes, GFP_KERNEL); + if (!*params) { + ret = -ENOMEM; + goto exit; + } + *num_params = msg.num_elems; + +exit: + kfree(reply); + return ret; +} + +/** + * sof_ipc_probe_dma_info - retrieve list of active injection dmas + * @sdev: SOF sound device + * @dma: Returned list of active dmas + * @num_dma: Returned count of active dmas + * + * Host sends DMA_INFO request to obtain list of injection dmas it + * can use to transfer data over with. + * + * Note that list contains only injection dmas as there is only one + * extractor (dma) and it is always assigned on probing init. + * DSP knows exactly where data from extraction probes is going to, + * which is not the case for injection where multiple streams + * could be engaged. + */ +int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev, + struct sof_probe_dma **dma, size_t *num_dma) +{ + return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_DMA_INFO, + (void **)dma, num_dma); +} +EXPORT_SYMBOL(sof_ipc_probe_dma_info); + +/** + * sof_ipc_probe_dma_add - attach to specified dmas + * @sdev: SOF sound device + * @dma: List of streams (dmas) to attach to + * @num_dma: Number of elements in @dma + * + * Contrary to extraction, injection streams are never assigned + * on init. Before attempting any data injection, host is responsible + * for specifying streams which will be later used to transfer data + * to connected probe points. + */ +int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev, + struct sof_probe_dma *dma, size_t num_dma) +{ + struct sof_ipc_probe_dma_add_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, dma, num_dma); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_dma; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_ADD; + memcpy(&msg->dma[0], dma, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_dma_add); + +/** + * sof_ipc_probe_dma_remove - detach from specified dmas + * @sdev: SOF sound device + * @stream_tag: List of stream tags to detach from + * @num_stream_tag: Number of elements in @stream_tag + * + * Host sends DMA_REMOVE request to free previously attached stream + * from being occupied for injection. Each detach operation should + * match equivalent DMA_ADD. Detach only when all probes tied to + * given stream have been disconnected. + */ +int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev, + unsigned int *stream_tag, size_t num_stream_tag) +{ + struct sof_ipc_probe_dma_remove_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, stream_tag, num_stream_tag); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_stream_tag; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_DMA_REMOVE; + memcpy(&msg->stream_tag[0], stream_tag, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_dma_remove); + +/** + * sof_ipc_probe_points_info - retrieve list of active probe points + * @sdev: SOF sound device + * @desc: Returned list of active probes + * @num_desc: Returned count of active probes + * + * Host sends PROBE_POINT_INFO request to obtain list of active probe + * points, valid for disconnection when given probe is no longer + * required. + */ +int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, + struct sof_probe_point_desc **desc, size_t *num_desc) +{ + return sof_ipc_probe_info(sdev, SOF_IPC_PROBE_POINT_INFO, + (void **)desc, num_desc); +} +EXPORT_SYMBOL(sof_ipc_probe_points_info); + +/** + * sof_ipc_probe_points_add - connect specified probes + * @sdev: SOF sound device + * @desc: List of probe points to connect + * @num_desc: Number of elements in @desc + * + * Dynamically connects to provided set of endpoints. Immediately + * after connection is established, host must be prepared to + * transfer data from or to target stream given the probing purpose. + * + * Each probe point should be removed using PROBE_POINT_REMOVE + * request when no longer needed. + */ +int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, + struct sof_probe_point_desc *desc, size_t num_desc) +{ + struct sof_ipc_probe_point_add_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, desc, num_desc); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_desc; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_ADD; + memcpy(&msg->desc[0], desc, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_points_add); + +/** + * sof_ipc_probe_points_remove - disconnect specified probes + * @sdev: SOF sound device + * @buffer_id: List of probe points to disconnect + * @num_buffer_id: Number of elements in @desc + * + * Removes previously connected probes from list of active probe + * points and frees all resources on DSP side. + */ +int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, + unsigned int *buffer_id, size_t num_buffer_id) +{ + struct sof_ipc_probe_point_remove_params *msg; + struct sof_ipc_reply reply; + size_t size = struct_size(msg, buffer_id, num_buffer_id); + int ret; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + msg->hdr.size = size; + msg->num_elems = num_buffer_id; + msg->hdr.cmd = SOF_IPC_GLB_PROBE | SOF_IPC_PROBE_POINT_REMOVE; + memcpy(&msg->buffer_id[0], buffer_id, size - sizeof(*msg)); + + ret = sof_ipc_tx_message(sdev->ipc, msg->hdr.cmd, msg, msg->hdr.size, + &reply, sizeof(reply)); + kfree(msg); + return ret; +} +EXPORT_SYMBOL(sof_ipc_probe_points_remove); diff --git a/sound/soc/sof/probe.h b/sound/soc/sof/probe.h new file mode 100644 index 000000000000..45daa5552834 --- /dev/null +++ b/sound/soc/sof/probe.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2019-2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef __SOF_PROBE_H +#define __SOF_PROBE_H + +#include + +struct snd_sof_dev; + +#define SOF_PROBE_INVALID_NODE_ID UINT_MAX + +struct sof_probe_dma { + unsigned int stream_tag; + unsigned int dma_buffer_size; +} __packed; + +enum sof_connection_purpose { + SOF_CONNECTION_PURPOSE_EXTRACT = 1, + SOF_CONNECTION_PURPOSE_INJECT, +}; + +struct sof_probe_point_desc { + unsigned int buffer_id; + unsigned int purpose; + unsigned int stream_tag; +} __packed; + +struct sof_ipc_probe_dma_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_dma dma[0]; +} __packed; + +struct sof_ipc_probe_info_params { + struct sof_ipc_reply rhdr; + unsigned int num_elems; + union { + struct sof_probe_dma dma[0]; + struct sof_probe_point_desc desc[0]; + }; +} __packed; + +struct sof_ipc_probe_dma_remove_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + unsigned int stream_tag[0]; +} __packed; + +struct sof_ipc_probe_point_add_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + struct sof_probe_point_desc desc[0]; +} __packed; + +struct sof_ipc_probe_point_remove_params { + struct sof_ipc_cmd_hdr hdr; + unsigned int num_elems; + unsigned int buffer_id[0]; +} __packed; + +int sof_ipc_probe_init(struct snd_sof_dev *sdev, + u32 stream_tag, size_t buffer_size); +int sof_ipc_probe_deinit(struct snd_sof_dev *sdev); +int sof_ipc_probe_dma_info(struct snd_sof_dev *sdev, + struct sof_probe_dma **dma, size_t *num_dma); +int sof_ipc_probe_dma_add(struct snd_sof_dev *sdev, + struct sof_probe_dma *dma, size_t num_dma); +int sof_ipc_probe_dma_remove(struct snd_sof_dev *sdev, + unsigned int *stream_tag, size_t num_stream_tag); +int sof_ipc_probe_points_info(struct snd_sof_dev *sdev, + struct sof_probe_point_desc **desc, size_t *num_desc); +int sof_ipc_probe_points_add(struct snd_sof_dev *sdev, + struct sof_probe_point_desc *desc, size_t num_desc); +int sof_ipc_probe_points_remove(struct snd_sof_dev *sdev, + unsigned int *buffer_id, size_t num_buffer_id); + +#endif -- cgit v1.2.3 From ebbfabc16d23dfd20eecd4b6e68212fec37ae7c6 Mon Sep 17 00:00:00 2001 From: Derek Fang Date: Tue, 18 Feb 2020 21:51:51 +0800 Subject: ASoC: rt5682: Add CCF usage for providing I2S clks There is a need to use RT5682 as DAI clock master for other codecs within a platform, which means that the DAI clocks are required to remain, regardless of whether the RT5682 is actually running playback/capture. The RT5682 CCF basic functions are implemented almost by the existing internal functions and asoc apis. It needs a clk provider (rt5682 mclk) to generate the bclk and wclk outputs. The RT5682 CCF supports and restricts as below: 1. Fmt of DAI-AIF1 must be configured to master before using CCF. 2. Only accept a 48MHz clk as the clk provider. 3. Only provide a 48kHz wclk and a set of multiples of wclk as bclk. There are some temporary limitations in this patch until a better implementation. Signed-off-by: Derek Fang Link: https://lore.kernel.org/r/1582033912-6841-1-git-send-email-derek.fang@realtek.com Signed-off-by: Mark Brown --- include/sound/rt5682.h | 8 + sound/soc/codecs/rt5682.c | 407 +++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/rt5682.h | 4 +- 3 files changed, 415 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/rt5682.h b/include/sound/rt5682.h index bc2c31734df1..6bf0e3581056 100644 --- a/include/sound/rt5682.h +++ b/include/sound/rt5682.h @@ -24,6 +24,12 @@ enum rt5682_jd_src { RT5682_JD1, }; +enum rt5682_dai_clks { + RT5682_DAI_WCLK_IDX, + RT5682_DAI_BCLK_IDX, + RT5682_DAI_NUM_CLKS, +}; + struct rt5682_platform_data { int ldo1_en; /* GPIO for LDO1_EN */ @@ -32,6 +38,8 @@ struct rt5682_platform_data { enum rt5682_dmic1_clk_pin dmic1_clk_pin; enum rt5682_jd_src jd_src; unsigned int btndet_delay; + + const char *dai_clk_names[RT5682_DAI_NUM_CLKS]; }; #endif diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 9fbb3862f8d7..6774813e0eea 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include +#include #include #include "rl6231.h" @@ -45,6 +48,8 @@ static const struct rt5682_platform_data i2s_default_platform_data = { .dmic1_clk_pin = RT5682_DMIC1_CLK_GPIO3, .jd_src = RT5682_JD1, .btndet_delay = 16, + .dai_clk_names[RT5682_DAI_WCLK_IDX] = "rt5682-dai-wclk", + .dai_clk_names[RT5682_DAI_BCLK_IDX] = "rt5682-dai-bclk", }; struct rt5682_priv { @@ -58,6 +63,13 @@ struct rt5682_priv { struct mutex calibrate_mutex; bool is_sdw; +#ifdef CONFIG_COMMON_CLK + struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS]; + struct clk_lookup *dai_clks_lookup[RT5682_DAI_NUM_CLKS]; + struct clk *dai_clks[RT5682_DAI_NUM_CLKS]; + struct clk *mclk; +#endif + int sysclk; int sysclk_src; int lrck[RT5682_AIFS]; @@ -921,6 +933,7 @@ static int rt5682_headset_detect(struct snd_soc_component *component, int jack_insert) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = &component->dapm; unsigned int val, count; if (jack_insert) { @@ -963,8 +976,13 @@ static int rt5682_headset_detect(struct snd_soc_component *component, rt5682_enable_push_button_irq(component, false); snd_soc_component_update_bits(component, RT5682_CBJ_CTRL_1, RT5682_TRIG_JD_MASK, RT5682_TRIG_JD_LOW); - snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, - RT5682_PWR_VREF2 | RT5682_PWR_MB, 0); + if (snd_soc_dapm_get_pin_status(dapm, "MICBIAS")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0); + else + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_MB, 0); snd_soc_component_update_bits(component, RT5682_PWR_ANLG_3, RT5682_PWR_CBJ, 0); @@ -1633,6 +1651,7 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { rt5655_set_verf, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_SUPPLY("Vref2", RT5682_PWR_ANLG_1, RT5682_PWR_VREF2_BIT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS", SND_SOC_NOPM, 0, 0, NULL, 0), /* ASRC */ SND_SOC_DAPM_SUPPLY_S("DAC STO1 ASRC", 1, RT5682_PLL_TRACK_1, @@ -2459,12 +2478,380 @@ static int rt5682_set_bias_level(struct snd_soc_component *component, return 0; } +#ifdef CONFIG_COMMON_CLK +#define CLK_PLL2_FIN 48000000 +#define CLK_PLL2_FOUT 24576000 +#define CLK_48 48000 + +static bool rt5682_clk_check(struct rt5682_priv *rt5682) +{ + if (!rt5682->master[RT5682_AIF1]) { + dev_err(rt5682->component->dev, "sysclk/dai not set correctly\n"); + return false; + } + return true; +} + +static int rt5682_wclk_prepare(struct clk_hw *hw) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_force_enable_pin_unlocked(dapm, "MICBIAS"); + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, RT5682_PWR_MB); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "I2S1"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2F"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "PLL2B"); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + + return 0; +} + +static void rt5682_wclk_unprepare(struct clk_hw *hw) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + + if (!rt5682_clk_check(rt5682)) + return; + + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_disable_pin_unlocked(dapm, "MICBIAS"); + if (!rt5682->jack_type) + snd_soc_component_update_bits(component, RT5682_PWR_ANLG_1, + RT5682_PWR_MB, 0); + snd_soc_dapm_disable_pin_unlocked(dapm, "I2S1"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2F"); + snd_soc_dapm_disable_pin_unlocked(dapm, "PLL2B"); + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + + if (!rt5682_clk_check(rt5682)) + return 0; + /* + * Only accept to set wclk rate to 48kHz temporarily. + */ + return CLK_48; +} + +static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + /* + * Only accept to set wclk rate to 48kHz temporarily. + */ + return CLK_48; +} + +static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_WCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct clk *parent_clk; + const char * const clk_name = __clk_get_name(hw->clk); + int pre_div; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + /* + * Whether the wclk's parent clk (mclk) exists or not, please ensure + * it is fixed or set to 48MHz before setting wclk rate. It's a + * temporary limitation. Only accept 48MHz clk as the clk provider. + * + * It will set the codec anyway by assuming mclk is 48MHz. + */ + parent_clk = clk_get_parent(hw->clk); + if (!parent_clk) + dev_warn(component->dev, + "Parent mclk of wclk not acquired in driver. Please ensure mclk was provided as %d Hz.\n", + CLK_PLL2_FIN); + + if (parent_rate != CLK_PLL2_FIN) + dev_warn(component->dev, "clk %s only support %d Hz input\n", + clk_name, CLK_PLL2_FIN); + + /* + * It's a temporary limitation. Only accept to set wclk rate to 48kHz. + * It will force wclk to 48kHz even it's not. + */ + if (rate != CLK_48) { + dev_warn(component->dev, "clk %s only support %d Hz output\n", + clk_name, CLK_48); + rate = CLK_48; + } + + /* + * To achieve the rate conversion from 48MHz to 48kHz, PLL2 is needed. + */ + rt5682_set_component_pll(component, RT5682_PLL2, RT5682_PLL2_S_MCLK, + CLK_PLL2_FIN, CLK_PLL2_FOUT); + + rt5682_set_component_sysclk(component, RT5682_SCLK_S_PLL2, 0, + CLK_PLL2_FOUT, SND_SOC_CLOCK_IN); + + pre_div = rl6231_get_clk_info(rt5682->sysclk, rate); + + snd_soc_component_update_bits(component, RT5682_ADDA_CLK_1, + RT5682_I2S_M_DIV_MASK | RT5682_I2S_CLK_SRC_MASK, + pre_div << RT5682_I2S_M_DIV_SFT | + (rt5682->sysclk_src) << RT5682_I2S_CLK_SRC_SFT); + + return 0; +} + +static unsigned long rt5682_bclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + unsigned int bclks_per_wclk; + + snd_soc_component_read(component, RT5682_TDM_TCON_CTRL, + &bclks_per_wclk); + + switch (bclks_per_wclk & RT5682_TDM_BCLK_MS1_MASK) { + case RT5682_TDM_BCLK_MS1_256: + return parent_rate * 256; + case RT5682_TDM_BCLK_MS1_128: + return parent_rate * 128; + case RT5682_TDM_BCLK_MS1_64: + return parent_rate * 64; + case RT5682_TDM_BCLK_MS1_32: + return parent_rate * 32; + default: + return 0; + } +} + +static unsigned long rt5682_bclk_get_factor(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long factor; + + factor = rate / parent_rate; + if (factor < 64) + return 32; + else if (factor < 128) + return 64; + else if (factor < 256) + return 128; + else + return 256; +} + +static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + unsigned long factor; + + if (!*parent_rate || !rt5682_clk_check(rt5682)) + return -EINVAL; + + /* + * BCLK rates are set as a multiplier of WCLK in HW. + * We don't allow changing the parent WCLK. We just do + * some rounding down based on the parent WCLK rate + * and find the appropriate multiplier of BCLK to + * get the rounded down BCLK value. + */ + factor = rt5682_bclk_get_factor(rate, *parent_rate); + + return *parent_rate * factor; +} + +static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct rt5682_priv *rt5682 = + container_of(hw, struct rt5682_priv, + dai_clks_hw[RT5682_DAI_BCLK_IDX]); + struct snd_soc_component *component = rt5682->component; + struct snd_soc_dai *dai = NULL; + unsigned long factor; + + if (!rt5682_clk_check(rt5682)) + return -EINVAL; + + factor = rt5682_bclk_get_factor(rate, parent_rate); + + for_each_component_dais(component, dai) + if (dai->id == RT5682_AIF1) + break; + if (!dai) { + dev_err(component->dev, "dai %d not found in component\n", + RT5682_AIF1); + return -ENODEV; + } + + return rt5682_set_bclk1_ratio(dai, factor); +} + +static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = { + [RT5682_DAI_WCLK_IDX] = { + .prepare = rt5682_wclk_prepare, + .unprepare = rt5682_wclk_unprepare, + .recalc_rate = rt5682_wclk_recalc_rate, + .round_rate = rt5682_wclk_round_rate, + .set_rate = rt5682_wclk_set_rate, + }, + [RT5682_DAI_BCLK_IDX] = { + .recalc_rate = rt5682_bclk_recalc_rate, + .round_rate = rt5682_bclk_round_rate, + .set_rate = rt5682_bclk_set_rate, + }, +}; + +static int rt5682_register_dai_clks(struct snd_soc_component *component) +{ + struct device *dev = component->dev; + struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); + struct rt5682_platform_data *pdata = &rt5682->pdata; + struct clk_init_data init; + struct clk *dai_clk; + struct clk_lookup *dai_clk_lookup; + struct clk_hw *dai_clk_hw; + const char *parent_name; + int i, ret; + + for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { + dai_clk_hw = &rt5682->dai_clks_hw[i]; + + switch (i) { + case RT5682_DAI_WCLK_IDX: + /* Make MCLK the parent of WCLK */ + if (rt5682->mclk) { + parent_name = __clk_get_name(rt5682->mclk); + init.parent_names = &parent_name; + init.num_parents = 1; + } else { + init.parent_names = NULL; + init.num_parents = 0; + } + break; + case RT5682_DAI_BCLK_IDX: + /* Make WCLK the parent of BCLK */ + parent_name = __clk_get_name( + rt5682->dai_clks[RT5682_DAI_WCLK_IDX]); + init.parent_names = &parent_name; + init.num_parents = 1; + break; + default: + dev_err(dev, "Invalid clock index\n"); + ret = -EINVAL; + goto err; + } + + init.name = pdata->dai_clk_names[i]; + init.ops = &rt5682_dai_clk_ops[i]; + init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; + dai_clk_hw->init = &init; + + dai_clk = devm_clk_register(dev, dai_clk_hw); + if (IS_ERR(dai_clk)) { + dev_warn(dev, "Failed to register %s: %ld\n", + init.name, PTR_ERR(dai_clk)); + ret = PTR_ERR(dai_clk); + goto err; + } + rt5682->dai_clks[i] = dai_clk; + + if (dev->of_node) { + devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + dai_clk_hw); + } else { + dai_clk_lookup = clkdev_create(dai_clk, init.name, + "%s", dev_name(dev)); + if (!dai_clk_lookup) { + ret = -ENOMEM; + goto err; + } else { + rt5682->dai_clks_lookup[i] = dai_clk_lookup; + } + } + } + + return 0; + +err: + do { + if (rt5682->dai_clks_lookup[i]) + clkdev_drop(rt5682->dai_clks_lookup[i]); + } while (i-- > 0); + + return ret; +} +#endif /* CONFIG_COMMON_CLK */ + static int rt5682_probe(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); +#ifdef CONFIG_COMMON_CLK + int ret; +#endif rt5682->component = component; +#ifdef CONFIG_COMMON_CLK + /* Check if MCLK provided */ + rt5682->mclk = devm_clk_get(component->dev, "mclk"); + if (IS_ERR(rt5682->mclk)) { + if (PTR_ERR(rt5682->mclk) != -ENOENT) { + ret = PTR_ERR(rt5682->mclk); + return ret; + } + rt5682->mclk = NULL; + } + + /* Register CCF DAI clock control */ + ret = rt5682_register_dai_clks(component); + if (ret) + return ret; + + /* Initial setup for CCF */ + rt5682->lrck[RT5682_AIF1] = CLK_48; +#endif + return 0; } @@ -2472,6 +2859,15 @@ static void rt5682_remove(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); +#ifdef CONFIG_COMMON_CLK + int i; + + for (i = RT5682_DAI_NUM_CLKS - 1; i >= 0; --i) { + if (rt5682->dai_clks_lookup[i]) + clkdev_drop(rt5682->dai_clks_lookup[i]); + } +#endif + rt5682_reset(rt5682); } @@ -2606,6 +3002,13 @@ static int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev) rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node, "realtek,ldo1-en-gpios", 0); + if (device_property_read_string_array(dev, "clock-output-names", + rt5682->pdata.dai_clk_names, + RT5682_DAI_NUM_CLKS) < 0) + dev_warn(dev, "Using default DAI clk names: %s, %s\n", + rt5682->pdata.dai_clk_names[RT5682_DAI_WCLK_IDX], + rt5682->pdata.dai_clk_names[RT5682_DAI_BCLK_IDX]); + return 0; } diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 465c99b7f906..f82126a6f211 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -841,8 +841,8 @@ #define RT5682_TDM_M_LP_INV (0x1 << 1) #define RT5682_TDM_MS_MASK (0x1 << 0) #define RT5682_TDM_MS_SFT 0 -#define RT5682_TDM_MS_M (0x0 << 0) -#define RT5682_TDM_MS_S (0x1 << 0) +#define RT5682_TDM_MS_S (0x0 << 0) +#define RT5682_TDM_MS_M (0x1 << 0) /* Global Clock Control (0x0080) */ #define RT5682_SCLK_SRC_MASK (0x7 << 13) -- cgit v1.2.3 From c9645d2a952b7925b6708b24242cd5ed04975648 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 17 Feb 2020 17:27:57 +0900 Subject: ASoC: soc-pcm: remove soc_dpcm_be_digital_mute() No one is using soc_dpcm_be_digital_mute(). If it exists only by assumption that "it may be necessary someday", let's remove it now. Otherwise code maintenance will be difficult. We can revive it when we really needed it. Let's remove it, so far. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87blpxbouc.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 1 - sound/soc/soc-pcm.c | 27 --------------------------- 2 files changed, 28 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index b654ebfc8766..665516387671 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -141,7 +141,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, enum snd_soc_dpcm_state state); /* internal use only */ -int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); int soc_dpcm_runtime_update(struct snd_soc_card *); #ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 1d48be24bfaa..b8ea4d892031 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2719,33 +2719,6 @@ out: mutex_unlock(&card->mutex); return ret; } -int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute) -{ - struct snd_soc_dpcm *dpcm; - struct snd_soc_dai *dai; - - for_each_dpcm_be(fe, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { - - struct snd_soc_pcm_runtime *be = dpcm->be; - int i; - - if (be->dai_link->ignore_suspend) - continue; - - for_each_rtd_codec_dai(be, i, dai) { - struct snd_soc_dai_driver *drv = dai->driver; - - dev_dbg(be->dev, "ASoC: BE digital mute %s\n", - be->dai_link->name); - - if (drv->ops && drv->ops->digital_mute && - dai->playback_active) - drv->ops->digital_mute(dai, mute); - } - } - - return 0; -} static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream) { -- cgit v1.2.3 From 289a7e64f8583aaa45847c7fa3b7fabf8d48fd6b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 17 Feb 2020 17:28:04 +0900 Subject: ASoC: soc-pcm: remove snd_soc_dpcm_be_get/set_state() No one is using snd_soc_dpcm_be_get/set_state(). If it exists only by assumption that "it may be necessary someday", let's remove it now. Otherwise code maintenance will be difficult. We can revive it when we really needed it. Let's remove it, so far. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87a75hbou7.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 8 -------- sound/soc/soc-pcm.c | 16 ---------------- 2 files changed, 24 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 665516387671..3e7819d2a6aa 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -132,14 +132,6 @@ int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream * snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream); -/* get the BE runtime state */ -enum snd_soc_dpcm_state - snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream); - -/* set the BE runtime state */ -void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, - enum snd_soc_dpcm_state state); - /* internal use only */ int soc_dpcm_runtime_update(struct snd_soc_card *); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index b8ea4d892031..bd4e4f86f5b2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2954,22 +2954,6 @@ struct snd_pcm_substream * } EXPORT_SYMBOL_GPL(snd_soc_dpcm_get_substream); -/* get the BE runtime state */ -enum snd_soc_dpcm_state - snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream) -{ - return be->dpcm[stream].state; -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_get_state); - -/* set the BE runtime state */ -void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, - int stream, enum snd_soc_dpcm_state state) -{ - be->dpcm[stream].state = state; -} -EXPORT_SYMBOL_GPL(snd_soc_dpcm_be_set_state); - /* * We can only hw_free, stop, pause or suspend a BE DAI if any of it's FE * are not running, paused or suspended for the specified stream direction. -- cgit v1.2.3 From 0f6011fd79a2fb92cb80177fd6bdc8aac3a3cd93 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 17 Feb 2020 17:28:15 +0900 Subject: ASoC: soc-pcm: merge playback/cature_active into stream_active DAI has playback_active and capture_active to care usage count. OTOH, we have SNDRV_PCM_STREAM_PLAYBACK/CAPTURE. But because of this kind of implementation mismatch, ALSA SoC has many verbose code. To solve this issue, this patch merge playback_active/capture_active into stream_active[2]; Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/875zg5botu.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 3 +-- sound/soc/codecs/cs4271.c | 4 ++-- sound/soc/dwc/dwc-i2s.c | 4 ++-- sound/soc/soc-core.c | 17 +++++++++-------- sound/soc/soc-pcm.c | 25 ++++++++++++------------- 5 files changed, 26 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 04c23ac0dfff..7481e468be39 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -322,8 +322,7 @@ struct snd_soc_dai { struct snd_soc_dai_driver *driver; /* DAI runtime info */ - unsigned int capture_active; /* stream usage count */ - unsigned int playback_active; /* stream usage count */ + unsigned int stream_active[SNDRV_PCM_STREAM_LAST + 1]; /* usage count */ unsigned int active; diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 04b86a51e055..62f412d6f9f2 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -356,9 +356,9 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, */ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - !dai->capture_active) || + !dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]) || (substream->stream == SNDRV_PCM_STREAM_CAPTURE && - !dai->playback_active)) { + !dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK])) { ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, CS4271_MODE2_PDN, CS4271_MODE2_PDN); diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c index 7eeca2150b2d..a8bff6f08a69 100644 --- a/sound/soc/dwc/dwc-i2s.c +++ b/sound/soc/dwc/dwc-i2s.c @@ -427,9 +427,9 @@ static int dw_i2s_resume(struct snd_soc_component *component) clk_enable(dev->clk); for_each_component_dais(component, dai) { - if (dai->playback_active) + if (dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]) dw_i2s_config(dev, SNDRV_PCM_STREAM_PLAYBACK); - if (dai->capture_active) + if (dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]) dw_i2s_config(dev, SNDRV_PCM_STREAM_CAPTURE); } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6a58a8f6e3c4..f0ae1a7d7e09 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -365,19 +365,20 @@ EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime); void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = rtd->codec_dai; + int playback = SNDRV_PCM_STREAM_PLAYBACK; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", codec_dai->driver->playback.stream_name, - codec_dai->playback_active ? "active" : "inactive", + codec_dai->stream_active[playback] ? "active" : "inactive", rtd->pop_wait ? "yes" : "no"); /* are we waiting on this codec DAI stream */ if (rtd->pop_wait == 1) { rtd->pop_wait = 0; - snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + snd_soc_dapm_stream_event(rtd, playback, SND_SOC_DAPM_STREAM_STOP); } @@ -514,6 +515,7 @@ int snd_soc_suspend(struct device *dev) struct snd_soc_card *card = dev_get_drvdata(dev); struct snd_soc_component *component; struct snd_soc_pcm_runtime *rtd; + int playback = SNDRV_PCM_STREAM_PLAYBACK; int i; /* If the card is not initialized yet there is nothing to do */ @@ -537,9 +539,8 @@ int snd_soc_suspend(struct device *dev) continue; for_each_rtd_codec_dai(rtd, i, dai) { - if (dai->playback_active) - snd_soc_dai_digital_mute(dai, 1, - SNDRV_PCM_STREAM_PLAYBACK); + if (dai->stream_active[playback]) + snd_soc_dai_digital_mute(dai, 1, playback); } } @@ -680,14 +681,14 @@ static void soc_resume_deferred(struct work_struct *work) /* unmute any active DACs */ for_each_card_rtds(card, rtd) { struct snd_soc_dai *dai; + int playback = SNDRV_PCM_STREAM_PLAYBACK; if (rtd->dai_link->ignore_suspend) continue; for_each_rtd_codec_dai(rtd, i, dai) { - if (dai->playback_active) - snd_soc_dai_digital_mute(dai, 0, - SNDRV_PCM_STREAM_PLAYBACK); + if (dai->stream_active[playback]) + snd_soc_dai_digital_mute(dai, 0, playback); } } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 5a79a830ee18..6fd69574ca31 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -100,15 +100,9 @@ static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, lockdep_assert_held(&rtd->card->pcm_mutex); - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - cpu_dai->playback_active += action; - for_each_rtd_codec_dai(rtd, i, codec_dai) - codec_dai->playback_active += action; - } else { - cpu_dai->capture_active += action; - for_each_rtd_codec_dai(rtd, i, codec_dai) - codec_dai->capture_active += action; - } + cpu_dai->stream_active[stream] += action; + for_each_rtd_codec_dai(rtd, i, codec_dai) + codec_dai->stream_active[stream] += action; cpu_dai->active += action; cpu_dai->component->active += action; @@ -967,8 +961,11 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) /* apply codec digital mute */ for_each_rtd_codec_dai(rtd, i, codec_dai) { - if ((playback && codec_dai->playback_active == 1) || - (!playback && codec_dai->capture_active == 1)) + int playback_active = codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]; + int capture_active = codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]; + + if ((playback && playback_active == 1) || + (!playback && capture_active == 1)) snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); } @@ -2634,7 +2631,8 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) goto capture; /* skip if FE isn't currently playing */ - if (!fe->cpu_dai->playback_active || !fe->codec_dai->playback_active) + if (!fe->cpu_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK] || + !fe->codec_dai->stream_active[SNDRV_PCM_STREAM_PLAYBACK]) goto capture; paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list); @@ -2665,7 +2663,8 @@ capture: return 0; /* skip if FE isn't currently capturing */ - if (!fe->cpu_dai->capture_active || !fe->codec_dai->capture_active) + if (!fe->cpu_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE] || + !fe->codec_dai->stream_active[SNDRV_PCM_STREAM_CAPTURE]) return 0; paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list); -- cgit v1.2.3 From 3193abd26b515ccac65e1c323533cb7f53d06176 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 17 Feb 2020 17:28:19 +0900 Subject: ALSA: pcm.h: add for_each_pcm_streams() ALSA code has SNDRV_PCM_STREAM_PLAYBACK/CAPTURE everywhere. Having for_each_xxxx macro is useful. This patch adds for_each_pcm_streams() for it. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Acked-by: Takashi Iwai Link: https://lore.kernel.org/r/874kvpbotq.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/pcm.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index f657ff08f317..2628246b76fa 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -644,6 +644,11 @@ void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream, #define snd_pcm_group_for_each_entry(s, substream) \ list_for_each_entry(s, &substream->group->substreams, link_list) +#define for_each_pcm_streams(stream) \ + for (stream = SNDRV_PCM_STREAM_PLAYBACK; \ + stream <= SNDRV_PCM_STREAM_LAST; \ + stream++) + /** * snd_pcm_running - Check whether the substream is in a running state * @substream: substream to check -- cgit v1.2.3 From b72bfc965eb5d3475acabb038a1f9f6034c4658d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 13 Feb 2020 15:19:11 -0400 Subject: RDMA/core: Get rid of ib_create_qp_user This function accepts a udata but does nothing with it, and is never passed a !NULL udata. Rename it to ib_create_qp which was the only caller and remove the udata. Link: https://lore.kernel.org/r/20200213191911.GA9898@ziepe.ca Reviewed-by: Max Gurtovoy Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/verbs.c | 22 +++++++++++++++------- include/rdma/ib_verbs.h | 31 ++----------------------------- 2 files changed, 17 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 3ebae3b65c28..13ff4c8bfe5a 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -1127,8 +1127,7 @@ struct ib_qp *ib_open_qp(struct ib_xrcd *xrcd, EXPORT_SYMBOL(ib_open_qp); static struct ib_qp *create_xrc_qp_user(struct ib_qp *qp, - struct ib_qp_init_attr *qp_init_attr, - struct ib_udata *udata) + struct ib_qp_init_attr *qp_init_attr) { struct ib_qp *real_qp = qp; @@ -1150,9 +1149,18 @@ static struct ib_qp *create_xrc_qp_user(struct ib_qp *qp, return qp; } -struct ib_qp *ib_create_qp_user(struct ib_pd *pd, - struct ib_qp_init_attr *qp_init_attr, - struct ib_udata *udata) +/** + * ib_create_qp - Creates a kernel QP associated with the specified protection + * domain. + * @pd: The protection domain associated with the QP. + * @qp_init_attr: A list of initial attributes required to create the + * QP. If QP creation succeeds, then the attributes are updated to + * the actual capabilities of the created QP. + * + * NOTE: for user qp use ib_create_qp_user with valid udata! + */ +struct ib_qp *ib_create_qp(struct ib_pd *pd, + struct ib_qp_init_attr *qp_init_attr) { struct ib_device *device = pd ? pd->device : qp_init_attr->xrcd->device; struct ib_qp *qp; @@ -1197,7 +1205,7 @@ struct ib_qp *ib_create_qp_user(struct ib_pd *pd, if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) { struct ib_qp *xrc_qp = - create_xrc_qp_user(qp, qp_init_attr, udata); + create_xrc_qp_user(qp, qp_init_attr); if (IS_ERR(xrc_qp)) { ret = PTR_ERR(xrc_qp); @@ -1253,7 +1261,7 @@ err: return ERR_PTR(ret); } -EXPORT_SYMBOL(ib_create_qp_user); +EXPORT_SYMBOL(ib_create_qp); static const struct { int valid; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 1f779fad3a1e..5f3a04ead9f5 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -3627,35 +3627,8 @@ static inline int ib_post_srq_recv(struct ib_srq *srq, bad_recv_wr ? : &dummy); } -/** - * ib_create_qp_user - Creates a QP associated with the specified protection - * domain. - * @pd: The protection domain associated with the QP. - * @qp_init_attr: A list of initial attributes required to create the - * QP. If QP creation succeeds, then the attributes are updated to - * the actual capabilities of the created QP. - * @udata: Valid user data or NULL for kernel objects - */ -struct ib_qp *ib_create_qp_user(struct ib_pd *pd, - struct ib_qp_init_attr *qp_init_attr, - struct ib_udata *udata); - -/** - * ib_create_qp - Creates a kernel QP associated with the specified protection - * domain. - * @pd: The protection domain associated with the QP. - * @qp_init_attr: A list of initial attributes required to create the - * QP. If QP creation succeeds, then the attributes are updated to - * the actual capabilities of the created QP. - * @udata: Valid user data or NULL for kernel objects - * - * NOTE: for user qp use ib_create_qp_user with valid udata! - */ -static inline struct ib_qp *ib_create_qp(struct ib_pd *pd, - struct ib_qp_init_attr *qp_init_attr) -{ - return ib_create_qp_user(pd, qp_init_attr, NULL); -} +struct ib_qp *ib_create_qp(struct ib_pd *pd, + struct ib_qp_init_attr *qp_init_attr); /** * ib_modify_qp_with_udata - Modifies the attributes for the specified QP. -- cgit v1.2.3 From 573ed90aa5e23b512168400ba6d65e592081944e Mon Sep 17 00:00:00 2001 From: Aya Levin Date: Tue, 11 Feb 2020 14:32:42 -0800 Subject: devlink: Force enclosing array on binary fmsg data Add a new API for start/end binary array brackets [] to force array around binary data as required from JSON. With this restriction, re-open API to set binary fmsg data. Signed-off-by: Aya Levin Reviewed-by: Jiri Pirko Signed-off-by: Saeed Mahameed --- include/net/devlink.h | 5 +++ net/core/devlink.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/devlink.h b/include/net/devlink.h index ce5cea428fdc..149c108be66f 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -981,12 +981,17 @@ int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg); int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg, const char *name); int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg); +int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg, + const char *name); +int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg); int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value); int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value); int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value); int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value); int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value); +int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value, + u16 value_len); int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name, bool value); diff --git a/net/core/devlink.c b/net/core/devlink.c index 549ee56b7a21..216bdd25ce39 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -4237,6 +4237,12 @@ struct devlink_fmsg_item { struct devlink_fmsg { struct list_head item_list; + bool putting_binary; /* This flag forces enclosing of binary data + * in an array brackets. It forces using + * of designated API: + * devlink_fmsg_binary_pair_nest_start() + * devlink_fmsg_binary_pair_nest_end() + */ }; static struct devlink_fmsg *devlink_fmsg_alloc(void) @@ -4280,17 +4286,26 @@ static int devlink_fmsg_nest_common(struct devlink_fmsg *fmsg, int devlink_fmsg_obj_nest_start(struct devlink_fmsg *fmsg) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_OBJ_NEST_START); } EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_start); static int devlink_fmsg_nest_end(struct devlink_fmsg *fmsg) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_NEST_END); } int devlink_fmsg_obj_nest_end(struct devlink_fmsg *fmsg) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_nest_end(fmsg); } EXPORT_SYMBOL_GPL(devlink_fmsg_obj_nest_end); @@ -4301,6 +4316,9 @@ static int devlink_fmsg_put_name(struct devlink_fmsg *fmsg, const char *name) { struct devlink_fmsg_item *item; + if (fmsg->putting_binary) + return -EINVAL; + if (strlen(name) + 1 > DEVLINK_FMSG_MAX_SIZE) return -EMSGSIZE; @@ -4321,6 +4339,9 @@ int devlink_fmsg_pair_nest_start(struct devlink_fmsg *fmsg, const char *name) { int err; + if (fmsg->putting_binary) + return -EINVAL; + err = devlink_fmsg_nest_common(fmsg, DEVLINK_ATTR_FMSG_PAIR_NEST_START); if (err) return err; @@ -4335,6 +4356,9 @@ EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_start); int devlink_fmsg_pair_nest_end(struct devlink_fmsg *fmsg) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_nest_end(fmsg); } EXPORT_SYMBOL_GPL(devlink_fmsg_pair_nest_end); @@ -4344,6 +4368,9 @@ int devlink_fmsg_arr_pair_nest_start(struct devlink_fmsg *fmsg, { int err; + if (fmsg->putting_binary) + return -EINVAL; + err = devlink_fmsg_pair_nest_start(fmsg, name); if (err) return err; @@ -4360,6 +4387,9 @@ int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg) { int err; + if (fmsg->putting_binary) + return -EINVAL; + err = devlink_fmsg_nest_end(fmsg); if (err) return err; @@ -4372,6 +4402,30 @@ int devlink_fmsg_arr_pair_nest_end(struct devlink_fmsg *fmsg) } EXPORT_SYMBOL_GPL(devlink_fmsg_arr_pair_nest_end); +int devlink_fmsg_binary_pair_nest_start(struct devlink_fmsg *fmsg, + const char *name) +{ + int err; + + err = devlink_fmsg_arr_pair_nest_start(fmsg, name); + if (err) + return err; + + fmsg->putting_binary = true; + return err; +} +EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_start); + +int devlink_fmsg_binary_pair_nest_end(struct devlink_fmsg *fmsg) +{ + if (!fmsg->putting_binary) + return -EINVAL; + + fmsg->putting_binary = false; + return devlink_fmsg_arr_pair_nest_end(fmsg); +} +EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_nest_end); + static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg, const void *value, u16 value_len, u8 value_nla_type) @@ -4396,40 +4450,59 @@ static int devlink_fmsg_put_value(struct devlink_fmsg *fmsg, int devlink_fmsg_bool_put(struct devlink_fmsg *fmsg, bool value) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_FLAG); } EXPORT_SYMBOL_GPL(devlink_fmsg_bool_put); int devlink_fmsg_u8_put(struct devlink_fmsg *fmsg, u8 value) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U8); } EXPORT_SYMBOL_GPL(devlink_fmsg_u8_put); int devlink_fmsg_u32_put(struct devlink_fmsg *fmsg, u32 value) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U32); } EXPORT_SYMBOL_GPL(devlink_fmsg_u32_put); int devlink_fmsg_u64_put(struct devlink_fmsg *fmsg, u64 value) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_put_value(fmsg, &value, sizeof(value), NLA_U64); } EXPORT_SYMBOL_GPL(devlink_fmsg_u64_put); int devlink_fmsg_string_put(struct devlink_fmsg *fmsg, const char *value) { + if (fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_put_value(fmsg, value, strlen(value) + 1, NLA_NUL_STRING); } EXPORT_SYMBOL_GPL(devlink_fmsg_string_put); -static int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value, - u16 value_len) +int devlink_fmsg_binary_put(struct devlink_fmsg *fmsg, const void *value, + u16 value_len) { + if (!fmsg->putting_binary) + return -EINVAL; + return devlink_fmsg_put_value(fmsg, value, value_len, NLA_BINARY); } +EXPORT_SYMBOL_GPL(devlink_fmsg_binary_put); int devlink_fmsg_bool_pair_put(struct devlink_fmsg *fmsg, const char *name, bool value) @@ -4540,10 +4613,11 @@ int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name, const void *value, u32 value_len) { u32 data_size; + int end_err; u32 offset; int err; - err = devlink_fmsg_arr_pair_nest_start(fmsg, name); + err = devlink_fmsg_binary_pair_nest_start(fmsg, name); if (err) return err; @@ -4553,14 +4627,18 @@ int devlink_fmsg_binary_pair_put(struct devlink_fmsg *fmsg, const char *name, data_size = DEVLINK_FMSG_MAX_SIZE; err = devlink_fmsg_binary_put(fmsg, value + offset, data_size); if (err) - return err; + break; + /* Exit from loop with a break (instead of + * return) to make sure putting_binary is turned off in + * devlink_fmsg_binary_pair_nest_end + */ } - err = devlink_fmsg_arr_pair_nest_end(fmsg); - if (err) - return err; + end_err = devlink_fmsg_binary_pair_nest_end(fmsg); + if (end_err) + err = end_err; - return 0; + return err; } EXPORT_SYMBOL_GPL(devlink_fmsg_binary_pair_put); -- cgit v1.2.3 From 12206b17235aed1ca6390b3e516825ae276f8345 Mon Sep 17 00:00:00 2001 From: Aya Levin Date: Tue, 11 Feb 2020 14:32:43 -0800 Subject: net/mlx5: Add support for resource dump On driver load: - Initialize resource dump data structure and memory access tools (mkey & pd). - Read the resource dump's menu which contains the FW segment identifier. Each record is identified by the segment name (ASCII). During the driver's course of life, users (like reporters) may request dumps per segment. The user should create a command providing the segment identifier (SW enumeration) and command keys. In return, the user receives a command context. In order to receive the dump, the user should supply the command context and a memory (aligned to a PAGE) on which the dump content will be written. Since the dump may be larger than the given memory, the user may resubmit the command until received an indication of end-of-dump. It is the user's responsibility to destroy the command. Signed-off-by: Aya Levin Reviewed-by: Moshe Shemesh Acked-by: Jiri Pirko Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/Makefile | 2 +- .../ethernet/mellanox/mlx5/core/diag/rsc_dump.c | 286 +++++++++++++++++++++ .../ethernet/mellanox/mlx5/core/diag/rsc_dump.h | 58 +++++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 12 + include/linux/mlx5/driver.h | 1 + 5 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.c create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.h (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index d3e06cec8317..e0bb8e12356e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -16,7 +16,7 @@ mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ transobj.o vport.o sriov.o fs_cmd.o fs_core.o pci_irq.o \ fs_counters.o rl.o lag.o dev.o events.o wq.o lib/gid.o \ lib/devcom.o lib/pci_vsc.o lib/dm.o diag/fs_tracepoint.o \ - diag/fw_tracer.o diag/crdump.o devlink.o + diag/fw_tracer.o diag/crdump.o devlink.o diag/rsc_dump.o # # Netdev basic diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.c new file mode 100644 index 000000000000..17ab7efe693d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* Copyright (c) 2019 Mellanox Technologies. */ + +#include "rsc_dump.h" +#include "lib/mlx5.h" + +#define MLX5_SGMT_TYPE(SGMT) MLX5_SGMT_TYPE_##SGMT +#define MLX5_SGMT_STR_ASSING(SGMT)[MLX5_SGMT_TYPE(SGMT)] = #SGMT +static const char *const mlx5_rsc_sgmt_name[] = { + MLX5_SGMT_STR_ASSING(HW_CQPC), + MLX5_SGMT_STR_ASSING(HW_SQPC), + MLX5_SGMT_STR_ASSING(HW_RQPC), + MLX5_SGMT_STR_ASSING(FULL_SRQC), + MLX5_SGMT_STR_ASSING(FULL_CQC), + MLX5_SGMT_STR_ASSING(FULL_EQC), + MLX5_SGMT_STR_ASSING(FULL_QPC), + MLX5_SGMT_STR_ASSING(SND_BUFF), + MLX5_SGMT_STR_ASSING(RCV_BUFF), + MLX5_SGMT_STR_ASSING(SRQ_BUFF), + MLX5_SGMT_STR_ASSING(CQ_BUFF), + MLX5_SGMT_STR_ASSING(EQ_BUFF), + MLX5_SGMT_STR_ASSING(SX_SLICE), + MLX5_SGMT_STR_ASSING(SX_SLICE_ALL), + MLX5_SGMT_STR_ASSING(RDB), + MLX5_SGMT_STR_ASSING(RX_SLICE_ALL), +}; + +struct mlx5_rsc_dump { + u32 pdn; + struct mlx5_core_mkey mkey; + u16 fw_segment_type[MLX5_SGMT_TYPE_NUM]; +}; + +struct mlx5_rsc_dump_cmd { + u64 mem_size; + u8 cmd[MLX5_ST_SZ_BYTES(resource_dump)]; +}; + +static int mlx5_rsc_dump_sgmt_get_by_name(char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(mlx5_rsc_sgmt_name); i++) + if (!strcmp(name, mlx5_rsc_sgmt_name[i])) + return i; + + return -EINVAL; +} + +static void mlx5_rsc_dump_read_menu_sgmt(struct mlx5_rsc_dump *rsc_dump, struct page *page) +{ + void *data = page_address(page); + enum mlx5_sgmt_type sgmt_idx; + int num_of_items; + char *sgmt_name; + void *member; + void *menu; + int i; + + menu = MLX5_ADDR_OF(menu_resource_dump_response, data, menu); + num_of_items = MLX5_GET(resource_dump_menu_segment, menu, num_of_records); + + for (i = 0; i < num_of_items; i++) { + member = MLX5_ADDR_OF(resource_dump_menu_segment, menu, record[i]); + sgmt_name = MLX5_ADDR_OF(resource_dump_menu_record, member, segment_name); + sgmt_idx = mlx5_rsc_dump_sgmt_get_by_name(sgmt_name); + if (sgmt_idx == -EINVAL) + continue; + rsc_dump->fw_segment_type[sgmt_idx] = MLX5_GET(resource_dump_menu_record, + member, segment_type); + } +} + +static int mlx5_rsc_dump_trigger(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd, + struct page *page) +{ + struct mlx5_rsc_dump *rsc_dump = dev->rsc_dump; + struct device *ddev = &dev->pdev->dev; + u32 out_seq_num; + u32 in_seq_num; + dma_addr_t dma; + int err; + + dma = dma_map_page(ddev, page, 0, cmd->mem_size, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ddev, dma))) + return -ENOMEM; + + in_seq_num = MLX5_GET(resource_dump, cmd->cmd, seq_num); + MLX5_SET(resource_dump, cmd->cmd, mkey, rsc_dump->mkey.key); + MLX5_SET64(resource_dump, cmd->cmd, address, dma); + + err = mlx5_core_access_reg(dev, cmd->cmd, sizeof(cmd->cmd), cmd->cmd, + sizeof(cmd->cmd), MLX5_REG_RESOURCE_DUMP, 0, 1); + if (err) { + mlx5_core_err(dev, "Resource dump: Failed to access err %d\n", err); + goto out; + } + out_seq_num = MLX5_GET(resource_dump, cmd->cmd, seq_num); + if (out_seq_num && (in_seq_num + 1 != out_seq_num)) + err = -EIO; +out: + dma_unmap_page(ddev, dma, cmd->mem_size, DMA_FROM_DEVICE); + return err; +} + +struct mlx5_rsc_dump_cmd *mlx5_rsc_dump_cmd_create(struct mlx5_core_dev *dev, + struct mlx5_rsc_key *key) +{ + struct mlx5_rsc_dump_cmd *cmd; + int sgmt_type; + + if (IS_ERR_OR_NULL(dev->rsc_dump)) + return ERR_PTR(-EOPNOTSUPP); + + sgmt_type = dev->rsc_dump->fw_segment_type[key->rsc]; + if (!sgmt_type && key->rsc != MLX5_SGMT_TYPE_MENU) + return ERR_PTR(-EOPNOTSUPP); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + mlx5_core_err(dev, "Resource dump: Failed to allocate command\n"); + return ERR_PTR(-ENOMEM); + } + MLX5_SET(resource_dump, cmd->cmd, segment_type, sgmt_type); + MLX5_SET(resource_dump, cmd->cmd, index1, key->index1); + MLX5_SET(resource_dump, cmd->cmd, index2, key->index2); + MLX5_SET(resource_dump, cmd->cmd, num_of_obj1, key->num_of_obj1); + MLX5_SET(resource_dump, cmd->cmd, num_of_obj2, key->num_of_obj2); + MLX5_SET(resource_dump, cmd->cmd, size, key->size); + cmd->mem_size = key->size; + return cmd; +} + +void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd) +{ + kfree(cmd); +} + +int mlx5_rsc_dump_next(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd, + struct page *page, int *size) +{ + bool more_dump; + int err; + + if (IS_ERR_OR_NULL(dev->rsc_dump)) + return -EOPNOTSUPP; + + err = mlx5_rsc_dump_trigger(dev, cmd, page); + if (err) { + mlx5_core_err(dev, "Resource dump: Failed to trigger dump, %d\n", err); + return err; + } + *size = MLX5_GET(resource_dump, cmd->cmd, size); + more_dump = MLX5_GET(resource_dump, cmd->cmd, more_dump); + + return more_dump; +} + +#define MLX5_RSC_DUMP_MENU_SEGMENT 0xffff +static int mlx5_rsc_dump_menu(struct mlx5_core_dev *dev) +{ + struct mlx5_rsc_dump_cmd *cmd = NULL; + struct mlx5_rsc_key key = {}; + struct page *page; + int size; + int err; + + page = alloc_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + key.rsc = MLX5_SGMT_TYPE_MENU; + key.size = PAGE_SIZE; + cmd = mlx5_rsc_dump_cmd_create(dev, &key); + if (IS_ERR(cmd)) { + err = PTR_ERR(cmd); + goto free_page; + } + MLX5_SET(resource_dump, cmd->cmd, segment_type, MLX5_RSC_DUMP_MENU_SEGMENT); + + do { + err = mlx5_rsc_dump_next(dev, cmd, page, &size); + if (err < 0) + goto destroy_cmd; + + mlx5_rsc_dump_read_menu_sgmt(dev->rsc_dump, page); + + } while (err > 0); + +destroy_cmd: + mlx5_rsc_dump_cmd_destroy(cmd); +free_page: + __free_page(page); + + return err; +} + +static int mlx5_rsc_dump_create_mkey(struct mlx5_core_dev *mdev, u32 pdn, + struct mlx5_core_mkey *mkey) +{ + int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); + void *mkc; + u32 *in; + int err; + + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) + return -ENOMEM; + + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + MLX5_SET(mkc, mkc, access_mode_1_0, MLX5_MKC_ACCESS_MODE_PA); + MLX5_SET(mkc, mkc, lw, 1); + MLX5_SET(mkc, mkc, lr, 1); + + MLX5_SET(mkc, mkc, pd, pdn); + MLX5_SET(mkc, mkc, length64, 1); + MLX5_SET(mkc, mkc, qpn, 0xffffff); + + err = mlx5_core_create_mkey(mdev, mkey, in, inlen); + + kvfree(in); + return err; +} + +struct mlx5_rsc_dump *mlx5_rsc_dump_create(struct mlx5_core_dev *dev) +{ + struct mlx5_rsc_dump *rsc_dump; + + if (!MLX5_CAP_DEBUG(dev, resource_dump)) { + mlx5_core_dbg(dev, "Resource dump: capability not present\n"); + return NULL; + } + rsc_dump = kzalloc(sizeof(*rsc_dump), GFP_KERNEL); + if (!rsc_dump) + return ERR_PTR(-ENOMEM); + + return rsc_dump; +} + +void mlx5_rsc_dump_destroy(struct mlx5_core_dev *dev) +{ + if (IS_ERR_OR_NULL(dev->rsc_dump)) + return; + kfree(dev->rsc_dump); +} + +int mlx5_rsc_dump_init(struct mlx5_core_dev *dev) +{ + struct mlx5_rsc_dump *rsc_dump = dev->rsc_dump; + int err; + + if (IS_ERR_OR_NULL(dev->rsc_dump)) + return 0; + + err = mlx5_core_alloc_pd(dev, &rsc_dump->pdn); + if (err) { + mlx5_core_warn(dev, "Resource dump: Failed to allocate PD %d\n", err); + return err; + } + err = mlx5_rsc_dump_create_mkey(dev, rsc_dump->pdn, &rsc_dump->mkey); + if (err) { + mlx5_core_err(dev, "Resource dump: Failed to create mkey, %d\n", err); + goto free_pd; + } + err = mlx5_rsc_dump_menu(dev); + if (err) { + mlx5_core_err(dev, "Resource dump: Failed to read menu, %d\n", err); + goto destroy_mkey; + } + return err; + +destroy_mkey: + mlx5_core_destroy_mkey(dev, &rsc_dump->mkey); +free_pd: + mlx5_core_dealloc_pd(dev, rsc_dump->pdn); + return err; +} + +void mlx5_rsc_dump_cleanup(struct mlx5_core_dev *dev) +{ + if (IS_ERR_OR_NULL(dev->rsc_dump)) + return; + + mlx5_core_destroy_mkey(dev, &dev->rsc_dump->mkey); + mlx5_core_dealloc_pd(dev, dev->rsc_dump->pdn); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.h b/drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.h new file mode 100644 index 000000000000..3b7573461a45 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/rsc_dump.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2019 Mellanox Technologies. */ + +#ifndef __MLX5_RSC_DUMP_H +#define __MLX5_RSC_DUMP__H + +#include +#include "mlx5_core.h" + +enum mlx5_sgmt_type { + MLX5_SGMT_TYPE_HW_CQPC, + MLX5_SGMT_TYPE_HW_SQPC, + MLX5_SGMT_TYPE_HW_RQPC, + MLX5_SGMT_TYPE_FULL_SRQC, + MLX5_SGMT_TYPE_FULL_CQC, + MLX5_SGMT_TYPE_FULL_EQC, + MLX5_SGMT_TYPE_FULL_QPC, + MLX5_SGMT_TYPE_SND_BUFF, + MLX5_SGMT_TYPE_RCV_BUFF, + MLX5_SGMT_TYPE_SRQ_BUFF, + MLX5_SGMT_TYPE_CQ_BUFF, + MLX5_SGMT_TYPE_EQ_BUFF, + MLX5_SGMT_TYPE_SX_SLICE, + MLX5_SGMT_TYPE_SX_SLICE_ALL, + MLX5_SGMT_TYPE_RDB, + MLX5_SGMT_TYPE_RX_SLICE_ALL, + MLX5_SGMT_TYPE_MENU, + MLX5_SGMT_TYPE_TERMINATE, + + MLX5_SGMT_TYPE_NUM, /* Keep last */ +}; + +struct mlx5_rsc_key { + enum mlx5_sgmt_type rsc; + int index1; + int index2; + int num_of_obj1; + int num_of_obj2; + int size; +}; + +#define MLX5_RSC_DUMP_ALL 0xFFFF +struct mlx5_rsc_dump_cmd; +struct mlx5_rsc_dump; + +struct mlx5_rsc_dump *mlx5_rsc_dump_create(struct mlx5_core_dev *dev); +void mlx5_rsc_dump_destroy(struct mlx5_core_dev *dev); + +int mlx5_rsc_dump_init(struct mlx5_core_dev *dev); +void mlx5_rsc_dump_cleanup(struct mlx5_core_dev *dev); + +struct mlx5_rsc_dump_cmd *mlx5_rsc_dump_cmd_create(struct mlx5_core_dev *dev, + struct mlx5_rsc_key *key); +void mlx5_rsc_dump_cmd_destroy(struct mlx5_rsc_dump_cmd *cmd); + +int mlx5_rsc_dump_next(struct mlx5_core_dev *dev, struct mlx5_rsc_dump_cmd *cmd, + struct page *page, int *size); +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index f554cfddcf4e..204a26bf0a5f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -70,6 +70,7 @@ #include "diag/fw_tracer.h" #include "ecpf.h" #include "lib/hv_vhca.h" +#include "diag/rsc_dump.h" MODULE_AUTHOR("Eli Cohen "); MODULE_DESCRIPTION("Mellanox 5th generation network adapters (ConnectX series) core driver"); @@ -880,6 +881,7 @@ static int mlx5_init_once(struct mlx5_core_dev *dev) dev->tracer = mlx5_fw_tracer_create(dev); dev->hv_vhca = mlx5_hv_vhca_create(dev); + dev->rsc_dump = mlx5_rsc_dump_create(dev); return 0; @@ -909,6 +911,7 @@ err_devcom: static void mlx5_cleanup_once(struct mlx5_core_dev *dev) { + mlx5_rsc_dump_destroy(dev); mlx5_hv_vhca_destroy(dev->hv_vhca); mlx5_fw_tracer_destroy(dev->tracer); mlx5_dm_cleanup(dev); @@ -1079,6 +1082,12 @@ static int mlx5_load(struct mlx5_core_dev *dev) mlx5_hv_vhca_init(dev->hv_vhca); + err = mlx5_rsc_dump_init(dev); + if (err) { + mlx5_core_err(dev, "Failed to init Resource dump\n"); + goto err_rsc_dump; + } + err = mlx5_fpga_device_start(dev); if (err) { mlx5_core_err(dev, "fpga device start failed %d\n", err); @@ -1134,6 +1143,8 @@ err_tls_start: err_ipsec_start: mlx5_fpga_device_stop(dev); err_fpga_start: + mlx5_rsc_dump_cleanup(dev); +err_rsc_dump: mlx5_hv_vhca_cleanup(dev->hv_vhca); mlx5_fw_tracer_cleanup(dev->tracer); err_fw_tracer: @@ -1155,6 +1166,7 @@ static void mlx5_unload(struct mlx5_core_dev *dev) mlx5_accel_ipsec_cleanup(dev); mlx5_accel_tls_cleanup(dev); mlx5_fpga_device_stop(dev); + mlx5_rsc_dump_cleanup(dev); mlx5_hv_vhca_cleanup(dev->hv_vhca); mlx5_fw_tracer_cleanup(dev->tracer); mlx5_eq_table_destroy(dev); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 277a51d3ec40..f99cbe249425 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -722,6 +722,7 @@ struct mlx5_core_dev { struct mlx5_clock clock; struct mlx5_ib_clock_info *clock_info; struct mlx5_fw_tracer *tracer; + struct mlx5_rsc_dump *rsc_dump; u32 vsc_addr; struct mlx5_hv_vhca *hv_vhca; }; -- cgit v1.2.3 From f623e5970501449b475a8dc007f0fe24743ab9d3 Mon Sep 17 00:00:00 2001 From: Aya Levin Date: Tue, 11 Feb 2020 14:32:52 -0800 Subject: ethtool: Add support for low latency RS FEC Add support for low latency Reed Solomon FEC as LLRS. The LL-FEC is defined by the 25G/50G ethernet consortium, in the document titled "Low Latency Reed Solomon Forward Error Correction" Signed-off-by: Aya Levin Reviewed-by: Eran Ben Elisha CC: Andrew Lunn Signed-off-by: Saeed Mahameed Reviewed-by: Andrew Lunn --- drivers/net/phy/phy-core.c | 2 +- include/uapi/linux/ethtool.h | 4 +++- net/ethtool/common.c | 1 + net/ethtool/linkmodes.c | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c index a4d2d59fceca..e083e7a76ada 100644 --- a/drivers/net/phy/phy-core.c +++ b/drivers/net/phy/phy-core.c @@ -8,7 +8,7 @@ const char *phy_speed_to_str(int speed) { - BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 74, + BUILD_BUG_ON_MSG(__ETHTOOL_LINK_MODE_MASK_NBITS != 75, "Enum ethtool_link_mode_bit_indices and phylib are out of sync. " "If a speed or mode has been added please update phy_speed_to_str " "and the PHY settings array.\n"); diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 4295ebfa2f91..d586ee5e10a1 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1330,6 +1330,7 @@ enum ethtool_fec_config_bits { ETHTOOL_FEC_OFF_BIT, ETHTOOL_FEC_RS_BIT, ETHTOOL_FEC_BASER_BIT, + ETHTOOL_FEC_LLRS_BIT, }; #define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT) @@ -1337,6 +1338,7 @@ enum ethtool_fec_config_bits { #define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT) #define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT) #define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT) +#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT) /* CMDs currently supported */ #define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings. @@ -1521,7 +1523,7 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71, ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72, ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73, - + ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74, /* must be last entry */ __ETHTOOL_LINK_MODE_MASK_NBITS }; diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 636ec6d5110e..7b6969af5ae7 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -168,6 +168,7 @@ const char link_mode_names[][ETH_GSTRING_LEN] = { __DEFINE_LINK_MODE_NAME(400000, LR8_ER8_FR8, Full), __DEFINE_LINK_MODE_NAME(400000, DR8, Full), __DEFINE_LINK_MODE_NAME(400000, CR8, Full), + __DEFINE_SPECIAL_MODE_NAME(FEC_LLRS, "LLRS"), }; static_assert(ARRAY_SIZE(link_mode_names) == __ETHTOOL_LINK_MODE_MASK_NBITS); diff --git a/net/ethtool/linkmodes.c b/net/ethtool/linkmodes.c index 96f20be64553..f049b97072fe 100644 --- a/net/ethtool/linkmodes.c +++ b/net/ethtool/linkmodes.c @@ -237,6 +237,7 @@ static const struct link_mode_info link_mode_params[] = { __DEFINE_LINK_MODE_PARAMS(400000, LR8_ER8_FR8, Full), __DEFINE_LINK_MODE_PARAMS(400000, DR8, Full), __DEFINE_LINK_MODE_PARAMS(400000, CR8, Full), + __DEFINE_SPECIAL_MODE_PARAMS(FEC_LLRS), }; static const struct nla_policy -- cgit v1.2.3 From df5c21002cf4bb9c755c6330d101487c5d530c10 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 19 Feb 2020 00:24:10 +0300 Subject: mtd: spi-nor: use spi-mem dirmap API Make use of the spi-mem direct mapping API to let advanced controllers optimize read/write operations when they support direct mapping. Based on the original patch by Boris Brezillon . Signed-off-by: Sergei Shtylyov Reviewed-by: Boris Brezillon Signed-off-by: Tudor Ambarus --- drivers/mtd/spi-nor/spi-nor.c | 94 ++++++++++++++++++++++++++++++++++++++----- include/linux/mtd/spi-nor.h | 6 +++ 2 files changed, 90 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 1ce9784d86e8..1224247b26cc 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -306,6 +306,7 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, SPI_MEM_OP_DUMMY(nor->read_dummy, 1), SPI_MEM_OP_DATA_IN(len, buf, 1)); bool usebouncebuf; + ssize_t nbytes; int error; /* get transfer protocols. */ @@ -319,14 +320,20 @@ static ssize_t spi_nor_spimem_read_data(struct spi_nor *nor, loff_t from, usebouncebuf = spi_nor_spimem_bounce(nor, &op); - error = spi_nor_spimem_exec_op(nor, &op); - if (error) - return error; + if (nor->dirmap.rdesc) { + nbytes = spi_mem_dirmap_read(nor->dirmap.rdesc, op.addr.val, + op.data.nbytes, op.data.buf.in); + } else { + error = spi_nor_spimem_exec_op(nor, &op); + if (error) + return error; + nbytes = op.data.nbytes; + } - if (usebouncebuf) - memcpy(buf, op.data.buf.in, op.data.nbytes); + if (usebouncebuf && nbytes > 0) + memcpy(buf, op.data.buf.in, nbytes); - return op.data.nbytes; + return nbytes; } /** @@ -365,6 +372,7 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, SPI_MEM_OP_ADDR(nor->addr_width, to, 1), SPI_MEM_OP_NO_DUMMY, SPI_MEM_OP_DATA_OUT(len, buf, 1)); + ssize_t nbytes; int error; op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); @@ -377,11 +385,17 @@ static ssize_t spi_nor_spimem_write_data(struct spi_nor *nor, loff_t to, if (spi_nor_spimem_bounce(nor, &op)) memcpy(nor->bouncebuf, buf, op.data.nbytes); - error = spi_nor_spimem_exec_op(nor, &op); - if (error) - return error; + if (nor->dirmap.wdesc) { + nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val, + op.data.nbytes, op.data.buf.out); + } else { + error = spi_nor_spimem_exec_op(nor, &op); + if (error) + return error; + nbytes = op.data.nbytes; + } - return op.data.nbytes; + return nbytes; } /** @@ -5265,6 +5279,58 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, } EXPORT_SYMBOL_GPL(spi_nor_scan); +static int spi_nor_create_read_dirmap(struct spi_nor *nor) +{ + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + SPI_MEM_OP_DUMMY(nor->read_dummy, 1), + SPI_MEM_OP_DATA_IN(0, NULL, 1)), + .offset = 0, + .length = nor->mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto); + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto); + op->dummy.buswidth = op->addr.buswidth; + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto); + + /* convert the dummy cycles to the number of bytes */ + op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8; + + nor->dirmap.rdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, + &info); + return PTR_ERR_OR_ZERO(nor->dirmap.rdesc); +} + +static int spi_nor_create_write_dirmap(struct spi_nor *nor) +{ + struct spi_mem_dirmap_info info = { + .op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1), + SPI_MEM_OP_ADDR(nor->addr_width, 0, 1), + SPI_MEM_OP_NO_DUMMY, + SPI_MEM_OP_DATA_OUT(0, NULL, 1)), + .offset = 0, + .length = nor->mtd.size, + }; + struct spi_mem_op *op = &info.op_tmpl; + + /* get transfer protocols. */ + op->cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto); + op->addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto); + op->dummy.buswidth = op->addr.buswidth; + op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto); + + if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second) + op->addr.nbytes = 0; + + nor->dirmap.wdesc = devm_spi_mem_dirmap_create(nor->dev, nor->spimem, + &info); + return PTR_ERR_OR_ZERO(nor->dirmap.wdesc); +} + static int spi_nor_probe(struct spi_mem *spimem) { struct spi_device *spi = spimem->spi; @@ -5326,6 +5392,14 @@ static int spi_nor_probe(struct spi_mem *spimem) return -ENOMEM; } + ret = spi_nor_create_read_dirmap(nor); + if (ret) + return ret; + + ret = spi_nor_create_write_dirmap(nor); + if (ret) + return ret; + return mtd_device_register(&nor->mtd, data ? data->parts : NULL, data ? data->nr_parts : 0); } diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index 61be6ed33097..de90724f62f1 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -580,6 +580,7 @@ struct flash_info; * The structure includes legacy flash parameters and * settings that can be overwritten by the spi_nor_fixups * hooks, or dynamically when parsing the SFDP tables. + * @dirmap: pointers to struct spi_mem_dirmap_desc for reads/writes. * @priv: the private data */ struct spi_nor { @@ -606,6 +607,11 @@ struct spi_nor { struct spi_nor_flash_parameter params; + struct { + struct spi_mem_dirmap_desc *rdesc; + struct spi_mem_dirmap_desc *wdesc; + } dirmap; + void *priv; }; -- cgit v1.2.3 From 9255782f70614c89b1a15ec6997c4b72ce9e630a Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Wed, 11 Dec 2019 21:39:06 +0530 Subject: sysfs: Wrap __compat_only_sysfs_link_entry_to_kobj function to change the symlink name The __compat_only_sysfs_link_entry_to_kobj function creates a symlink to a kobject but doesn't provide an option to change the symlink file name. This patch adds a wrapper function compat_only_sysfs_link_entry_to_kobj that extends the __compat_only_sysfs_link_entry_to_kobj functionality which allows function caller to customize the symlink name. Signed-off-by: Sourabh Jain [mpe: Fix compile error when CONFIG_SYSFS=n] Signed-off-by: Michael Ellerman Link: https://lore.kernel.org/r/20191211160910.21656-3-sourabhjain@linux.ibm.com --- fs/sysfs/group.c | 28 +++++++++++++++++++++++++--- include/linux/sysfs.h | 12 ++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index c4ab045926b7..1e2a096057bc 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -424,6 +424,25 @@ EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, struct kobject *target_kobj, const char *target_name) +{ + return compat_only_sysfs_link_entry_to_kobj(kobj, target_kobj, + target_name, NULL); +} +EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj); + +/** + * compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing + * to a group or an attribute + * @kobj: The kobject containing the group. + * @target_kobj: The target kobject. + * @target_name: The name of the target group or attribute. + * @symlink_name: The name of the symlink file (target_name will be + * considered if symlink_name is NULL). + */ +int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, + struct kobject *target_kobj, + const char *target_name, + const char *symlink_name) { struct kernfs_node *target; struct kernfs_node *entry; @@ -448,12 +467,15 @@ int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, return -ENOENT; } - link = kernfs_create_link(kobj->sd, target_name, entry); + if (!symlink_name) + symlink_name = target_name; + + link = kernfs_create_link(kobj->sd, symlink_name, entry); if (PTR_ERR(link) == -EEXIST) - sysfs_warn_dup(kobj->sd, target_name); + sysfs_warn_dup(kobj->sd, symlink_name); kernfs_put(entry); kernfs_put(target); return PTR_ERR_OR_ZERO(link); } -EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj); +EXPORT_SYMBOL_GPL(compat_only_sysfs_link_entry_to_kobj); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fa7ee503fb76..7462315a643b 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -300,6 +300,10 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, struct kobject *target_kobj, const char *target_name); +int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, + struct kobject *target_kobj, + const char *target_name, + const char *symlink_name); void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); @@ -508,6 +512,14 @@ static inline int __compat_only_sysfs_link_entry_to_kobj( return 0; } +static inline int compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, + struct kobject *target_kobj, + const char *target_name, + const char *symlink_name) +{ + return 0; +} + static inline void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) { -- cgit v1.2.3 From 10892847de816edf4f8f2151174dc557c4a3f63f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 17 Feb 2020 16:59:54 -0800 Subject: usb-storage: Use const to reduce object data size Make structs const to reduce data size ~20KB. Change function arguments and prototypes as necessary to compile. $ size (x86-64 defconfig pre) text data bss dec hex filename 12281 10948 480 23709 5c9d ./drivers/usb/storage/usb.o 111 10528 8 10647 2997 ./drivers/usb/storage/usual-tables.o $ size (x86-64 defconfig post) text data bss dec hex filename 22809 420 480 23709 5c9d drivers/usb/storage/usb.o 10551 0 0 10551 2937 drivers/usb/storage/usual-tables.o Signed-off-by: Joe Perches Acked-by: Alan Stern Link: https://lore.kernel.org/r/cf13bd2d790ae3afbf5da55ea7bed12e00c5119d.camel@perches.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/storage/usb.c | 10 +++++----- drivers/usb/storage/usb.h | 5 +++-- drivers/usb/storage/usual-tables.c | 6 +++--- include/linux/usb_usual.h | 2 +- 4 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 9a79cd9762f3..94a64729dc27 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -121,12 +121,12 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); .initFunction = init_function, \ } -static struct us_unusual_dev us_unusual_dev_list[] = { +static const struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" { } /* Terminating entry */ }; -static struct us_unusual_dev for_dynamic_ids = +static const struct us_unusual_dev for_dynamic_ids = USUAL_DEV(USB_SC_SCSI, USB_PR_BULK); #undef UNUSUAL_DEV @@ -583,7 +583,7 @@ EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks); /* Get the unusual_devs entries and the string descriptors */ static int get_device_info(struct us_data *us, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev) + const struct us_unusual_dev *unusual_dev) { struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = @@ -933,7 +933,7 @@ static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev, + const struct us_unusual_dev *unusual_dev, struct scsi_host_template *sht) { struct Scsi_Host *host; @@ -1092,7 +1092,7 @@ static struct scsi_host_template usb_stor_host_template; static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct us_unusual_dev *unusual_dev; + const struct us_unusual_dev *unusual_dev; struct us_data *us; int result; int size; diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 85052cd66839..5850d624cac7 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -93,7 +93,8 @@ struct us_data { struct mutex dev_mutex; /* protect pusb_dev */ struct usb_device *pusb_dev; /* this usb_device */ struct usb_interface *pusb_intf; /* this interface */ - struct us_unusual_dev *unusual_dev; /* device-filter entry */ + const struct us_unusual_dev *unusual_dev; + /* device-filter entry */ unsigned long fflags; /* fixed flags from filter */ unsigned long dflags; /* dynamic atomic bitflags */ unsigned int send_bulk_pipe; /* cached pipe values */ @@ -185,7 +186,7 @@ extern int usb_stor_post_reset(struct usb_interface *iface); extern int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev, + const struct us_unusual_dev *unusual_dev, struct scsi_host_template *sht); extern int usb_stor_probe2(struct us_data *us); extern void usb_stor_disconnect(struct usb_interface *intf); diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index cfd12e523678..529512827d8f 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -40,7 +40,7 @@ .driver_info = (flags) \ } -struct usb_device_id usb_storage_usb_ids[] = { +const struct usb_device_id usb_storage_usb_ids[] = { # include "unusual_devs.h" { } /* Terminating entry */ }; @@ -68,7 +68,7 @@ struct ignore_entry { .bcdmax = bcdDeviceMax, \ } -static struct ignore_entry ignore_ids[] = { +static const struct ignore_entry ignore_ids[] = { # include "unusual_alauda.h" # include "unusual_cypress.h" # include "unusual_datafab.h" @@ -92,7 +92,7 @@ int usb_usual_ignore_device(struct usb_interface *intf) { struct usb_device *udev; unsigned vid, pid, bcd; - struct ignore_entry *p; + const struct ignore_entry *p; udev = interface_to_usbdev(intf); vid = le16_to_cpu(udev->descriptor.idVendor); diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h index 000a5954b2e8..4a19ac3f24d0 100644 --- a/include/linux/usb_usual.h +++ b/include/linux/usb_usual.h @@ -92,6 +92,6 @@ enum { US_DO_ALL_FLAGS }; #include extern int usb_usual_ignore_device(struct usb_interface *intf); -extern struct usb_device_id usb_storage_usb_ids[]; +extern const struct usb_device_id usb_storage_usb_ids[]; #endif /* __LINUX_USB_USUAL_H */ -- cgit v1.2.3 From dda520c4d4623701dd70cf7b40d29a4eed232d0f Mon Sep 17 00:00:00 2001 From: Raed Salem Date: Wed, 19 Feb 2020 14:49:57 +0200 Subject: ESP: Export esp_output_fill_trailer function The esp fill trailer method is identical for both IPv6 and IPv4. Share the implementation for esp6 and esp to avoid code duplication in addition it could be also used at various drivers code. Signed-off-by: Raed Salem Reviewed-by: Boris Pismenny Reviewed-by: Saeed Mahameed Signed-off-by: Steffen Klassert --- include/net/esp.h | 16 ++++++++++++++++ net/ipv4/esp4.c | 16 ---------------- net/ipv6/esp6.c | 16 ---------------- 3 files changed, 16 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/include/net/esp.h b/include/net/esp.h index 117652eb6ea3..9c5637d41d95 100644 --- a/include/net/esp.h +++ b/include/net/esp.h @@ -11,6 +11,22 @@ static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb) return (struct ip_esp_hdr *)skb_transport_header(skb); } +static inline void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto) +{ + /* Fill padding... */ + if (tfclen) { + memset(tail, 0, tfclen); + tail += tfclen; + } + do { + int i; + for (i = 0; i < plen - 2; i++) + tail[i] = i + 1; + } while (0); + tail[plen - 2] = plen - 2; + tail[plen - 1] = proto; +} + struct esp_info { struct ip_esp_hdr *esph; __be64 seqno; diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 103c7d599a3c..8b07f3a4f2db 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -341,22 +341,6 @@ static void esp_output_done_esn(struct crypto_async_request *base, int err) esp_output_done(base, err); } -static void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto) -{ - /* Fill padding... */ - if (tfclen) { - memset(tail, 0, tfclen); - tail += tfclen; - } - do { - int i; - for (i = 0; i < plen - 2; i++) - tail[i] = i + 1; - } while (0); - tail[plen - 2] = plen - 2; - tail[plen - 1] = proto; -} - static struct ip_esp_hdr *esp_output_udp_encap(struct sk_buff *skb, int encap_type, struct esp_info *esp, diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index a3b403ba8f8f..11143d039f16 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -207,22 +207,6 @@ static void esp_output_done_esn(struct crypto_async_request *base, int err) esp_output_done(base, err); } -static void esp_output_fill_trailer(u8 *tail, int tfclen, int plen, __u8 proto) -{ - /* Fill padding... */ - if (tfclen) { - memset(tail, 0, tfclen); - tail += tfclen; - } - do { - int i; - for (i = 0; i < plen - 2; i++) - tail[i] = i + 1; - } while (0); - tail[plen - 2] = plen - 2; - tail[plen - 1] = proto; -} - int esp6_output_head(struct xfrm_state *x, struct sk_buff *skb, struct esp_info *esp) { u8 *tail; -- cgit v1.2.3 From a4393861a351f66fef1102e775743c86a276afce Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 17 Feb 2020 12:15:28 +0000 Subject: bpf, sk_msg: Let ULP restore sk_proto and write_space callback We don't need a fallback for when the socket is not using ULP. tcp_update_ulp handles this case exactly the same as we do in sk_psock_restore_proto. Get rid of the duplicated code. Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200217121530.754315-2-jakub@cloudflare.com --- include/linux/skmsg.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 14d61bba0b79..8605947d6c08 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -361,16 +361,7 @@ static inline void sk_psock_restore_proto(struct sock *sk, sk->sk_prot->unhash = psock->saved_unhash; if (psock->sk_proto) { - struct inet_connection_sock *icsk = inet_csk(sk); - bool has_ulp = !!icsk->icsk_ulp_data; - - if (has_ulp) { - tcp_update_ulp(sk, psock->sk_proto, - psock->saved_write_space); - } else { - sk->sk_prot = psock->sk_proto; - sk->sk_write_space = psock->saved_write_space; - } + tcp_update_ulp(sk, psock->sk_proto, psock->saved_write_space); psock->sk_proto = NULL; } else { sk->sk_write_space = psock->saved_write_space; -- cgit v1.2.3 From a178b4585865a4c756c41bc5376f63416b7d9271 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 17 Feb 2020 12:15:29 +0000 Subject: bpf, sk_msg: Don't clear saved sock proto on restore There is no need to clear psock->sk_proto when restoring socket protocol callbacks in sk->sk_prot. The psock is about to get detached from the sock and eventually destroyed. At worst we will restore the protocol callbacks and the write callback twice. This makes reasoning about psock state easier. Once psock is initialized, we can count on psock->sk_proto always being set. Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200217121530.754315-3-jakub@cloudflare.com --- include/linux/skmsg.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 8605947d6c08..d90ef61712a1 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -359,13 +359,7 @@ static inline void sk_psock_restore_proto(struct sock *sk, struct sk_psock *psock) { sk->sk_prot->unhash = psock->saved_unhash; - - if (psock->sk_proto) { - tcp_update_ulp(sk, psock->sk_proto, psock->saved_write_space); - psock->sk_proto = NULL; - } else { - sk->sk_write_space = psock->saved_write_space; - } + tcp_update_ulp(sk, psock->sk_proto, psock->saved_write_space); } static inline void sk_psock_set_state(struct sk_psock *psock, -- cgit v1.2.3 From 42be7c41a5edc990e329b991b4ad6ec172e72e18 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 19 Feb 2020 09:49:27 +0100 Subject: dt-bindings: clk: g12a-clkc: add SPICC SCLK Source clock IDs Add clock ids used by the SPICC Controllers of the G12A and compatible SoCs Signed-off-by: Neil Armstrong Signed-off-by: Jerome Brunet --- include/dt-bindings/clock/g12a-clkc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/clock/g12a-clkc.h b/include/dt-bindings/clock/g12a-clkc.h index 0837c1a7ae49..b0d65d73db96 100644 --- a/include/dt-bindings/clock/g12a-clkc.h +++ b/include/dt-bindings/clock/g12a-clkc.h @@ -143,5 +143,7 @@ #define CLKID_CPU1_CLK 253 #define CLKID_CPU2_CLK 254 #define CLKID_CPU3_CLK 255 +#define CLKID_SPICC0_SCLK 258 +#define CLKID_SPICC1_SCLK 261 #endif /* __G12A_CLKC_H */ -- cgit v1.2.3 From ad5906bd6e9aa3e156dcac8fc070b153648e8b68 Mon Sep 17 00:00:00 2001 From: Phong LE Date: Wed, 19 Feb 2020 15:09:06 +0100 Subject: regmap: wrong descriptions in regmap_range_cfg Swap selector_mask and selector_shift descriptions Signed-off-by: Phong LE Link: https://lore.kernel.org/r/20200219140906.29180-1-ple@baylibre.com Signed-off-by: Mark Brown --- include/linux/regmap.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/regmap.h b/include/linux/regmap.h index f0a092a1a96d..40b07168fd8e 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -461,8 +461,8 @@ struct regmap_config { * @range_max: Address of the highest register in virtual range. * * @selector_reg: Register with selector field. - * @selector_mask: Bit shift for selector value. - * @selector_shift: Bit mask for selector value. + * @selector_mask: Bit mask for selector value. + * @selector_shift: Bit shift for selector value. * * @window_start: Address of first (lowest) register in data window. * @window_len: Number of registers in data window. -- cgit v1.2.3 From 76eeb12b71a4aa5654694765c8c1bde9a1ce8bd8 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 18 Feb 2020 20:56:41 +0100 Subject: net: core: add helper tcp_v6_gso_csum_prep Several network drivers for chips that support TSO6 share the same code for preparing the TCP header, so let's factor it out to a helper. A difference is that some drivers reset the payload_len whilst others don't do this. This value is overwritten by TSO anyway, therefore the new helper resets it in general. Signed-off-by: Heiner Kallweit Reviewed-by: Alexander Duyck Signed-off-by: David S. Miller --- include/net/ip6_checksum.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h index 7bec95df4f80..27ec612cd4a4 100644 --- a/include/net/ip6_checksum.h +++ b/include/net/ip6_checksum.h @@ -76,6 +76,15 @@ static inline void __tcp_v6_send_check(struct sk_buff *skb, } } +static inline void tcp_v6_gso_csum_prep(struct sk_buff *skb) +{ + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + struct tcphdr *th = tcp_hdr(skb); + + ipv6h->payload_len = 0; + th->check = ~tcp_v6_check(0, &ipv6h->saddr, &ipv6h->daddr, 0); +} + #if IS_ENABLED(CONFIG_IPV6) static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) { -- cgit v1.2.3 From 1d0827b75ee7df497f611a2ac412a88135fb0ef5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 30 Jan 2020 12:06:01 -0800 Subject: mm/memremap_pages: Kill unused __devm_memremap_pages() Kill this definition that was introduced in commit 41e94a851304 ("add devm_memremap_pages") add never used. Cc: Christoph Hellwig Reviewed-by: Aneesh Kumar K.V Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/158041476158.3889308.4221100673554151124.stgit@dwillia2-desk3.amr.corp.intel.com Signed-off-by: Dan Williams --- include/linux/io.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/io.h b/include/linux/io.h index b1c44bb4b2d7..8394c56babc2 100644 --- a/include/linux/io.h +++ b/include/linux/io.h @@ -77,8 +77,6 @@ void *devm_memremap(struct device *dev, resource_size_t offset, size_t size, unsigned long flags); void devm_memunmap(struct device *dev, void *addr); -void *__devm_memremap_pages(struct device *dev, struct resource *res); - #ifdef CONFIG_PCI /* * The PCI specifications (Rev 3.0, 3.2.5 "Transaction Ordering and -- cgit v1.2.3 From 1c22d3cda8afa3fffa3875cbfa5c82e818a3f780 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 19 Feb 2020 21:31:55 -0800 Subject: Bluetooth: RFCOMM: Use MTU auto tune logic This reuse the L2CAP MTU auto logic to select the MTU used for RFCOMM channels, this should increase the maximum from 1013 to 1021 when 3-DH5 is supported. Since it does not set an L2CAP MTU we no longer need a debugfs so that is removed. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- include/net/bluetooth/rfcomm.h | 1 - net/bluetooth/rfcomm/core.c | 10 ++++------ 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index da4acefe39c8..8d65d2a0b9b4 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -34,7 +34,6 @@ #define RFCOMM_DEFAULT_MTU 127 #define RFCOMM_DEFAULT_CREDITS 7 -#define RFCOMM_MAX_L2CAP_MTU 1013 #define RFCOMM_MAX_CREDITS 40 #define RFCOMM_SKB_HEAD_RESERVE 8 diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index dcecce087b24..2e20af317cea 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -40,7 +40,6 @@ static bool disable_cfc; static bool l2cap_ertm; static int channel_mtu = -1; -static unsigned int l2cap_mtu = RFCOMM_MAX_L2CAP_MTU; static struct task_struct *rfcomm_thread; @@ -749,7 +748,8 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); - l2cap_pi(sk)->chan->imtu = l2cap_mtu; + /* Set MTU to 0 so L2CAP can auto select the MTU */ + l2cap_pi(sk)->chan->imtu = 0; l2cap_pi(sk)->chan->sec_level = sec_level; if (l2cap_ertm) l2cap_pi(sk)->chan->mode = L2CAP_MODE_ERTM; @@ -2036,7 +2036,8 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Set L2CAP options */ sk = sock->sk; lock_sock(sk); - l2cap_pi(sk)->chan->imtu = l2cap_mtu; + /* Set MTU to 0 so L2CAP can auto select the MTU */ + l2cap_pi(sk)->chan->imtu = 0; release_sock(sk); /* Start listening on the socket */ @@ -2234,9 +2235,6 @@ MODULE_PARM_DESC(disable_cfc, "Disable credit based flow control"); module_param(channel_mtu, int, 0644); MODULE_PARM_DESC(channel_mtu, "Default MTU for the RFCOMM channel"); -module_param(l2cap_mtu, uint, 0644); -MODULE_PARM_DESC(l2cap_mtu, "Default MTU for the L2CAP connection"); - module_param(l2cap_ertm, bool, 0644); MODULE_PARM_DESC(l2cap_ertm, "Use L2CAP ERTM mode for connection"); -- cgit v1.2.3 From 779820c2e1e9251ddfdce5dd43b0bba30cd22271 Mon Sep 17 00:00:00 2001 From: Selvin Xavier Date: Wed, 19 Feb 2020 02:19:53 -0800 Subject: RDMA/core: Add helper function to retrieve driver gid context from gid attr Adding a helper function to retrieve the driver gid context from the gid attr. Link: https://lore.kernel.org/r/1582107594-5180-2-git-send-email-selvin.xavier@broadcom.com Suggested-by: Jason Gunthorpe Signed-off-by: Selvin Xavier Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cache.c | 17 +++++++++++++++++ include/rdma/ib_cache.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 17bfedd24cc3..9bbdace3c809 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -972,6 +972,23 @@ done: } EXPORT_SYMBOL(rdma_query_gid); +/** + * rdma_read_gid_hw_context - Read the HW GID context from GID attribute + * @attr: Potinter to the GID attribute + * + * rdma_read_gid_hw_context() reads the drivers GID HW context corresponding + * to the SGID attr. Callers are required to already be holding the reference + * to an existing GID entry. + * + * Returns the HW GID context + * + */ +void *rdma_read_gid_hw_context(const struct ib_gid_attr *attr) +{ + return container_of(attr, struct ib_gid_table_entry, attr)->context; +} +EXPORT_SYMBOL(rdma_read_gid_hw_context); + /** * rdma_find_gid - Returns SGID attributes if the matching GID is found. * @device: The device to query. diff --git a/include/rdma/ib_cache.h b/include/rdma/ib_cache.h index 870b5e6c06db..e06d13388ae7 100644 --- a/include/rdma/ib_cache.h +++ b/include/rdma/ib_cache.h @@ -39,6 +39,7 @@ int rdma_query_gid(struct ib_device *device, u8 port_num, int index, union ib_gid *gid); +void *rdma_read_gid_hw_context(const struct ib_gid_attr *attr); const struct ib_gid_attr *rdma_find_gid(struct ib_device *device, const union ib_gid *gid, enum ib_gid_type gid_type, -- cgit v1.2.3 From fff7b64355eac6e29b50229ad1512315bc04b44e Mon Sep 17 00:00:00 2001 From: Daniel Xu Date: Mon, 17 Feb 2020 19:04:31 -0800 Subject: bpf: Add bpf_read_branch_records() helper Branch records are a CPU feature that can be configured to record certain branches that are taken during code execution. This data is particularly interesting for profile guided optimizations. perf has had branch record support for a while but the data collection can be a bit coarse grained. We (Facebook) have seen in experiments that associating metadata with branch records can improve results (after postprocessing). We generally use bpf_probe_read_*() to get metadata out of userspace. That's why bpf support for branch records is useful. Aside from this particular use case, having branch data available to bpf progs can be useful to get stack traces out of userspace applications that omit frame pointers. Signed-off-by: Daniel Xu Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Link: https://lore.kernel.org/bpf/20200218030432.4600-2-dxu@dxuuu.xyz --- include/uapi/linux/bpf.h | 25 ++++++++++++++++++++++++- kernel/trace/bpf_trace.c | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index f1d74a2bd234..a7e59756853f 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2892,6 +2892,25 @@ union bpf_attr { * Obtain the 64bit jiffies * Return * The 64 bit jiffies + * + * int bpf_read_branch_records(struct bpf_perf_event_data *ctx, void *buf, u32 size, u64 flags) + * Description + * For an eBPF program attached to a perf event, retrieve the + * branch records (struct perf_branch_entry) associated to *ctx* + * and store it in the buffer pointed by *buf* up to size + * *size* bytes. + * Return + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * The *flags* can be set to **BPF_F_GET_BRANCH_RECORDS_SIZE** to + * instead return the number of bytes required to store all the + * branch entries. If this flag is set, *buf* may be NULL. + * + * **-EINVAL** if arguments invalid or **size** not a multiple + * of sizeof(struct perf_branch_entry). + * + * **-ENOENT** if architecture does not support branch records. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3012,7 +3031,8 @@ union bpf_attr { FN(probe_read_kernel_str), \ FN(tcp_send_ack), \ FN(send_signal_thread), \ - FN(jiffies64), + FN(jiffies64), \ + FN(read_branch_records), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -3091,6 +3111,9 @@ enum bpf_func_id { /* BPF_FUNC_sk_storage_get flags */ #define BPF_SK_STORAGE_GET_F_CREATE (1ULL << 0) +/* BPF_FUNC_read_branch_records flags. */ +#define BPF_F_GET_BRANCH_RECORDS_SIZE (1ULL << 0) + /* Mode for BPF_FUNC_skb_adjust_room helper. */ enum bpf_adj_room_mode { BPF_ADJ_ROOM_NET, diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 4ddd5ac46094..b8661bd0d028 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1028,6 +1028,45 @@ static const struct bpf_func_proto bpf_perf_prog_read_value_proto = { .arg3_type = ARG_CONST_SIZE, }; +BPF_CALL_4(bpf_read_branch_records, struct bpf_perf_event_data_kern *, ctx, + void *, buf, u32, size, u64, flags) +{ +#ifndef CONFIG_X86 + return -ENOENT; +#else + static const u32 br_entry_size = sizeof(struct perf_branch_entry); + struct perf_branch_stack *br_stack = ctx->data->br_stack; + u32 to_copy; + + if (unlikely(flags & ~BPF_F_GET_BRANCH_RECORDS_SIZE)) + return -EINVAL; + + if (unlikely(!br_stack)) + return -EINVAL; + + if (flags & BPF_F_GET_BRANCH_RECORDS_SIZE) + return br_stack->nr * br_entry_size; + + if (!buf || (size % br_entry_size != 0)) + return -EINVAL; + + to_copy = min_t(u32, br_stack->nr * br_entry_size, size); + memcpy(buf, br_stack->entries, to_copy); + + return to_copy; +#endif +} + +static const struct bpf_func_proto bpf_read_branch_records_proto = { + .func = bpf_read_branch_records, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_MEM_OR_NULL, + .arg3_type = ARG_CONST_SIZE_OR_ZERO, + .arg4_type = ARG_ANYTHING, +}; + static const struct bpf_func_proto * pe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) { @@ -1040,6 +1079,8 @@ pe_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_get_stack_proto_tp; case BPF_FUNC_perf_prog_read_value: return &bpf_perf_prog_read_value_proto; + case BPF_FUNC_read_branch_records: + return &bpf_read_branch_records_proto; default: return tracing_func_proto(func_id, prog); } -- cgit v1.2.3 From 9d98809711ae0ebcfb8115a0bc54604c59908710 Mon Sep 17 00:00:00 2001 From: Youri Querry Date: Thu, 12 Dec 2019 17:01:13 +0000 Subject: soc: fsl: dpio: Adding QMAN multiple enqueue interface Update of QMAN the interface to enqueue frame. We now support multiple enqueue (qbman_swp_enqueue_multiple) and multiple enqueue with a table of descriptor (qbman_swp_enqueue_multiple_desc). Signed-off-by: Youri Querry Acked-by: Roy Pledge Signed-off-by: Li Yang --- drivers/soc/fsl/dpio/dpio-service.c | 69 ++++++++++++++++++++++++++++-- drivers/soc/fsl/dpio/qbman-portal.c | 83 +++++++++++++++++++++++++++++++------ drivers/soc/fsl/dpio/qbman-portal.h | 24 +++++++++++ include/soc/fsl/dpaa2-io.h | 6 ++- 4 files changed, 165 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/soc/fsl/dpio/dpio-service.c b/drivers/soc/fsl/dpio/dpio-service.c index 518a8e081b49..cd4f6410e8c2 100644 --- a/drivers/soc/fsl/dpio/dpio-service.c +++ b/drivers/soc/fsl/dpio/dpio-service.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright 2016 NXP + * Copyright 2016-2019 NXP * */ #include @@ -432,6 +432,69 @@ int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, } EXPORT_SYMBOL(dpaa2_io_service_enqueue_fq); +/** + * dpaa2_io_service_enqueue_multiple_fq() - Enqueue multiple frames + * to a frame queue using one fqid. + * @d: the given DPIO service. + * @fqid: the given frame queue id. + * @fd: the frame descriptor which is enqueued. + * @nb: number of frames to be enqueud + * + * Return 0 for successful enqueue, -EBUSY if the enqueue ring is not ready, + * or -ENODEV if there is no dpio service. + */ +int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d, + u32 fqid, + const struct dpaa2_fd *fd, + int nb) +{ + struct qbman_eq_desc ed; + + d = service_select(d); + if (!d) + return -ENODEV; + + qbman_eq_desc_clear(&ed); + qbman_eq_desc_set_no_orp(&ed, 0); + qbman_eq_desc_set_fq(&ed, fqid); + + return qbman_swp_enqueue_multiple(d->swp, &ed, fd, 0, nb); +} +EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_fq); + +/** + * dpaa2_io_service_enqueue_multiple_desc_fq() - Enqueue multiple frames + * to different frame queue using a list of fqids. + * @d: the given DPIO service. + * @fqid: the given list of frame queue ids. + * @fd: the frame descriptor which is enqueued. + * @nb: number of frames to be enqueud + * + * Return 0 for successful enqueue, -EBUSY if the enqueue ring is not ready, + * or -ENODEV if there is no dpio service. + */ +int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d, + u32 *fqid, + const struct dpaa2_fd *fd, + int nb) +{ + int i; + struct qbman_eq_desc ed[32]; + + d = service_select(d); + if (!d) + return -ENODEV; + + for (i = 0; i < nb; i++) { + qbman_eq_desc_clear(&ed[i]); + qbman_eq_desc_set_no_orp(&ed[i], 0); + qbman_eq_desc_set_fq(&ed[i], fqid[i]); + } + + return qbman_swp_enqueue_multiple_desc(d->swp, &ed[0], fd, nb); +} +EXPORT_SYMBOL(dpaa2_io_service_enqueue_multiple_desc_fq); + /** * dpaa2_io_service_enqueue_qd() - Enqueue a frame to a QD. * @d: the given DPIO service. @@ -526,7 +589,7 @@ EXPORT_SYMBOL_GPL(dpaa2_io_service_acquire); /** * dpaa2_io_store_create() - Create the dma memory storage for dequeue result. - * @max_frames: the maximum number of dequeued result for frames, must be <= 16. + * @max_frames: the maximum number of dequeued result for frames, must be <= 32. * @dev: the device to allow mapping/unmapping the DMAable region. * * The size of the storage is "max_frames*sizeof(struct dpaa2_dq)". @@ -541,7 +604,7 @@ struct dpaa2_io_store *dpaa2_io_store_create(unsigned int max_frames, struct dpaa2_io_store *ret; size_t size; - if (!max_frames || (max_frames > 16)) + if (!max_frames || (max_frames > 32)) return NULL; ret = kmalloc(sizeof(*ret), GFP_KERNEL); diff --git a/drivers/soc/fsl/dpio/qbman-portal.c b/drivers/soc/fsl/dpio/qbman-portal.c index c66f5b73777c..5a37ac8a171c 100644 --- a/drivers/soc/fsl/dpio/qbman-portal.c +++ b/drivers/soc/fsl/dpio/qbman-portal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. - * Copyright 2016 NXP + * Copyright 2016-2019 NXP * */ @@ -12,13 +12,6 @@ #include "qbman-portal.h" -#define QMAN_REV_4000 0x04000000 -#define QMAN_REV_4100 0x04010000 -#define QMAN_REV_4101 0x04010001 -#define QMAN_REV_5000 0x05000000 - -#define QMAN_REV_MASK 0xffff0000 - /* All QBMan command and result structures use this "valid bit" encoding */ #define QB_VALID_BIT ((u32)0x80) @@ -156,7 +149,7 @@ static inline u32 qbman_set_swp_cfg(u8 max_fill, u8 wn, u8 est, u8 rpm, u8 dcm, */ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) { - struct qbman_swp *p = kmalloc(sizeof(*p), GFP_KERNEL); + struct qbman_swp *p = kzalloc(sizeof(*p), GFP_KERNEL); u32 reg; if (!p) @@ -467,22 +460,32 @@ static inline void qbman_write_eqcr_am_rt_register(struct qbman_swp *p, int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct dpaa2_fd *fd) { - struct qbman_eq_desc *p; + struct qbman_eq_desc_with_fd *p; u32 eqar = qbman_read_register(s, QBMAN_CINH_SWP_EQAR); if (!EQAR_SUCCESS(eqar)) return -EBUSY; p = qbman_get_cmd(s, QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); - memcpy(&p->dca, &d->dca, 31); + /* This is mapped as DEVICE type memory, writes are + * with address alignment: + * desc.dca address alignment = 1 + * desc.seqnum address alignment = 2 + * desc.orpid address alignment = 4 + * desc.tgtid address alignment = 8 + */ + p->desc.dca = d->dca; + p->desc.seqnum = d->seqnum; + p->desc.orpid = d->orpid; + memcpy(&p->desc.tgtid, &d->tgtid, 24); memcpy(&p->fd, fd, sizeof(*fd)); if ((s->desc->qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { /* Set the verb byte, have to substitute in the valid-bit */ dma_wmb(); - p->verb = d->verb | EQAR_VB(eqar); + p->desc.verb = d->verb | EQAR_VB(eqar); } else { - p->verb = d->verb | EQAR_VB(eqar); + p->desc.verb = d->verb | EQAR_VB(eqar); dma_wmb(); qbman_write_eqcr_am_rt_register(s, EQAR_IDX(eqar)); } @@ -490,6 +493,60 @@ int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, return 0; } +/** + * qbman_swp_enqueue_multiple() - Issue a multi enqueue command + * using one enqueue descriptor + * @s: the software portal used for enqueue + * @d: the enqueue descriptor + * @fd: table pointer of frame descriptor table to be enqueued + * @flags: table pointer of flags, not used for the moment + * @num_frames: number of fd to be enqueued + * + * Return the number of fd enqueued, or a negative error number. + */ +int qbman_swp_enqueue_multiple(struct qbman_swp *s, + const struct qbman_eq_desc *d, + const struct dpaa2_fd *fd, + uint32_t *flags, + int num_frames) +{ + int count = 0; + + while (count < num_frames) { + if (qbman_swp_enqueue(s, d, fd) != 0) + break; + count++; + } + + return count; +} + +/** + * qbman_swp_enqueue_multiple_desc() - Issue a multi enqueue command + * using multiple enqueue descriptor + * @s: the software portal used for enqueue + * @d: table of minimal enqueue descriptor + * @fd: table pointer of frame descriptor table to be enqueued + * @num_frames: number of fd to be enqueued + * + * Return the number of fd enqueued, or a negative error number. + */ +int qbman_swp_enqueue_multiple_desc(struct qbman_swp *s, + const struct qbman_eq_desc *d, + const struct dpaa2_fd *fd, + int num_frames) +{ + int count = 0; + + while (count < num_frames) { + if (qbman_swp_enqueue(s, &(d[count]), fd) != 0) + break; + count++; + } + + return count; +} + /* Static (push) dequeue */ /** diff --git a/drivers/soc/fsl/dpio/qbman-portal.h b/drivers/soc/fsl/dpio/qbman-portal.h index f3ec5d2044fb..ac58a9741d33 100644 --- a/drivers/soc/fsl/dpio/qbman-portal.h +++ b/drivers/soc/fsl/dpio/qbman-portal.h @@ -9,6 +9,13 @@ #include +#define QMAN_REV_4000 0x04000000 +#define QMAN_REV_4100 0x04010000 +#define QMAN_REV_4101 0x04010001 +#define QMAN_REV_5000 0x05000000 + +#define QMAN_REV_MASK 0xffff0000 + struct dpaa2_dq; struct qbman_swp; @@ -81,6 +88,10 @@ struct qbman_eq_desc { u8 wae; u8 rspid; __le64 rsp_addr; +}; + +struct qbman_eq_desc_with_fd { + struct qbman_eq_desc desc; u8 fd[32]; }; @@ -193,6 +204,19 @@ void *qbman_swp_mc_start(struct qbman_swp *p); void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, u8 cmd_verb); void *qbman_swp_mc_result(struct qbman_swp *p); +int +qbman_swp_enqueue_multiple(struct qbman_swp *s, + const struct qbman_eq_desc *d, + const struct dpaa2_fd *fd, + uint32_t *flags, + int num_frames); + +int +qbman_swp_enqueue_multiple_desc(struct qbman_swp *s, + const struct qbman_eq_desc *d, + const struct dpaa2_fd *fd, + int num_frames); + /** * qbman_result_is_DQ() - check if the dequeue result is a dequeue response * @dq: the dequeue result to be checked diff --git a/include/soc/fsl/dpaa2-io.h b/include/soc/fsl/dpaa2-io.h index 672cfb58046f..c9d849924f89 100644 --- a/include/soc/fsl/dpaa2-io.h +++ b/include/soc/fsl/dpaa2-io.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* * Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright NXP + * Copyright 2017-2019 NXP * */ #ifndef __FSL_DPAA2_IO_H @@ -109,6 +109,10 @@ int dpaa2_io_service_pull_channel(struct dpaa2_io *d, u32 channelid, int dpaa2_io_service_enqueue_fq(struct dpaa2_io *d, u32 fqid, const struct dpaa2_fd *fd); +int dpaa2_io_service_enqueue_multiple_fq(struct dpaa2_io *d, u32 fqid, + const struct dpaa2_fd *fd, int number_of_frame); +int dpaa2_io_service_enqueue_multiple_desc_fq(struct dpaa2_io *d, u32 *fqid, + const struct dpaa2_fd *fd, int number_of_frame); int dpaa2_io_service_enqueue_qd(struct dpaa2_io *d, u32 qdid, u8 prio, u16 qdbin, const struct dpaa2_fd *fd); int dpaa2_io_service_release(struct dpaa2_io *d, u16 bpid, -- cgit v1.2.3 From bd1a5a53d7c1231f89c51705f6a894516b40aa88 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 15 Feb 2020 23:08:38 -0800 Subject: security: : fix all kernel-doc warnings Fix all kernel-doc warnings in . Fixes the following warnings: ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'quotactl' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'quota_on' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'sb_free_mnt_opts' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'sb_eat_lsm_opts' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'sb_kern_mount' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'sb_show_options' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'sb_add_mnt_opt' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'd_instantiate' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'getprocattr' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'setprocattr' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'locked_down' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'perf_event_open' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'perf_event_alloc' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'perf_event_free' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'perf_event_read' not described in 'security_list_options' ../include/linux/lsm_hooks.h:1830: warning: Function parameter or member 'perf_event_write' not described in 'security_list_options' Signed-off-by: Randy Dunlap Cc: John Johansen Cc: Kees Cook Cc: Micah Morton Cc: James Morris Cc: "Serge E. Hallyn" Cc: linux-security-module@vger.kernel.org Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: Casey Schaufler Cc: Kentaro Takeda Cc: Tetsuo Handa Acked-by: Casey Schaufler Acked-by: Kees Cook Signed-off-by: James Morris --- include/linux/lsm_hooks.h | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index a3763247547c..59f5ee945c1c 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -103,6 +103,10 @@ * @sb_free_security: * Deallocate and clear the sb->s_security field. * @sb contains the super_block structure to be modified. + * @sb_free_mnt_opts: + * Free memory associated with @mnt_ops. + * @sb_eat_lsm_opts: + * Eat (scan @orig options) and save them in @mnt_opts. * @sb_statfs: * Check permission before obtaining filesystem statistics for the @mnt * mountpoint. @@ -136,6 +140,10 @@ * @sb superblock being remounted * @data contains the filesystem-specific data. * Return 0 if permission is granted. + * @sb_kern_mount: + * Mount this @sb if allowed by permissions. + * @sb_show_options: + * Show (print on @m) mount options for this @sb. * @sb_umount: * Check permission before the @mnt file system is unmounted. * @mnt contains the mounted file system. @@ -155,6 +163,8 @@ * Copy all security options from a given superblock to another * @oldsb old superblock which contain information to clone * @newsb new superblock which needs filled in + * @sb_add_mnt_opt: + * Add one mount @option to @mnt_opts. * @sb_parse_opts_str: * Parse a string of security data filling in the opts structure * @options string containing all mount options known by the LSM @@ -451,6 +461,12 @@ * security module does not know about attribute or a negative error code * to abort the copy up. Note that the caller is responsible for reading * and writing the xattrs as this hook is merely a filter. + * @d_instantiate: + * Fill in @inode security information for a @dentry if allowed. + * @getprocattr: + * Read attribute @name for process @p and store it into @value if allowed. + * @setprocattr: + * Write (set) attribute @name to @value, size @size if allowed. * * Security hooks for kernfs node operations * @@ -1113,6 +1129,7 @@ * In case of failure, @secid will be set to zero. * * Security hooks for individual messages held in System V IPC message queues + * * @msg_msg_alloc_security: * Allocate and attach a security structure to the msg->security field. * The security field is initialized to NULL when the structure is first @@ -1302,6 +1319,10 @@ * @cap contains the capability . * @opts contains options for the capable check * Return 0 if the capability is granted for @tsk. + * @quotactl: + * Check whether the quotactl syscall is allowed for this @sb. + * @quota_on: + * Check whether QUOTAON is allowed for this @dentry. * @syslog: * Check permission before accessing the kernel message ring or changing * logging to the console. @@ -1449,11 +1470,24 @@ * @bpf_prog_free_security: * Clean up the security information stored inside bpf prog. * - * @locked_down + * @locked_down: * Determine whether a kernel feature that potentially enables arbitrary * code execution in kernel space should be permitted. * * @what: kernel feature being accessed + * + * Security hooks for perf events + * + * @perf_event_open: + * Check whether the @type of perf_event_open syscall is allowed. + * @perf_event_alloc: + * Allocate and save perf_event security info. + * @perf_event_free: + * Release (free) perf_event security info. + * @perf_event_read: + * Read perf_event security info if allowed. + * @perf_event_write: + * Write perf_event security info if allowed. */ union security_list_options { int (*binder_set_context_mgr)(struct task_struct *mgr); -- cgit v1.2.3 From 9410c9409d3e3a1ee6a02a830f9b6ab678c456d1 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Sun, 16 Feb 2020 12:01:21 +0200 Subject: net: sched: Introduce ingress classification function TC multi chain configuration can cause offloaded tc chains to miss in hardware after jumping to some chain. In such cases the software should continue from the chain that missed in hardware, as the hardware may have manipulated the packet and updated some counters. Currently a single tcf classification function serves both ingress and egress. However, multi chain miss processing (get tc skb extension on hw miss, set tc skb extension on tc miss) should happen only on ingress. Refactor the code to use ingress classification function, and move setting the tc skb extension from general classification to it, as a prestep for supporting the hw miss scenario. Co-developed-by: Vlad Buslov Signed-off-by: Vlad Buslov Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: Saeed Mahameed --- include/net/pkt_cls.h | 10 +++++++++ net/core/dev.c | 3 ++- net/sched/cls_api.c | 58 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 56 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 53946b509b51..109cbe3a0d51 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -72,6 +72,8 @@ static inline struct Qdisc *tcf_block_q(struct tcf_block *block) int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode); +int tcf_classify_ingress(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res, bool compat_mode); #else static inline bool tcf_block_shared(struct tcf_block *block) @@ -133,6 +135,14 @@ static inline int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, { return TC_ACT_UNSPEC; } + +static inline int tcf_classify_ingress(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res, bool compat_mode) +{ + return TC_ACT_UNSPEC; +} + #endif static inline unsigned long diff --git a/net/core/dev.c b/net/core/dev.c index a6316b336128..107af00e4932 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4860,7 +4860,8 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, skb->tc_at_ingress = 1; mini_qdisc_bstats_cpu_update(miniq, skb); - switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { + switch (tcf_classify_ingress(skb, miniq->filter_list, &cl_res, + false)) { case TC_ACT_OK: case TC_ACT_RECLASSIFY: skb->tc_index = TC_H_MIN(cl_res.classid); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 13c33eaf1ca1..d52b43c56d5d 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1559,8 +1559,11 @@ static int tcf_block_setup(struct tcf_block *block, * to this qdisc, (optionally) tests for protocol and asks * specific classifiers. */ -int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res, bool compat_mode) +static inline int __tcf_classify(struct sk_buff *skb, + const struct tcf_proto *tp, + struct tcf_result *res, + bool compat_mode, + u32 *last_executed_chain) { #ifdef CONFIG_NET_CLS_ACT const int max_reclassify_loop = 4; @@ -1582,21 +1585,11 @@ reclassify: #ifdef CONFIG_NET_CLS_ACT if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) { first_tp = orig_tp; + *last_executed_chain = first_tp->chain->index; goto reset; } else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) { first_tp = res->goto_tp; - -#if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) - { - struct tc_skb_ext *ext; - - ext = skb_ext_add(skb, TC_SKB_EXT); - if (WARN_ON_ONCE(!ext)) - return TC_ACT_SHOT; - - ext->chain = err & TC_ACT_EXT_VAL_MASK; - } -#endif + *last_executed_chain = err & TC_ACT_EXT_VAL_MASK; goto reset; } #endif @@ -1619,8 +1612,45 @@ reset: goto reclassify; #endif } + +int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res, bool compat_mode) +{ + u32 last_executed_chain = 0; + + return __tcf_classify(skb, tp, res, compat_mode, + &last_executed_chain); +} EXPORT_SYMBOL(tcf_classify); +int tcf_classify_ingress(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res, bool compat_mode) +{ +#if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT) + u32 last_executed_chain = 0; + + return __tcf_classify(skb, tp, res, compat_mode, + &last_executed_chain); +#else + u32 last_executed_chain = tp ? tp->chain->index : 0; + struct tc_skb_ext *ext; + int ret; + + ret = __tcf_classify(skb, tp, res, compat_mode, &last_executed_chain); + + /* If we missed on some chain */ + if (ret == TC_ACT_UNSPEC && last_executed_chain) { + ext = skb_ext_add(skb, TC_SKB_EXT); + if (WARN_ON_ONCE(!ext)) + return TC_ACT_SHOT; + ext->chain = last_executed_chain; + } + + return ret; +#endif +} +EXPORT_SYMBOL(tcf_classify_ingress); + struct tcf_chain_info { struct tcf_proto __rcu **pprev; struct tcf_proto __rcu *next; -- cgit v1.2.3 From 7d17c544cd304c15317e64ac77617bc774fb3f55 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Sun, 16 Feb 2020 12:01:22 +0200 Subject: net: sched: Pass ingress block to tcf_classify_ingress On ingress and cls_act qdiscs init, save the block on ingress mini_Qdisc and and pass it on to ingress classification, so it can be used for the looking up a specified chain index. Co-developed-by: Vlad Buslov Signed-off-by: Vlad Buslov Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: Saeed Mahameed --- include/net/pkt_cls.h | 7 +++++-- include/net/sch_generic.h | 3 +++ net/core/dev.c | 4 ++-- net/sched/cls_api.c | 4 +++- net/sched/sch_generic.c | 8 ++++++++ net/sched/sch_ingress.c | 11 ++++++++++- 6 files changed, 31 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 109cbe3a0d51..75be5c065dba 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -72,8 +72,10 @@ static inline struct Qdisc *tcf_block_q(struct tcf_block *block) int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode); -int tcf_classify_ingress(struct sk_buff *skb, const struct tcf_proto *tp, - struct tcf_result *res, bool compat_mode); +int tcf_classify_ingress(struct sk_buff *skb, + const struct tcf_block *ingress_block, + const struct tcf_proto *tp, struct tcf_result *res, + bool compat_mode); #else static inline bool tcf_block_shared(struct tcf_block *block) @@ -137,6 +139,7 @@ static inline int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, } static inline int tcf_classify_ingress(struct sk_buff *skb, + const struct tcf_block *ingress_block, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode) { diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 151208704ed2..bcdf98d21094 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -1269,6 +1269,7 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res, */ struct mini_Qdisc { struct tcf_proto *filter_list; + struct tcf_block *block; struct gnet_stats_basic_cpu __percpu *cpu_bstats; struct gnet_stats_queue __percpu *cpu_qstats; struct rcu_head rcu; @@ -1295,6 +1296,8 @@ void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, struct tcf_proto *tp_head); void mini_qdisc_pair_init(struct mini_Qdisc_pair *miniqp, struct Qdisc *qdisc, struct mini_Qdisc __rcu **p_miniq); +void mini_qdisc_pair_block_init(struct mini_Qdisc_pair *miniqp, + struct tcf_block *block); static inline int skb_tc_reinsert(struct sk_buff *skb, struct tcf_result *res) { diff --git a/net/core/dev.c b/net/core/dev.c index 107af00e4932..4866e6198a29 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4860,8 +4860,8 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, skb->tc_at_ingress = 1; mini_qdisc_bstats_cpu_update(miniq, skb); - switch (tcf_classify_ingress(skb, miniq->filter_list, &cl_res, - false)) { + switch (tcf_classify_ingress(skb, miniq->block, miniq->filter_list, + &cl_res, false)) { case TC_ACT_OK: case TC_ACT_RECLASSIFY: skb->tc_index = TC_H_MIN(cl_res.classid); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index d52b43c56d5d..bbd8b5e7b74b 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1623,7 +1623,9 @@ int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, } EXPORT_SYMBOL(tcf_classify); -int tcf_classify_ingress(struct sk_buff *skb, const struct tcf_proto *tp, +int tcf_classify_ingress(struct sk_buff *skb, + const struct tcf_block *ingress_block, + const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode) { #if !IS_ENABLED(CONFIG_NET_TC_SKB_EXT) diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6c9595f1048a..2efd5b61acef 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1391,6 +1391,14 @@ void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, } EXPORT_SYMBOL(mini_qdisc_pair_swap); +void mini_qdisc_pair_block_init(struct mini_Qdisc_pair *miniqp, + struct tcf_block *block) +{ + miniqp->miniq1.block = block; + miniqp->miniq2.block = block; +} +EXPORT_SYMBOL(mini_qdisc_pair_block_init); + void mini_qdisc_pair_init(struct mini_Qdisc_pair *miniqp, struct Qdisc *qdisc, struct mini_Qdisc __rcu **p_miniq) { diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index bf56aa519797..84838128b9c5 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -78,6 +78,7 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt, { struct ingress_sched_data *q = qdisc_priv(sch); struct net_device *dev = qdisc_dev(sch); + int err; net_inc_ingress_queue(); @@ -87,7 +88,13 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt, q->block_info.chain_head_change = clsact_chain_head_change; q->block_info.chain_head_change_priv = &q->miniqp; - return tcf_block_get_ext(&q->block, sch, &q->block_info, extack); + err = tcf_block_get_ext(&q->block, sch, &q->block_info, extack); + if (err) + return err; + + mini_qdisc_pair_block_init(&q->miniqp, q->block); + + return 0; } static void ingress_destroy(struct Qdisc *sch) @@ -226,6 +233,8 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt, if (err) return err; + mini_qdisc_pair_block_init(&q->miniqp_ingress, q->ingress_block); + mini_qdisc_pair_init(&q->miniqp_egress, sch, &dev->miniq_egress); q->egress_block_info.binder_type = FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS; -- cgit v1.2.3 From 0f0d3827c0b4d6c3d219a73ea103077dc5bc17aa Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Sun, 16 Feb 2020 12:01:26 +0200 Subject: net/mlx5: E-Switch, Move source port on reg_c0 to the upper 16 bits Multi chain support requires the miss path to continue the processing from the last chain id, and for that we need to save the chain miss tag (a mapping for 32bit chain id) on reg_c0 which will come in a next patch. Currently reg_c0 is exclusively used to store the source port metadata, giving it 32bit, it is created from 16bits of vcha_id, and 16bits of vport number. We will move this source port metadata to upper 16bits, and leave the lower bits for the chain miss tag. We compress the reg_c0 source port metadata to 16bits by taking 8 bits from vhca_id, and 8bits from the vport number. Since we compress the vport number to 8bits statically, and leave two top ids for special PF/ECPF numbers, we will only support a max of 254 vports with this strategy. Signed-off-by: Paul Blakey Reviewed-by: Oz Shlomo Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- drivers/infiniband/hw/mlx5/main.c | 3 +- .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 64 ++++++++++++++++++---- include/linux/mlx5/eswitch.h | 29 +++++++++- 3 files changed, 83 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index e874d688d040..230028cb292c 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -3570,7 +3570,8 @@ static void mlx5_ib_set_rule_source_port(struct mlx5_ib_dev *dev, misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_2); - MLX5_SET_TO_ONES(fte_match_set_misc2, misc, metadata_reg_c_0); + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_mask()); } else { misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 979f13bdc203..788bb83b18e5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -85,7 +85,8 @@ mlx5_eswitch_set_rule_source_port(struct mlx5_eswitch *esw, attr->in_rep->vport)); misc2 = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_2); - MLX5_SET_TO_ONES(fte_match_set_misc2, misc2, metadata_reg_c_0); + MLX5_SET(fte_match_set_misc2, misc2, metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_mask()); spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2; misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters); @@ -621,7 +622,8 @@ static void peer_miss_rules_setup(struct mlx5_eswitch *esw, if (mlx5_eswitch_vport_match_metadata_enabled(esw)) { misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_2); - MLX5_SET_TO_ONES(fte_match_set_misc2, misc, metadata_reg_c_0); + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_mask()); spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2; } else { @@ -851,8 +853,9 @@ static void esw_set_flow_group_source_port(struct mlx5_eswitch *esw, match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS_2); - MLX5_SET_TO_ONES(fte_match_param, match_criteria, - misc_parameters_2.metadata_reg_c_0); + MLX5_SET(fte_match_param, match_criteria, + misc_parameters_2.metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_mask()); } else { MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, @@ -1134,7 +1137,8 @@ mlx5_eswitch_create_vport_rx_rule(struct mlx5_eswitch *esw, u16 vport, mlx5_eswitch_get_vport_metadata_for_match(esw, vport)); misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_2); - MLX5_SET_TO_ONES(fte_match_set_misc2, misc, metadata_reg_c_0); + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, + mlx5_eswitch_get_vport_metadata_mask()); spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2; } else { @@ -1604,11 +1608,19 @@ static int esw_vport_add_ingress_acl_modify_metadata(struct mlx5_eswitch *esw, static const struct mlx5_flow_spec spec = {}; struct mlx5_flow_act flow_act = {}; int err = 0; + u32 key; + + key = mlx5_eswitch_get_vport_metadata_for_match(esw, vport->vport); + key >>= ESW_SOURCE_PORT_METADATA_OFFSET; MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); - MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_C_0); - MLX5_SET(set_action_in, action, data, - mlx5_eswitch_get_vport_metadata_for_match(esw, vport->vport)); + MLX5_SET(set_action_in, action, field, + MLX5_ACTION_IN_FIELD_METADATA_REG_C_0); + MLX5_SET(set_action_in, action, data, key); + MLX5_SET(set_action_in, action, offset, + ESW_SOURCE_PORT_METADATA_OFFSET); + MLX5_SET(set_action_in, action, length, + ESW_SOURCE_PORT_METADATA_BITS); vport->ingress.offloads.modify_metadata = mlx5_modify_header_alloc(esw->dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS, @@ -2470,9 +2482,41 @@ bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw) } EXPORT_SYMBOL(mlx5_eswitch_vport_match_metadata_enabled); -u32 mlx5_eswitch_get_vport_metadata_for_match(const struct mlx5_eswitch *esw, +u32 mlx5_eswitch_get_vport_metadata_for_match(struct mlx5_eswitch *esw, u16 vport_num) { - return ((MLX5_CAP_GEN(esw->dev, vhca_id) & 0xffff) << 16) | vport_num; + u32 vport_num_mask = GENMASK(ESW_VPORT_BITS - 1, 0); + u32 vhca_id_mask = GENMASK(ESW_VHCA_ID_BITS - 1, 0); + u32 vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id); + u32 val; + + /* Make sure the vhca_id fits the ESW_VHCA_ID_BITS */ + WARN_ON_ONCE(vhca_id >= BIT(ESW_VHCA_ID_BITS)); + + /* Trim vhca_id to ESW_VHCA_ID_BITS */ + vhca_id &= vhca_id_mask; + + /* Make sure pf and ecpf map to end of ESW_VPORT_BITS range so they + * don't overlap with VF numbers, and themselves, after trimming. + */ + WARN_ON_ONCE((MLX5_VPORT_UPLINK & vport_num_mask) < + vport_num_mask - 1); + WARN_ON_ONCE((MLX5_VPORT_ECPF & vport_num_mask) < + vport_num_mask - 1); + WARN_ON_ONCE((MLX5_VPORT_UPLINK & vport_num_mask) == + (MLX5_VPORT_ECPF & vport_num_mask)); + + /* Make sure that the VF vport_num fits ESW_VPORT_BITS and don't + * overlap with pf and ecpf. + */ + if (vport_num != MLX5_VPORT_UPLINK && + vport_num != MLX5_VPORT_ECPF) + WARN_ON_ONCE(vport_num >= vport_num_mask - 1); + + /* We can now trim vport_num to ESW_VPORT_BITS */ + vport_num &= vport_num_mask; + + val = (vhca_id << ESW_VPORT_BITS) | vport_num; + return val << (32 - ESW_SOURCE_PORT_METADATA_BITS); } EXPORT_SYMBOL(mlx5_eswitch_get_vport_metadata_for_match); diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h index 98e667b176ef..dd1333f29f6e 100644 --- a/include/linux/mlx5/eswitch.h +++ b/include/linux/mlx5/eswitch.h @@ -71,7 +71,26 @@ enum devlink_eswitch_encap_mode mlx5_eswitch_get_encap_mode(const struct mlx5_core_dev *dev); bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw); -u32 mlx5_eswitch_get_vport_metadata_for_match(const struct mlx5_eswitch *esw, + +/* Reg C0 usage: + * Reg C0 = < ESW_VHCA_ID_BITS(8) | ESW_VPORT BITS(8) | ESW_CHAIN_TAG(16) > + * + * Highest 8 bits of the reg c0 is the vhca_id, next 8 bits is vport_num, + * the rest (lowest 16 bits) is left for tc chain tag restoration. + * VHCA_ID + VPORT comprise the SOURCE_PORT matching. + */ +#define ESW_VHCA_ID_BITS 8 +#define ESW_VPORT_BITS 8 +#define ESW_SOURCE_PORT_METADATA_BITS (ESW_VHCA_ID_BITS + ESW_VPORT_BITS) +#define ESW_SOURCE_PORT_METADATA_OFFSET (32 - ESW_SOURCE_PORT_METADATA_BITS) +#define ESW_CHAIN_TAG_METADATA_BITS (32 - ESW_SOURCE_PORT_METADATA_BITS) + +static inline u32 mlx5_eswitch_get_vport_metadata_mask(void) +{ + return GENMASK(31, 32 - ESW_SOURCE_PORT_METADATA_BITS); +} + +u32 mlx5_eswitch_get_vport_metadata_for_match(struct mlx5_eswitch *esw, u16 vport_num); u8 mlx5_eswitch_mode(struct mlx5_eswitch *esw); #else /* CONFIG_MLX5_ESWITCH */ @@ -94,11 +113,17 @@ mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw) }; static inline u32 -mlx5_eswitch_get_vport_metadata_for_match(const struct mlx5_eswitch *esw, +mlx5_eswitch_get_vport_metadata_for_match(struct mlx5_eswitch *esw, int vport_num) { return 0; }; + +static inline u32 +mlx5_eswitch_get_vport_metadata_mask(void) +{ + return 0; +} #endif /* CONFIG_MLX5_ESWITCH */ #endif -- cgit v1.2.3 From 11b717d6152699623fb1133759f9b8f235935a51 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Sun, 16 Feb 2020 12:01:27 +0200 Subject: net/mlx5: E-Switch, Get reg_c0 value on CQE On RX side create a restore table in OFFLOADS namespace. This table will match on all values for reg_c0 we will use, and set it to the flow_tag. This flow tag can then be read on the CQE. As there is no copy action from reg c0 to flow tag, instead we have to set the flow tag explictily. We add an API so callers can add all the used reg_c0 values (tags) and for each of those we add a restore rule. This will be used in a following patch to save the miss chain mapping tag on reg_c0 and from it restore the tc chain on the skb. Signed-off-by: Paul Blakey Reviewed-by: Roi Dayan Reviewed-by: Oz Shlomo Reviewed-by: Mark Bloch Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 14 ++ .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 147 +++++++++++++++++++-- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 4 +- include/linux/mlx5/eswitch.h | 2 + 4 files changed, 156 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 4472710ccc9c..a94d91cdc758 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -189,6 +189,9 @@ struct mlx5_eswitch_fdb { }; struct mlx5_esw_offload { + struct mlx5_flow_table *ft_offloads_restore; + struct mlx5_flow_group *restore_group; + struct mlx5_flow_table *ft_offloads; struct mlx5_flow_group *vport_rx_group; struct mlx5_eswitch_rep *vport_reps; @@ -623,6 +626,11 @@ void esw_vport_destroy_offloads_acl_tables(struct mlx5_eswitch *esw, struct mlx5_vport *vport); +struct mlx5_flow_handle * +esw_add_restore_rule(struct mlx5_eswitch *esw, u32 tag); +u32 +esw_get_max_restore_tag(struct mlx5_eswitch *esw); + #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } @@ -638,6 +646,12 @@ static inline const u32 *mlx5_esw_query_functions(struct mlx5_core_dev *dev) static inline void mlx5_eswitch_update_num_of_vfs(struct mlx5_eswitch *esw, const int num_vfs) {} +static struct mlx5_flow_handle * +esw_add_restore_rule(struct mlx5_eswitch *esw, u32 tag) +{ + return ERR_PTR(-EOPNOTSUPP); +} + #endif /* CONFIG_MLX5_ESWITCH */ #endif /* __MLX5_ESWITCH_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 788bb83b18e5..81c2cbf0c308 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -838,6 +838,54 @@ out: return err; } +struct mlx5_flow_handle * +esw_add_restore_rule(struct mlx5_eswitch *esw, u32 tag) +{ + struct mlx5_flow_act flow_act = { .flags = FLOW_ACT_NO_APPEND, }; + struct mlx5_flow_table *ft = esw->offloads.ft_offloads_restore; + struct mlx5_flow_context *flow_context; + struct mlx5_flow_handle *flow_rule; + struct mlx5_flow_destination dest; + struct mlx5_flow_spec *spec; + void *misc; + + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return ERR_PTR(-ENOMEM); + + misc = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, + misc_parameters_2); + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, + ESW_CHAIN_TAG_METADATA_MASK); + misc = MLX5_ADDR_OF(fte_match_param, spec->match_value, + misc_parameters_2); + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, tag); + spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2; + flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST; + + flow_context = &spec->flow_context; + flow_context->flags |= FLOW_CONTEXT_HAS_TAG; + flow_context->flow_tag = tag; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = esw->offloads.ft_offloads; + + flow_rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); + kfree(spec); + + if (IS_ERR(flow_rule)) + esw_warn(esw->dev, + "Failed to create restore rule for tag: %d, err(%d)\n", + tag, (int)PTR_ERR(flow_rule)); + + return flow_rule; +} + +u32 +esw_get_max_restore_tag(struct mlx5_eswitch *esw) +{ + return ESW_CHAIN_TAG_METADATA_MASK; +} + #define MAX_PF_SQ 256 #define MAX_SQ_NVPORTS 32 @@ -1060,6 +1108,7 @@ static int esw_create_offloads_table(struct mlx5_eswitch *esw, int nvports) } ft_attr.max_fte = nvports + MLX5_ESW_MISS_FLOWS; + ft_attr.prio = 1; ft_offloads = mlx5_create_flow_table(ns, &ft_attr); if (IS_ERR(ft_offloads)) { @@ -1164,6 +1213,81 @@ out: return flow_rule; } +static void esw_destroy_restore_table(struct mlx5_eswitch *esw) +{ + struct mlx5_esw_offload *offloads = &esw->offloads; + + mlx5_destroy_flow_group(offloads->restore_group); + mlx5_destroy_flow_table(offloads->ft_offloads_restore); +} + +static int esw_create_restore_table(struct mlx5_eswitch *esw) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_namespace *ns; + void *match_criteria, *misc; + struct mlx5_flow_table *ft; + struct mlx5_flow_group *g; + u32 *flow_group_in; + int err = 0; + + ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_OFFLOADS); + if (!ns) { + esw_warn(esw->dev, "Failed to get offloads flow namespace\n"); + return -EOPNOTSUPP; + } + + flow_group_in = kvzalloc(inlen, GFP_KERNEL); + if (!flow_group_in) { + err = -ENOMEM; + goto out_free; + } + + ft_attr.max_fte = 1 << ESW_CHAIN_TAG_METADATA_BITS; + ft = mlx5_create_flow_table(ns, &ft_attr); + if (IS_ERR(ft)) { + err = PTR_ERR(ft); + esw_warn(esw->dev, "Failed to create restore table, err %d\n", + err); + goto out_free; + } + + memset(flow_group_in, 0, inlen); + match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, + match_criteria); + misc = MLX5_ADDR_OF(fte_match_param, match_criteria, + misc_parameters_2); + + MLX5_SET(fte_match_set_misc2, misc, metadata_reg_c_0, + ESW_CHAIN_TAG_METADATA_MASK); + MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0); + MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, + ft_attr.max_fte - 1); + MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, + MLX5_MATCH_MISC_PARAMETERS_2); + g = mlx5_create_flow_group(ft, flow_group_in); + if (IS_ERR(g)) { + err = PTR_ERR(g); + esw_warn(dev, "Failed to create restore flow group, err: %d\n", + err); + goto err_group; + } + + esw->offloads.ft_offloads_restore = ft; + esw->offloads.restore_group = g; + + return 0; + +err_group: + mlx5_destroy_flow_table(ft); +out_free: + kvfree(flow_group_in); + + return err; +} + static int esw_offloads_start(struct mlx5_eswitch *esw, struct netlink_ext_ack *extack) { @@ -1923,13 +2047,17 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw) if (err) return err; - err = esw_create_offloads_fdb_tables(esw, total_vports); + err = esw_create_offloads_table(esw, total_vports); if (err) - goto create_fdb_err; + goto create_offloads_err; - err = esw_create_offloads_table(esw, total_vports); + err = esw_create_restore_table(esw); if (err) - goto create_ft_err; + goto create_restore_err; + + err = esw_create_offloads_fdb_tables(esw, total_vports); + if (err) + goto create_fdb_err; err = esw_create_vport_rx_group(esw, total_vports); if (err) @@ -1938,12 +2066,12 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw) return 0; create_fg_err: - esw_destroy_offloads_table(esw); - -create_ft_err: esw_destroy_offloads_fdb_tables(esw); - create_fdb_err: + esw_destroy_restore_table(esw); +create_restore_err: + esw_destroy_offloads_table(esw); +create_offloads_err: esw_destroy_uplink_offloads_acl_tables(esw); return err; @@ -1952,8 +2080,9 @@ create_fdb_err: static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) { esw_destroy_vport_rx_group(esw); - esw_destroy_offloads_table(esw); esw_destroy_offloads_fdb_tables(esw); + esw_destroy_restore_table(esw); + esw_destroy_offloads_table(esw); esw_destroy_uplink_offloads_acl_tables(esw); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 9dc24241dc91..2660ffabb09f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -111,8 +111,8 @@ #define ANCHOR_MIN_LEVEL (BY_PASS_MIN_LEVEL + 1) #define OFFLOADS_MAX_FT 1 -#define OFFLOADS_NUM_PRIOS 1 -#define OFFLOADS_MIN_LEVEL (ANCHOR_MIN_LEVEL + 1) +#define OFFLOADS_NUM_PRIOS 2 +#define OFFLOADS_MIN_LEVEL (ANCHOR_MIN_LEVEL + OFFLOADS_NUM_PRIOS) #define LAG_PRIO_NUM_LEVELS 1 #define LAG_NUM_PRIOS 1 diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h index dd1333f29f6e..61705e74a5bb 100644 --- a/include/linux/mlx5/eswitch.h +++ b/include/linux/mlx5/eswitch.h @@ -84,6 +84,8 @@ bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw); #define ESW_SOURCE_PORT_METADATA_BITS (ESW_VHCA_ID_BITS + ESW_VPORT_BITS) #define ESW_SOURCE_PORT_METADATA_OFFSET (32 - ESW_SOURCE_PORT_METADATA_BITS) #define ESW_CHAIN_TAG_METADATA_BITS (32 - ESW_SOURCE_PORT_METADATA_BITS) +#define ESW_CHAIN_TAG_METADATA_MASK GENMASK(ESW_CHAIN_TAG_METADATA_BITS - 1,\ + 0) static inline u32 mlx5_eswitch_get_vport_metadata_mask(void) { -- cgit v1.2.3 From 5b361328ca649534d721e4eae20c96ccbe702ce7 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 12 Feb 2020 19:04:25 -0600 Subject: RDMA: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Link: https://lore.kernel.org/r/20200213010425.GA13068@embeddedor.com Signed-off-by: Gustavo A. R. Silva Signed-off-by: Jason Gunthorpe # added a few more --- drivers/infiniband/core/cache.c | 2 +- drivers/infiniband/core/cm.c | 4 ++-- drivers/infiniband/core/mad_priv.h | 4 ++-- drivers/infiniband/core/multicast.c | 2 +- drivers/infiniband/core/sa_query.c | 2 +- drivers/infiniband/hw/cxgb4/iw_cxgb4.h | 4 ++-- drivers/infiniband/hw/cxgb4/t4fw_ri_api.h | 8 ++++---- drivers/infiniband/hw/hfi1/mad.c | 4 ++-- drivers/infiniband/hw/hfi1/mad.h | 2 +- drivers/infiniband/hw/hfi1/pio.h | 4 ++-- drivers/infiniband/hw/hfi1/sdma.c | 2 +- drivers/infiniband/hw/hfi1/sdma.h | 4 ++-- drivers/infiniband/hw/hfi1/user_exp_rcv.h | 2 +- drivers/infiniband/hw/i40iw/i40iw_cm.h | 4 ++-- drivers/infiniband/hw/mthca/mthca_memfree.c | 2 +- drivers/infiniband/hw/mthca/mthca_memfree.h | 2 +- drivers/infiniband/hw/usnic/usnic_uiom.h | 2 +- drivers/infiniband/sw/rxe/rxe_queue.h | 2 +- drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h | 4 ++-- drivers/infiniband/ulp/srp/ib_srp.h | 2 +- include/rdma/ib_fmr_pool.h | 2 +- include/rdma/ib_verbs.h | 18 +++++++++--------- include/rdma/opa_vnic.h | 2 +- include/rdma/rdmavt_mr.h | 2 +- include/rdma/rdmavt_qp.h | 2 +- 25 files changed, 44 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c index 9bbdace3c809..717b798cddad 100644 --- a/drivers/infiniband/core/cache.c +++ b/drivers/infiniband/core/cache.c @@ -46,7 +46,7 @@ struct ib_pkey_cache { int table_len; - u16 table[0]; + u16 table[]; }; struct ib_update_work { diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 33c0d9e7bb66..aec6867f0ed2 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -197,7 +197,7 @@ struct cm_device { struct ib_device *ib_device; u8 ack_delay; int going_down; - struct cm_port *port[0]; + struct cm_port *port[]; }; struct cm_av { @@ -216,7 +216,7 @@ struct cm_work { __be32 local_id; /* Established / timewait */ __be32 remote_id; struct ib_cm_event cm_event; - struct sa_path_rec path[0]; + struct sa_path_rec path[]; }; struct cm_timewait_info { diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h index 956b3a7dfed7..403d8673a2f9 100644 --- a/drivers/infiniband/core/mad_priv.h +++ b/drivers/infiniband/core/mad_priv.h @@ -79,13 +79,13 @@ struct ib_mad_private { struct ib_mad_private_header header; size_t mad_size; struct ib_grh grh; - u8 mad[0]; + u8 mad[]; } __packed; struct ib_rmpp_segment { struct list_head list; u32 num; - u8 data[0]; + u8 data[]; }; struct ib_mad_agent_private { diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c index cd338ddc4a39..9c2d8b7f1af9 100644 --- a/drivers/infiniband/core/multicast.c +++ b/drivers/infiniband/core/multicast.c @@ -71,7 +71,7 @@ struct mcast_device { struct ib_event_handler event_handler; int start_port; int end_port; - struct mcast_port port[0]; + struct mcast_port port[]; }; enum mcast_state { diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index 30d4c126a2db..74e0058fcf9e 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -101,7 +101,7 @@ struct ib_sa_port { struct ib_sa_device { int start_port, end_port; struct ib_event_handler event_handler; - struct ib_sa_port port[0]; + struct ib_sa_port port[]; }; struct ib_sa_query { diff --git a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h index 7d06b0f8d49a..e8e11bd95e42 100644 --- a/drivers/infiniband/hw/cxgb4/iw_cxgb4.h +++ b/drivers/infiniband/hw/cxgb4/iw_cxgb4.h @@ -707,7 +707,7 @@ struct mpa_message { u8 flags; u8 revision; __be16 private_data_size; - u8 private_data[0]; + u8 private_data[]; }; struct mpa_v2_conn_params { @@ -719,7 +719,7 @@ struct terminate_message { u8 layer_etype; u8 ecode; __be16 hdrct_rsvd; - u8 len_hdrs[0]; + u8 len_hdrs[]; }; #define TERM_MAX_LENGTH (sizeof(struct terminate_message) + 2 + 18 + 28) diff --git a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h index cbdb300a4794..a2f5e29ef226 100644 --- a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h +++ b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h @@ -123,7 +123,7 @@ struct fw_ri_dsgl { __be32 len0; __be64 addr0; #ifndef C99_NOT_SUPPORTED - struct fw_ri_dsge_pair sge[0]; + struct fw_ri_dsge_pair sge[]; #endif }; @@ -139,7 +139,7 @@ struct fw_ri_isgl { __be16 nsge; __be32 r2; #ifndef C99_NOT_SUPPORTED - struct fw_ri_sge sge[0]; + struct fw_ri_sge sge[]; #endif }; @@ -149,7 +149,7 @@ struct fw_ri_immd { __be16 r2; __be32 immdlen; #ifndef C99_NOT_SUPPORTED - __u8 data[0]; + __u8 data[]; #endif }; @@ -321,7 +321,7 @@ struct fw_ri_res_wr { __be32 len16_pkd; __u64 cookie; #ifndef C99_NOT_SUPPORTED - struct fw_ri_res res[0]; + struct fw_ri_res res[]; #endif }; diff --git a/drivers/infiniband/hw/hfi1/mad.c b/drivers/infiniband/hw/hfi1/mad.c index a51bcd2b4391..7073f237a949 100644 --- a/drivers/infiniband/hw/hfi1/mad.c +++ b/drivers/infiniband/hw/hfi1/mad.c @@ -2381,7 +2381,7 @@ struct opa_port_status_rsp { __be64 port_vl_rcv_bubble; __be64 port_vl_mark_fecn; __be64 port_vl_xmit_discards; - } vls[0]; /* real array size defined by # bits set in vl_select_mask */ + } vls[]; /* real array size defined by # bits set in vl_select_mask */ }; enum counter_selects { @@ -2423,7 +2423,7 @@ struct opa_aggregate { __be16 attr_id; __be16 err_reqlength; /* 1 bit, 8 res, 7 bit */ __be32 attr_mod; - u8 data[0]; + u8 data[]; }; #define MSK_LLI 0x000000f0 diff --git a/drivers/infiniband/hw/hfi1/mad.h b/drivers/infiniband/hw/hfi1/mad.h index 2f48e6953629..889e63d3f2cc 100644 --- a/drivers/infiniband/hw/hfi1/mad.h +++ b/drivers/infiniband/hw/hfi1/mad.h @@ -165,7 +165,7 @@ struct opa_mad_notice_attr { } __packed ntc_2048; }; - u8 class_data[0]; + u8 class_data[]; }; #define IB_VLARB_LOWPRI_0_31 1 diff --git a/drivers/infiniband/hw/hfi1/pio.h b/drivers/infiniband/hw/hfi1/pio.h index c9a58b642bdd..0102262343c0 100644 --- a/drivers/infiniband/hw/hfi1/pio.h +++ b/drivers/infiniband/hw/hfi1/pio.h @@ -243,7 +243,7 @@ struct sc_config_sizes { */ struct pio_map_elem { u32 mask; - struct send_context *ksc[0]; + struct send_context *ksc[]; }; /* @@ -263,7 +263,7 @@ struct pio_vl_map { u32 mask; u8 actual_vls; u8 vls; - struct pio_map_elem *map[0]; + struct pio_map_elem *map[]; }; int pio_map_init(struct hfi1_devdata *dd, u8 port, u8 num_vls, diff --git a/drivers/infiniband/hw/hfi1/sdma.c b/drivers/infiniband/hw/hfi1/sdma.c index a51525647ac8..c93ea021cf49 100644 --- a/drivers/infiniband/hw/hfi1/sdma.c +++ b/drivers/infiniband/hw/hfi1/sdma.c @@ -833,7 +833,7 @@ struct sdma_engine *sdma_select_engine_sc( struct sdma_rht_map_elem { u32 mask; u8 ctr; - struct sdma_engine *sde[0]; + struct sdma_engine *sde[]; }; struct sdma_rht_node { diff --git a/drivers/infiniband/hw/hfi1/sdma.h b/drivers/infiniband/hw/hfi1/sdma.h index 1e2e40f79cb2..7a851191f987 100644 --- a/drivers/infiniband/hw/hfi1/sdma.h +++ b/drivers/infiniband/hw/hfi1/sdma.h @@ -1002,7 +1002,7 @@ void sdma_engine_interrupt(struct sdma_engine *sde, u64 status); */ struct sdma_map_elem { u32 mask; - struct sdma_engine *sde[0]; + struct sdma_engine *sde[]; }; /** @@ -1024,7 +1024,7 @@ struct sdma_vl_map { u32 mask; u8 actual_vls; u8 vls; - struct sdma_map_elem *map[0]; + struct sdma_map_elem *map[]; }; int sdma_map_init( diff --git a/drivers/infiniband/hw/hfi1/user_exp_rcv.h b/drivers/infiniband/hw/hfi1/user_exp_rcv.h index 6257eee083a1..332abb446861 100644 --- a/drivers/infiniband/hw/hfi1/user_exp_rcv.h +++ b/drivers/infiniband/hw/hfi1/user_exp_rcv.h @@ -73,7 +73,7 @@ struct tid_rb_node { dma_addr_t dma_addr; bool freed; unsigned int npages; - struct page *pages[0]; + struct page *pages[]; }; static inline int num_user_pages(unsigned long addr, diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.h b/drivers/infiniband/hw/i40iw/i40iw_cm.h index 66dc1ba03389..6e43e4d730f4 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_cm.h +++ b/drivers/infiniband/hw/i40iw/i40iw_cm.h @@ -85,7 +85,7 @@ struct ietf_mpa_v1 { u8 flags; u8 rev; __be16 priv_data_len; - u8 priv_data[0]; + u8 priv_data[]; }; #define ietf_mpa_req_resp_frame ietf_mpa_frame @@ -101,7 +101,7 @@ struct ietf_mpa_v2 { u8 rev; __be16 priv_data_len; struct ietf_rtr_msg rtr_msg; - u8 priv_data[0]; + u8 priv_data[]; }; struct i40iw_cm_node; diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c index 78a48aea3faf..fa808582b08b 100644 --- a/drivers/infiniband/hw/mthca/mthca_memfree.c +++ b/drivers/infiniband/hw/mthca/mthca_memfree.c @@ -58,7 +58,7 @@ struct mthca_user_db_table { u64 uvirt; struct scatterlist mem; int refcount; - } page[0]; + } page[]; }; static void mthca_free_icm_pages(struct mthca_dev *dev, struct mthca_icm_chunk *chunk) diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.h b/drivers/infiniband/hw/mthca/mthca_memfree.h index da9b8f9b884f..f9a2e65e2ff5 100644 --- a/drivers/infiniband/hw/mthca/mthca_memfree.h +++ b/drivers/infiniband/hw/mthca/mthca_memfree.h @@ -68,7 +68,7 @@ struct mthca_icm_table { int lowmem; int coherent; struct mutex mutex; - struct mthca_icm *icm[0]; + struct mthca_icm *icm[]; }; struct mthca_icm_iter { diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.h b/drivers/infiniband/hw/usnic/usnic_uiom.h index 70be49b1ca05..7ec8991ace67 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.h +++ b/drivers/infiniband/hw/usnic/usnic_uiom.h @@ -77,7 +77,7 @@ struct usnic_uiom_reg { struct usnic_uiom_chunk { struct list_head list; int nents; - struct scatterlist page_list[0]; + struct scatterlist page_list[]; }; struct usnic_uiom_pd *usnic_uiom_alloc_pd(void); diff --git a/drivers/infiniband/sw/rxe/rxe_queue.h b/drivers/infiniband/sw/rxe/rxe_queue.h index acd0a925481c..8ef17d617022 100644 --- a/drivers/infiniband/sw/rxe/rxe_queue.h +++ b/drivers/infiniband/sw/rxe/rxe_queue.h @@ -63,7 +63,7 @@ struct rxe_queue_buf { __u32 pad_2[31]; __u32 consumer_index; __u32 pad_3[31]; - __u8 data[0]; + __u8 data[]; }; struct rxe_queue { diff --git a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h b/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h index 4480092c68e0..0b3570dc606d 100644 --- a/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h +++ b/drivers/infiniband/ulp/opa_vnic/opa_vnic_encap.h @@ -258,7 +258,7 @@ struct opa_veswport_mactable { __be16 offset; __be16 num_entries; __be32 mac_tbl_digest; - struct opa_veswport_mactable_entry tbl_entries[0]; + struct opa_veswport_mactable_entry tbl_entries[]; } __packed; /** @@ -440,7 +440,7 @@ struct opa_veswport_iface_macs { __be16 num_macs_in_msg; __be16 tot_macs_in_lst; __be16 gen_count; - struct opa_vnic_iface_mac_entry entry[0]; + struct opa_vnic_iface_mac_entry entry[]; } __packed; /** diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h index 5359ece561ca..6fabcc2faf1f 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.h +++ b/drivers/infiniband/ulp/srp/ib_srp.h @@ -309,7 +309,7 @@ struct srp_fr_pool { int max_page_list_len; spinlock_t lock; struct list_head free_list; - struct srp_fr_desc desc[0]; + struct srp_fr_desc desc[]; }; /** diff --git a/include/rdma/ib_fmr_pool.h b/include/rdma/ib_fmr_pool.h index f8982e4e9702..2fd9bfb6d648 100644 --- a/include/rdma/ib_fmr_pool.h +++ b/include/rdma/ib_fmr_pool.h @@ -73,7 +73,7 @@ struct ib_pool_fmr { int remap_count; u64 io_virtual_address; int page_list_len; - u64 page_list[0]; + u64 page_list[]; }; struct ib_fmr_pool *ib_create_fmr_pool(struct ib_pd *pd, diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 5f3a04ead9f5..bbc5cfb57cd2 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -1876,7 +1876,7 @@ struct ib_flow_eth_filter { __be16 ether_type; __be16 vlan_tag; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_eth { @@ -1890,7 +1890,7 @@ struct ib_flow_ib_filter { __be16 dlid; __u8 sl; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_ib { @@ -1915,7 +1915,7 @@ struct ib_flow_ipv4_filter { u8 ttl; u8 flags; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_ipv4 { @@ -1933,7 +1933,7 @@ struct ib_flow_ipv6_filter { u8 traffic_class; u8 hop_limit; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_ipv6 { @@ -1947,7 +1947,7 @@ struct ib_flow_tcp_udp_filter { __be16 dst_port; __be16 src_port; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_tcp_udp { @@ -1959,7 +1959,7 @@ struct ib_flow_spec_tcp_udp { struct ib_flow_tunnel_filter { __be32 tunnel_id; - u8 real_sz[0]; + u8 real_sz[]; }; /* ib_flow_spec_tunnel describes the Vxlan tunnel @@ -1976,7 +1976,7 @@ struct ib_flow_esp_filter { __be32 spi; __be32 seq; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_esp { @@ -1991,7 +1991,7 @@ struct ib_flow_gre_filter { __be16 protocol; __be32 key; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_gre { @@ -2004,7 +2004,7 @@ struct ib_flow_spec_gre { struct ib_flow_mpls_filter { __be32 tag; /* Must be last */ - u8 real_sz[0]; + u8 real_sz[]; }; struct ib_flow_spec_mpls { diff --git a/include/rdma/opa_vnic.h b/include/rdma/opa_vnic.h index 0c07a70bd7f6..e90b149fe92a 100644 --- a/include/rdma/opa_vnic.h +++ b/include/rdma/opa_vnic.h @@ -75,7 +75,7 @@ struct opa_vnic_rdma_netdev { struct rdma_netdev rn; /* keep this first */ /* followed by device private data */ - char *dev_priv[0]; + char *dev_priv[]; }; static inline void *opa_vnic_priv(const struct net_device *dev) diff --git a/include/rdma/rdmavt_mr.h b/include/rdma/rdmavt_mr.h index 72a3856d4057..ce6c888f7fe7 100644 --- a/include/rdma/rdmavt_mr.h +++ b/include/rdma/rdmavt_mr.h @@ -85,7 +85,7 @@ struct rvt_mregion { u8 lkey_published; /* in global table */ struct percpu_ref refcount; struct completion comp; /* complete when refcount goes to zero */ - struct rvt_segarray *map[0]; /* the segments */ + struct rvt_segarray *map[]; /* the segments */ }; #define RVT_MAX_LKEY_TABLE_BITS 23 diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index 0d5c70e2d8ab..5fc10108703a 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -191,7 +191,7 @@ struct rvt_swqe { u32 ssn; /* send sequence number */ u32 length; /* total length of data in sg_list */ void *priv; /* driver dependent field */ - struct rvt_sge sg_list[0]; + struct rvt_sge sg_list[]; }; /** -- cgit v1.2.3 From 458de8a97f107e1b6120d608b93ae9e3de019a2e Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Thu, 20 Feb 2020 09:41:55 +0200 Subject: net: page_pool: API cleanup and comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions starting with __ usually indicate those which are exported, but should not be called directly. Update some of those declared in the API and make it more readable. page_pool_unmap_page() and page_pool_release_page() were doing exactly the same thing calling __page_pool_clean_page(). Let's rename __page_pool_clean_page() to page_pool_release_page() and export it in order to show up on perf logs and get rid of page_pool_unmap_page(). Finally rename __page_pool_put_page() to page_pool_put_page() since we can now directly call it from drivers and rename the existing page_pool_put_page() to page_pool_put_full_page() since they do the same thing but the latter is trying to sync the full DMA area. This patch also updates netsec, mvneta and stmmac drivers which use those functions. Suggested-by: Jonathan Lemon Acked-by: Toke Høiland-Jørgensen Acked-by: Jesper Dangaard Brouer Signed-off-by: Ilias Apalodimas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvneta.c | 19 +++--- drivers/net/ethernet/socionext/netsec.c | 23 ++++---- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +- include/net/page_pool.h | 36 +++++------- net/core/page_pool.c | 70 ++++++++++++----------- net/core/xdp.c | 2 +- 6 files changed, 74 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 8e1feb678cea..1c391f63a26f 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1956,7 +1956,7 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp, if (!data || !(rx_desc->buf_phys_addr)) continue; - page_pool_put_page(rxq->page_pool, data, false); + page_pool_put_full_page(rxq->page_pool, data, false); } if (xdp_rxq_info_is_reg(&rxq->xdp_rxq)) xdp_rxq_info_unreg(&rxq->xdp_rxq); @@ -2154,9 +2154,9 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, err = xdp_do_redirect(pp->dev, xdp, prog); if (err) { ret = MVNETA_XDP_DROPPED; - __page_pool_put_page(rxq->page_pool, - virt_to_head_page(xdp->data), - len, true); + page_pool_put_page(rxq->page_pool, + virt_to_head_page(xdp->data), len, + true); } else { ret = MVNETA_XDP_REDIR; stats->xdp_redirect++; @@ -2166,9 +2166,9 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, case XDP_TX: ret = mvneta_xdp_xmit_back(pp, xdp); if (ret != MVNETA_XDP_TX) - __page_pool_put_page(rxq->page_pool, - virt_to_head_page(xdp->data), - len, true); + page_pool_put_page(rxq->page_pool, + virt_to_head_page(xdp->data), len, + true); break; default: bpf_warn_invalid_xdp_action(act); @@ -2177,9 +2177,8 @@ mvneta_run_xdp(struct mvneta_port *pp, struct mvneta_rx_queue *rxq, trace_xdp_exception(pp->dev, prog, act); /* fall through */ case XDP_DROP: - __page_pool_put_page(rxq->page_pool, - virt_to_head_page(xdp->data), - len, true); + page_pool_put_page(rxq->page_pool, + virt_to_head_page(xdp->data), len, true); ret = MVNETA_XDP_DROPPED; stats->xdp_drop++; break; diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index 6266926fe054..58b9b7ce7195 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -896,9 +896,9 @@ static u32 netsec_run_xdp(struct netsec_priv *priv, struct bpf_prog *prog, case XDP_TX: ret = netsec_xdp_xmit_back(priv, xdp); if (ret != NETSEC_XDP_TX) - __page_pool_put_page(dring->page_pool, - virt_to_head_page(xdp->data), - len, true); + page_pool_put_page(dring->page_pool, + virt_to_head_page(xdp->data), len, + true); break; case XDP_REDIRECT: err = xdp_do_redirect(priv->ndev, xdp, prog); @@ -906,9 +906,9 @@ static u32 netsec_run_xdp(struct netsec_priv *priv, struct bpf_prog *prog, ret = NETSEC_XDP_REDIR; } else { ret = NETSEC_XDP_CONSUMED; - __page_pool_put_page(dring->page_pool, - virt_to_head_page(xdp->data), - len, true); + page_pool_put_page(dring->page_pool, + virt_to_head_page(xdp->data), len, + true); } break; default: @@ -919,9 +919,8 @@ static u32 netsec_run_xdp(struct netsec_priv *priv, struct bpf_prog *prog, /* fall through -- handle aborts by dropping packet */ case XDP_DROP: ret = NETSEC_XDP_CONSUMED; - __page_pool_put_page(dring->page_pool, - virt_to_head_page(xdp->data), - len, true); + page_pool_put_page(dring->page_pool, + virt_to_head_page(xdp->data), len, true); break; } @@ -1020,8 +1019,8 @@ static int netsec_process_rx(struct netsec_priv *priv, int budget) * cache state. Since we paid the allocation cost if * building an skb fails try to put the page into cache */ - __page_pool_put_page(dring->page_pool, page, - pkt_len, true); + page_pool_put_page(dring->page_pool, page, pkt_len, + true); netif_err(priv, drv, priv->ndev, "rx failed to build skb\n"); break; @@ -1195,7 +1194,7 @@ static void netsec_uninit_pkt_dring(struct netsec_priv *priv, int id) if (id == NETSEC_RING_RX) { struct page *page = virt_to_page(desc->addr); - page_pool_put_page(dring->page_pool, page, false); + page_pool_put_full_page(dring->page_pool, page, false); } else if (id == NETSEC_RING_TX) { dma_unmap_single(priv->dev, desc->dma_addr, desc->len, DMA_TO_DEVICE); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 5836b21edd7e..37920b4da091 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1251,11 +1251,11 @@ static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (buf->page) - page_pool_put_page(rx_q->page_pool, buf->page, false); + page_pool_put_full_page(rx_q->page_pool, buf->page, false); buf->page = NULL; if (buf->sec_page) - page_pool_put_page(rx_q->page_pool, buf->sec_page, false); + page_pool_put_full_page(rx_q->page_pool, buf->sec_page, false); buf->sec_page = NULL; } diff --git a/include/net/page_pool.h b/include/net/page_pool.h index cfbed00ba7ee..81d7773f96cd 100644 --- a/include/net/page_pool.h +++ b/include/net/page_pool.h @@ -151,6 +151,7 @@ struct page_pool *page_pool_create(const struct page_pool_params *params); #ifdef CONFIG_PAGE_POOL void page_pool_destroy(struct page_pool *pool); void page_pool_use_xdp_mem(struct page_pool *pool, void (*disconnect)(void *)); +void page_pool_release_page(struct page_pool *pool, struct page *page); #else static inline void page_pool_destroy(struct page_pool *pool) { @@ -160,41 +161,32 @@ static inline void page_pool_use_xdp_mem(struct page_pool *pool, void (*disconnect)(void *)) { } +static inline void page_pool_release_page(struct page_pool *pool, + struct page *page) +{ +} #endif -/* Never call this directly, use helpers below */ -void __page_pool_put_page(struct page_pool *pool, struct page *page, - unsigned int dma_sync_size, bool allow_direct); +void page_pool_put_page(struct page_pool *pool, struct page *page, + unsigned int dma_sync_size, bool allow_direct); -static inline void page_pool_put_page(struct page_pool *pool, - struct page *page, bool allow_direct) +/* Same as above but will try to sync the entire area pool->max_len */ +static inline void page_pool_put_full_page(struct page_pool *pool, + struct page *page, bool allow_direct) { /* When page_pool isn't compiled-in, net/core/xdp.c doesn't * allow registering MEM_TYPE_PAGE_POOL, but shield linker. */ #ifdef CONFIG_PAGE_POOL - __page_pool_put_page(pool, page, -1, allow_direct); + page_pool_put_page(pool, page, -1, allow_direct); #endif } -/* Very limited use-cases allow recycle direct */ + +/* Same as above but the caller must guarantee safe context. e.g NAPI */ static inline void page_pool_recycle_direct(struct page_pool *pool, struct page *page) { - __page_pool_put_page(pool, page, -1, true); -} - -/* Disconnects a page (from a page_pool). API users can have a need - * to disconnect a page (from a page_pool), to allow it to be used as - * a regular page (that will eventually be returned to the normal - * page-allocator via put_page). - */ -void page_pool_unmap_page(struct page_pool *pool, struct page *page); -static inline void page_pool_release_page(struct page_pool *pool, - struct page *page) -{ -#ifdef CONFIG_PAGE_POOL - page_pool_unmap_page(pool, page); -#endif + page_pool_put_full_page(pool, page, true); } static inline dma_addr_t page_pool_get_dma_addr(struct page *page) diff --git a/net/core/page_pool.c b/net/core/page_pool.c index 10d2b255df5e..626db912fce4 100644 --- a/net/core/page_pool.c +++ b/net/core/page_pool.c @@ -96,7 +96,7 @@ struct page_pool *page_pool_create(const struct page_pool_params *params) } EXPORT_SYMBOL(page_pool_create); -static void __page_pool_return_page(struct page_pool *pool, struct page *page); +static void page_pool_return_page(struct page_pool *pool, struct page *page); noinline static struct page *page_pool_refill_alloc_cache(struct page_pool *pool) @@ -136,7 +136,7 @@ static struct page *page_pool_refill_alloc_cache(struct page_pool *pool) * (2) break out to fallthrough to alloc_pages_node. * This limit stress on page buddy alloactor. */ - __page_pool_return_page(pool, page); + page_pool_return_page(pool, page); page = NULL; break; } @@ -274,18 +274,25 @@ static s32 page_pool_inflight(struct page_pool *pool) return inflight; } -/* Cleanup page_pool state from page */ -static void __page_pool_clean_page(struct page_pool *pool, - struct page *page) +/* Disconnects a page (from a page_pool). API users can have a need + * to disconnect a page (from a page_pool), to allow it to be used as + * a regular page (that will eventually be returned to the normal + * page-allocator via put_page). + */ +void page_pool_release_page(struct page_pool *pool, struct page *page) { dma_addr_t dma; int count; if (!(pool->p.flags & PP_FLAG_DMA_MAP)) + /* Always account for inflight pages, even if we didn't + * map them + */ goto skip_dma_unmap; dma = page->dma_addr; - /* DMA unmap */ + + /* When page is unmapped, it cannot be returned our pool */ dma_unmap_page_attrs(pool->p.dev, dma, PAGE_SIZE << pool->p.order, pool->p.dma_dir, DMA_ATTR_SKIP_CPU_SYNC); @@ -297,21 +304,12 @@ skip_dma_unmap: count = atomic_inc_return(&pool->pages_state_release_cnt); trace_page_pool_state_release(pool, page, count); } - -/* unmap the page and clean our state */ -void page_pool_unmap_page(struct page_pool *pool, struct page *page) -{ - /* When page is unmapped, this implies page will not be - * returned to page_pool. - */ - __page_pool_clean_page(pool, page); -} -EXPORT_SYMBOL(page_pool_unmap_page); +EXPORT_SYMBOL(page_pool_release_page); /* Return a page to the page allocator, cleaning up our state */ -static void __page_pool_return_page(struct page_pool *pool, struct page *page) +static void page_pool_return_page(struct page_pool *pool, struct page *page) { - __page_pool_clean_page(pool, page); + page_pool_release_page(pool, page); put_page(page); /* An optimization would be to call __free_pages(page, pool->p.order) @@ -320,8 +318,7 @@ static void __page_pool_return_page(struct page_pool *pool, struct page *page) */ } -static bool __page_pool_recycle_into_ring(struct page_pool *pool, - struct page *page) +static bool page_pool_recycle_in_ring(struct page_pool *pool, struct page *page) { int ret; /* BH protection not needed if current is serving softirq */ @@ -338,7 +335,7 @@ static bool __page_pool_recycle_into_ring(struct page_pool *pool, * * Caller must provide appropriate safe context. */ -static bool __page_pool_recycle_direct(struct page *page, +static bool page_pool_recycle_in_cache(struct page *page, struct page_pool *pool) { if (unlikely(pool->alloc.count == PP_ALLOC_CACHE_SIZE)) @@ -357,8 +354,14 @@ static bool pool_page_reusable(struct page_pool *pool, struct page *page) return !page_is_pfmemalloc(page); } -void __page_pool_put_page(struct page_pool *pool, struct page *page, - unsigned int dma_sync_size, bool allow_direct) +/* If the page refcnt == 1, this will try to recycle the page. + * if PP_FLAG_DMA_SYNC_DEV is set, we'll try to sync the DMA area for + * the configured size min(dma_sync_size, pool->max_len). + * If the page refcnt != 1, then the page will be returned to memory + * subsystem. + */ +void page_pool_put_page(struct page_pool *pool, struct page *page, + unsigned int dma_sync_size, bool allow_direct) { /* This allocator is optimized for the XDP mode that uses * one-frame-per-page, but have fallbacks that act like the @@ -375,12 +378,12 @@ void __page_pool_put_page(struct page_pool *pool, struct page *page, dma_sync_size); if (allow_direct && in_serving_softirq()) - if (__page_pool_recycle_direct(page, pool)) + if (page_pool_recycle_in_cache(page, pool)) return; - if (!__page_pool_recycle_into_ring(pool, page)) { + if (!page_pool_recycle_in_ring(pool, page)) { /* Cache full, fallback to free pages */ - __page_pool_return_page(pool, page); + page_pool_return_page(pool, page); } return; } @@ -397,12 +400,13 @@ void __page_pool_put_page(struct page_pool *pool, struct page *page, * doing refcnt based recycle tricks, meaning another process * will be invoking put_page. */ - __page_pool_clean_page(pool, page); + /* Do not replace this with page_pool_return_page() */ + page_pool_release_page(pool, page); put_page(page); } -EXPORT_SYMBOL(__page_pool_put_page); +EXPORT_SYMBOL(page_pool_put_page); -static void __page_pool_empty_ring(struct page_pool *pool) +static void page_pool_empty_ring(struct page_pool *pool) { struct page *page; @@ -413,7 +417,7 @@ static void __page_pool_empty_ring(struct page_pool *pool) pr_crit("%s() page_pool refcnt %d violation\n", __func__, page_ref_count(page)); - __page_pool_return_page(pool, page); + page_pool_return_page(pool, page); } } @@ -443,7 +447,7 @@ static void page_pool_empty_alloc_cache_once(struct page_pool *pool) */ while (pool->alloc.count) { page = pool->alloc.cache[--pool->alloc.count]; - __page_pool_return_page(pool, page); + page_pool_return_page(pool, page); } } @@ -455,7 +459,7 @@ static void page_pool_scrub(struct page_pool *pool) /* No more consumers should exist, but producers could still * be in-flight. */ - __page_pool_empty_ring(pool); + page_pool_empty_ring(pool); } static int page_pool_release(struct page_pool *pool) @@ -529,7 +533,7 @@ void page_pool_update_nid(struct page_pool *pool, int new_nid) /* Flush pool alloc cache, as refill will check NUMA node */ while (pool->alloc.count) { page = pool->alloc.cache[--pool->alloc.count]; - __page_pool_return_page(pool, page); + page_pool_return_page(pool, page); } } EXPORT_SYMBOL(page_pool_update_nid); diff --git a/net/core/xdp.c b/net/core/xdp.c index 8310714c47fd..4c7ea85486af 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -372,7 +372,7 @@ static void __xdp_return(void *data, struct xdp_mem_info *mem, bool napi_direct, xa = rhashtable_lookup(mem_id_ht, &mem->id, mem_id_rht_params); page = virt_to_head_page(data); napi_direct &= !xdp_return_frame_no_direct(); - page_pool_put_page(xa->page_pool, page, napi_direct); + page_pool_put_full_page(xa->page_pool, page, napi_direct); rcu_read_unlock(); break; case MEM_TYPE_PAGE_SHARED: -- cgit v1.2.3 From 66630058e56b26b3a9cf2625e250a8c592dd0207 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sat, 8 Feb 2020 20:48:29 +0100 Subject: sched/rt: Provide migrate_disable/enable() inlines Code which solely needs to prevent migration of a task uses preempt_disable()/enable() pairs. This is the only reliable way to do so as setting the task affinity to a single CPU can be undone by a setaffinity operation from a different task/process. RT provides a seperate migrate_disable/enable() mechanism which does not disable preemption to achieve the semantic requirements of a (almost) fully preemptible kernel. As it is unclear from looking at a given code path whether the intention is to disable preemption or migration, introduce migrate_disable/enable() inline functions which can be used to annotate code which merely needs to disable migration. Map them to preempt_disable/enable() for now. The RT substitution will be provided later. Code which is annotated that way documents that it has no requirement to protect against reentrancy of a preempting task. Either this is not required at all or the call sites are already serialized by other means. Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Signed-off-by: Thomas Gleixner Cc: Peter Zijlstra Cc: Juri Lelli Cc: Vincent Guittot Cc: Dietmar Eggemann Cc: Steven Rostedt Cc: Ben Segall Cc: Mel Gorman Cc: Sebastian Andrzej Siewior Link: https://lore.kernel.org/r/878slclv1u.fsf@nanos.tec.linutronix.de --- include/linux/preempt.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'include') diff --git a/include/linux/preempt.h b/include/linux/preempt.h index bbb68dba37cc..bc3f1aecaa19 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -322,4 +322,34 @@ static inline void preempt_notifier_init(struct preempt_notifier *notifier, #endif +/** + * migrate_disable - Prevent migration of the current task + * + * Maps to preempt_disable() which also disables preemption. Use + * migrate_disable() to annotate that the intent is to prevent migration, + * but not necessarily preemption. + * + * Can be invoked nested like preempt_disable() and needs the corresponding + * number of migrate_enable() invocations. + */ +static __always_inline void migrate_disable(void) +{ + preempt_disable(); +} + +/** + * migrate_enable - Allow migration of the current task + * + * Counterpart to migrate_disable(). + * + * As migrate_disable() can be invoked nested, only the outermost invocation + * reenables migration. + * + * Currently mapped to preempt_enable(). + */ +static __always_inline void migrate_enable(void) +{ + preempt_enable(); +} + #endif /* __LINUX_PREEMPT_H */ -- cgit v1.2.3 From 4e139c7711633365ebb52fbb63905395522a8413 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 14 Feb 2020 14:39:19 +0100 Subject: sched: Provide cant_migrate() Some code pathes rely on preempt_disable() to prevent migration on a non RT enabled kernel. These preempt_disable/enable() pairs are substituted by migrate_disable/enable() pairs or other forms of RT specific protection. On RT these protections prevent migration but not preemption. Obviously a cant_sleep() check in such a section will trigger on RT because preemption is not disabled. Provide a cant_migrate() macro which maps to cant_sleep() on a non RT kernel and an empty placeholder for RT for now. The placeholder will be changed to a proper debug check along with the RT specific migration protection mechanism. Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/20200214161503.070487511@linutronix.de --- include/linux/kernel.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 0d9db2a14f44..9b7a8d74a9d6 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -257,6 +257,13 @@ extern void __cant_sleep(const char *file, int line, int preempt_offset); #define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0) +#ifndef CONFIG_PREEMPT_RT +# define cant_migrate() cant_sleep() +#else + /* Placeholder for now */ +# define cant_migrate() do { } while (0) +#endif + /** * abs - return absolute value of an argument * @x: the value. If it is unsigned type, it is converted to signed type first. -- cgit v1.2.3 From 90c018942c2babab73814648b37808fc3bf2ed1a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 7 Nov 2019 11:37:38 -0800 Subject: timer: Use hlist_unhashed_lockless() in timer_pending() The timer_pending() function is mostly used in lockless contexts, so Without proper annotations, KCSAN might detect a data-race [1]. Using hlist_unhashed_lockless() instead of hand-coding it seems appropriate (as suggested by Paul E. McKenney). [1] BUG: KCSAN: data-race in del_timer / detach_if_pending write to 0xffff88808697d870 of 8 bytes by task 10 on cpu 0: __hlist_del include/linux/list.h:764 [inline] detach_timer kernel/time/timer.c:815 [inline] detach_if_pending+0xcd/0x2d0 kernel/time/timer.c:832 try_to_del_timer_sync+0x60/0xb0 kernel/time/timer.c:1226 del_timer_sync+0x6b/0xa0 kernel/time/timer.c:1365 schedule_timeout+0x2d2/0x6e0 kernel/time/timer.c:1896 rcu_gp_fqs_loop+0x37c/0x580 kernel/rcu/tree.c:1639 rcu_gp_kthread+0x143/0x230 kernel/rcu/tree.c:1799 kthread+0x1d4/0x200 drivers/block/aoe/aoecmd.c:1253 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:352 read to 0xffff88808697d870 of 8 bytes by task 12060 on cpu 1: del_timer+0x3b/0xb0 kernel/time/timer.c:1198 sk_stop_timer+0x25/0x60 net/core/sock.c:2845 inet_csk_clear_xmit_timers+0x69/0xa0 net/ipv4/inet_connection_sock.c:523 tcp_clear_xmit_timers include/net/tcp.h:606 [inline] tcp_v4_destroy_sock+0xa3/0x3f0 net/ipv4/tcp_ipv4.c:2096 inet_csk_destroy_sock+0xf4/0x250 net/ipv4/inet_connection_sock.c:836 tcp_close+0x6f3/0x970 net/ipv4/tcp.c:2497 inet_release+0x86/0x100 net/ipv4/af_inet.c:427 __sock_release+0x85/0x160 net/socket.c:590 sock_close+0x24/0x30 net/socket.c:1268 __fput+0x1e1/0x520 fs/file_table.c:280 ____fput+0x1f/0x30 fs/file_table.c:313 task_work_run+0xf6/0x130 kernel/task_work.c:113 tracehook_notify_resume include/linux/tracehook.h:188 [inline] exit_to_usermode_loop+0x2b4/0x2c0 arch/x86/entry/common.c:163 Reported by Kernel Concurrency Sanitizer on: CPU: 1 PID: 12060 Comm: syz-executor.5 Not tainted 5.4.0-rc3+ #0 Hardware name: Google Google Compute Engine/Google Compute Engine, Signed-off-by: Eric Dumazet Cc: Thomas Gleixner [ paulmck: Pulled in Eric's later amendments. ] Signed-off-by: Paul E. McKenney --- include/linux/timer.h | 2 +- kernel/time/timer.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/timer.h b/include/linux/timer.h index 1e6650ed066d..0dc19a8c39c9 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -164,7 +164,7 @@ static inline void destroy_timer_on_stack(struct timer_list *timer) { } */ static inline int timer_pending(const struct timer_list * timer) { - return timer->entry.pprev != NULL; + return !hlist_unhashed_lockless(&timer->entry); } extern void add_timer_on(struct timer_list *timer, int cpu); diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 4820823515e9..568564ae3597 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -944,6 +944,7 @@ static struct timer_base *lock_timer_base(struct timer_list *timer, #define MOD_TIMER_PENDING_ONLY 0x01 #define MOD_TIMER_REDUCE 0x02 +#define MOD_TIMER_NOTPENDING 0x04 static inline int __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int options) @@ -960,7 +961,7 @@ __mod_timer(struct timer_list *timer, unsigned long expires, unsigned int option * the timer is re-modified to have the same timeout or ends up in the * same array bucket then just return: */ - if (timer_pending(timer)) { + if (!(options & MOD_TIMER_NOTPENDING) && timer_pending(timer)) { /* * The downside of this optimization is that it can result in * larger granularity than you would get from adding a new @@ -1133,7 +1134,7 @@ EXPORT_SYMBOL(timer_reduce); void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); - mod_timer(timer, timer->expires); + __mod_timer(timer, timer->expires, MOD_TIMER_NOTPENDING); } EXPORT_SYMBOL(add_timer); @@ -1891,7 +1892,7 @@ signed long __sched schedule_timeout(signed long timeout) timer.task = current; timer_setup_on_stack(&timer.timer, process_timeout, 0); - __mod_timer(&timer.timer, expire, 0); + __mod_timer(&timer.timer, expire, MOD_TIMER_NOTPENDING); schedule(); del_singleshot_timer_sync(&timer.timer); -- cgit v1.2.3 From 4dfd5cd83dc4458049c7f6eb9c4f361acc4239ea Mon Sep 17 00:00:00 2001 From: Amol Grover Date: Sat, 18 Jan 2020 22:24:18 +0530 Subject: rculist: Add brackets around cond argument in __list_check_rcu macro Passing a complex lockdep condition to __list_check_rcu results in false positive lockdep splat due to incorrect expression evaluation. For example, a lockdep check condition `cond1 || cond2` is evaluated as `!cond1 || cond2 && !rcu_read_lock_any_held()` which, according to operator precedence, evaluates to `!cond1 || (cond2 && !rcu_read_lock_any_held())`. This would result in a lockdep splat when cond1 is false and cond2 is true which is logically incorrect. Signed-off-by: Amol Grover Acked-by: Joel Fernandes (Google) Signed-off-by: Paul E. McKenney --- include/linux/rculist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rculist.h b/include/linux/rculist.h index 9f313e4999fe..8214cdc715f2 100644 --- a/include/linux/rculist.h +++ b/include/linux/rculist.h @@ -60,9 +60,9 @@ static inline void INIT_LIST_HEAD_RCU(struct list_head *list) #define __list_check_rcu(dummy, cond, extra...) \ ({ \ check_arg_count_one(extra); \ - RCU_LOCKDEP_WARN(!cond && !rcu_read_lock_any_held(), \ + RCU_LOCKDEP_WARN(!(cond) && !rcu_read_lock_any_held(), \ "RCU-list traversed in non-reader section!"); \ - }) + }) #else #define __list_check_rcu(dummy, cond, extra...) \ ({ check_arg_count_one(extra); }) -- cgit v1.2.3 From 613707929b304737e6eb841588772f1994f6702b Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Mon, 20 Jan 2020 15:42:26 +0100 Subject: rcu: Add a trace event for kfree_rcu() use of kfree_bulk() The event is given three parameters, first one is the name of RCU flavour, second one is the number of elements in array for free and last one is an address of the array holding pointers to be freed by the kfree_bulk() function. To enable the trace event your kernel has to be build with CONFIG_RCU_TRACE=y, after that it is possible to track the events using ftrace subsystem. Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Paul E. McKenney --- include/trace/events/rcu.h | 28 ++++++++++++++++++++++++++++ kernel/rcu/tree.c | 3 +++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 5e49b06e8104..49a49e68b916 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -623,6 +623,34 @@ TRACE_EVENT_RCU(rcu_invoke_kfree_callback, __entry->rcuname, __entry->rhp, __entry->offset) ); +/* + * Tracepoint for the invocation of a single RCU callback of the special + * kfree_bulk() form. The first argument is the RCU flavor, the second + * argument is a number of elements in array to free, the third is an + * address of the array holding nr_records entries. + */ +TRACE_EVENT_RCU(rcu_invoke_kfree_bulk_callback, + + TP_PROTO(const char *rcuname, unsigned long nr_records, void **p), + + TP_ARGS(rcuname, nr_records, p), + + TP_STRUCT__entry( + __field(const char *, rcuname) + __field(unsigned long, nr_records) + __field(void **, p) + ), + + TP_fast_assign( + __entry->rcuname = rcuname; + __entry->nr_records = nr_records; + __entry->p = p; + ), + + TP_printk("%s bulk=0x%p nr_records=%lu", + __entry->rcuname, __entry->p, __entry->nr_records) +); + /* * Tracepoint for exiting rcu_do_batch after RCU callbacks have been * invoked. The first argument is the name of the RCU flavor, diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 51a3aa884a7c..909f97efb1ed 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2792,6 +2792,9 @@ static void kfree_rcu_work(struct work_struct *work) debug_rcu_head_unqueue_bulk(bhead->head_free_debug); rcu_lock_acquire(&rcu_callback_map); + trace_rcu_invoke_kfree_bulk_callback(rcu_state.name, + bhead->nr_records, bhead->records); + kfree_bulk(bhead->nr_records, bhead->records); rcu_lock_release(&rcu_callback_map); -- cgit v1.2.3 From 59ee0326ccf712f9a637d5df2465a16a784cbfb0 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 28 Nov 2019 18:54:06 -0800 Subject: rcutorture: Suppress forward-progress complaints during early boot Some larger systems can take in excess of 50 seconds to complete their early boot initcalls prior to spawing init. This does not in any way help the forward-progress judgments of built-in rcutorture (when rcutorture is built as a module, the insmod or modprobe command normally cannot happen until some time after boot completes). This commit therefore suppresses such complaints until about the time that init is spawned. This also includes a fix to a stupid error located by kbuild test robot. [ paulmck: Apply kbuild test robot feedback. ] Signed-off-by: Paul E. McKenney [ paulmck: Fix to nohz_full slow-expediting recovery logic, per bpetkov. ] [ paulmck: Restrict splat to CONFIG_PREEMPT_RT=y kernels and simplify. ] Tested-by: Borislav Petkov --- include/linux/rcutiny.h | 1 + include/linux/rcutree.h | 1 + kernel/rcu/rcutorture.c | 3 ++- kernel/rcu/tree_exp.h | 7 ++++++- kernel/rcu/update.c | 12 ++++++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/rcutiny.h b/include/linux/rcutiny.h index b2b2dc990da9..045c28b71f4f 100644 --- a/include/linux/rcutiny.h +++ b/include/linux/rcutiny.h @@ -83,6 +83,7 @@ void rcu_scheduler_starting(void); static inline void rcu_scheduler_starting(void) { } #endif /* #else #ifndef CONFIG_SRCU */ static inline void rcu_end_inkernel_boot(void) { } +static inline bool rcu_inkernel_boot_has_ended(void) { return true; } static inline bool rcu_is_watching(void) { return true; } static inline void rcu_momentary_dyntick_idle(void) { } static inline void kfree_rcu_scheduler_running(void) { } diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 2f787b9029d1..45f3f66bb04d 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -54,6 +54,7 @@ void exit_rcu(void); void rcu_scheduler_starting(void); extern int rcu_scheduler_active __read_mostly; void rcu_end_inkernel_boot(void); +bool rcu_inkernel_boot_has_ended(void); bool rcu_is_watching(void); #ifndef CONFIG_PREEMPTION void rcu_all_qs(void); diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 1aeecc165b21..9ba49788cb48 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -1067,7 +1067,8 @@ rcu_torture_writer(void *arg) if (stutter_wait("rcu_torture_writer") && !READ_ONCE(rcu_fwd_cb_nodelay) && !cur_ops->slow_gps && - !torture_must_stop()) + !torture_must_stop() && + rcu_inkernel_boot_has_ended()) for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++) if (list_empty(&rcu_tortures[i].rtort_free) && rcu_access_pointer(rcu_torture_current) != diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index dcbd75791f39..a72d16eb869e 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -485,6 +485,7 @@ static bool synchronize_rcu_expedited_wait_once(long tlimit) static void synchronize_rcu_expedited_wait(void) { int cpu; + unsigned long j; unsigned long jiffies_stall; unsigned long jiffies_start; unsigned long mask; @@ -496,7 +497,7 @@ static void synchronize_rcu_expedited_wait(void) trace_rcu_exp_grace_period(rcu_state.name, rcu_exp_gp_seq_endval(), TPS("startwait")); jiffies_stall = rcu_jiffies_till_stall_check(); jiffies_start = jiffies; - if (IS_ENABLED(CONFIG_NO_HZ_FULL)) { + if (tick_nohz_full_enabled() && rcu_inkernel_boot_has_ended()) { if (synchronize_rcu_expedited_wait_once(1)) return; rcu_for_each_leaf_node(rnp) { @@ -508,6 +509,10 @@ static void synchronize_rcu_expedited_wait(void) tick_dep_set_cpu(cpu, TICK_DEP_BIT_RCU_EXP); } } + j = READ_ONCE(jiffies_till_first_fqs); + if (synchronize_rcu_expedited_wait_once(j + HZ)) + return; + WARN_ON_ONCE(IS_ENABLED(CONFIG_PREEMPT_RT)); } for (;;) { diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 6c4b862f57d6..feaaec5747a3 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -183,6 +183,8 @@ void rcu_unexpedite_gp(void) } EXPORT_SYMBOL_GPL(rcu_unexpedite_gp); +static bool rcu_boot_ended __read_mostly; + /* * Inform RCU of the end of the in-kernel boot sequence. */ @@ -191,7 +193,17 @@ void rcu_end_inkernel_boot(void) rcu_unexpedite_gp(); if (rcu_normal_after_boot) WRITE_ONCE(rcu_normal, 1); + rcu_boot_ended = 1; +} + +/* + * Let rcutorture know when it is OK to turn it up to eleven. + */ +bool rcu_inkernel_boot_has_ended(void) +{ + return rcu_boot_ended; } +EXPORT_SYMBOL_GPL(rcu_inkernel_boot_has_ended); #endif /* #ifndef CONFIG_TINY_RCU */ -- cgit v1.2.3 From 9ffc1d19fc4a6dfcfe06c91c2861ad6d44fdd92d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 30 Jan 2020 12:06:07 -0800 Subject: mm/memremap_pages: Introduce memremap_compat_align() The "sub-section memory hotplug" facility allows memremap_pages() users like libnvdimm to compensate for hardware platforms like x86 that have a section size larger than their hardware memory mapping granularity. The compensation that sub-section support affords is being tolerant of physical memory resources shifting by units smaller (64MiB on x86) than the memory-hotplug section size (128 MiB). Where the platform physical-memory mapping granularity is limited by the number and capability of address-decode-registers in the memory controller. While the sub-section support allows memremap_pages() to operate on sub-section (2MiB) granularity, the Power architecture may still require 16MiB alignment on "!radix_enabled()" platforms. In order for libnvdimm to be able to detect and manage this per-arch limitation, introduce memremap_compat_align() as a common minimum alignment across all driver-facing memory-mapping interfaces, and let Power override it to 16MiB in the "!radix_enabled()" case. The assumption / requirement for 16MiB to be a viable memremap_compat_align() value is that Power does not have platforms where its equivalent of address-decode-registers never hardware remaps a persistent memory resource on smaller than 16MiB boundaries. Note that I tried my best to not add a new Kconfig symbol, but header include entanglements defeated the #ifndef memremap_compat_align design pattern and the need to export it defeats the __weak design pattern for arch overrides. Based on an initial patch by Aneesh. Link: http://lore.kernel.org/r/CAPcyv4gBGNP95APYaBcsocEa50tQj9b5h__83vgngjq3ouGX_Q@mail.gmail.com Reported-by: Aneesh Kumar K.V Reported-by: Jeff Moyer Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Reviewed-by: Aneesh Kumar K.V Acked-by: Michael Ellerman (powerpc) Signed-off-by: Dan Williams --- arch/powerpc/Kconfig | 1 + arch/powerpc/mm/ioremap.c | 21 +++++++++++++++++++++ drivers/nvdimm/pfn_devs.c | 2 +- include/linux/memremap.h | 8 ++++++++ include/linux/mmzone.h | 1 + lib/Kconfig | 3 +++ mm/memremap.c | 23 +++++++++++++++++++++++ 7 files changed, 58 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 497b7d0b2d7e..e6ffe905e2b9 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -122,6 +122,7 @@ config PPC select ARCH_HAS_GCOV_PROFILE_ALL select ARCH_HAS_KCOV select ARCH_HAS_HUGEPD if HUGETLB_PAGE + select ARCH_HAS_MEMREMAP_COMPAT_ALIGN select ARCH_HAS_MMIOWB if PPC64 select ARCH_HAS_PHYS_TO_DMA select ARCH_HAS_PMEM_API diff --git a/arch/powerpc/mm/ioremap.c b/arch/powerpc/mm/ioremap.c index fc669643ce6a..b1a0aebe8c48 100644 --- a/arch/powerpc/mm/ioremap.c +++ b/arch/powerpc/mm/ioremap.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -97,3 +98,23 @@ void __iomem *do_ioremap(phys_addr_t pa, phys_addr_t offset, unsigned long size, return NULL; } + +#ifdef CONFIG_ZONE_DEVICE +/* + * Override the generic version in mm/memremap.c. + * + * With hash translation, the direct-map range is mapped with just one + * page size selected by htab_init_page_sizes(). Consult + * mmu_psize_defs[] to determine the minimum page size alignment. +*/ +unsigned long memremap_compat_align(void) +{ + unsigned int shift = mmu_psize_defs[mmu_linear_psize].shift; + + if (radix_enabled()) + return SUBSECTION_SIZE; + return max(SUBSECTION_SIZE, 1UL << shift); + +} +EXPORT_SYMBOL_GPL(memremap_compat_align); +#endif diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index b94f7a7e94b8..a5c25cb87116 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -750,7 +750,7 @@ static int nd_pfn_init(struct nd_pfn *nd_pfn) start = nsio->res.start; size = resource_size(&nsio->res); npfns = PHYS_PFN(size - SZ_8K); - align = max(nd_pfn->align, (1UL << SUBSECTION_SHIFT)); + align = max(nd_pfn->align, SUBSECTION_SIZE); end_trunc = start + size - ALIGN_DOWN(start + size, align); if (nd_pfn->mode == PFN_MODE_PMEM) { /* diff --git a/include/linux/memremap.h b/include/linux/memremap.h index 6fefb09af7c3..8af1cbd8f293 100644 --- a/include/linux/memremap.h +++ b/include/linux/memremap.h @@ -132,6 +132,7 @@ struct dev_pagemap *get_dev_pagemap(unsigned long pfn, unsigned long vmem_altmap_offset(struct vmem_altmap *altmap); void vmem_altmap_free(struct vmem_altmap *altmap, unsigned long nr_pfns); +unsigned long memremap_compat_align(void); #else static inline void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap) @@ -165,6 +166,12 @@ static inline void vmem_altmap_free(struct vmem_altmap *altmap, unsigned long nr_pfns) { } + +/* when memremap_pages() is disabled all archs can remap a single page */ +static inline unsigned long memremap_compat_align(void) +{ + return PAGE_SIZE; +} #endif /* CONFIG_ZONE_DEVICE */ static inline void put_dev_pagemap(struct dev_pagemap *pgmap) @@ -172,4 +179,5 @@ static inline void put_dev_pagemap(struct dev_pagemap *pgmap) if (pgmap) percpu_ref_put(pgmap->ref); } + #endif /* _LINUX_MEMREMAP_H_ */ diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 462f6873905a..6b77f7239af5 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -1170,6 +1170,7 @@ static inline unsigned long section_nr_to_pfn(unsigned long sec) #define SECTION_ALIGN_DOWN(pfn) ((pfn) & PAGE_SECTION_MASK) #define SUBSECTION_SHIFT 21 +#define SUBSECTION_SIZE (1UL << SUBSECTION_SHIFT) #define PFN_SUBSECTION_SHIFT (SUBSECTION_SHIFT - PAGE_SHIFT) #define PAGES_PER_SUBSECTION (1UL << PFN_SUBSECTION_SHIFT) diff --git a/lib/Kconfig b/lib/Kconfig index bc7e56370129..5d53f9609c25 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -615,6 +615,9 @@ config ARCH_HAS_PMEM_API config MEMREGION bool +config ARCH_HAS_MEMREMAP_COMPAT_ALIGN + bool + # use memcpy to implement user copies for nommu architectures config UACCESS_MEMCPY bool diff --git a/mm/memremap.c b/mm/memremap.c index 09b5b7adc773..3e7afaf05639 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,28 @@ static DEFINE_XARRAY(pgmap_array); +/* + * The memremap() and memremap_pages() interfaces are alternately used + * to map persistent memory namespaces. These interfaces place different + * constraints on the alignment and size of the mapping (namespace). + * memremap() can map individual PAGE_SIZE pages. memremap_pages() can + * only map subsections (2MB), and at least one architecture (PowerPC) + * the minimum mapping granularity of memremap_pages() is 16MB. + * + * The role of memremap_compat_align() is to communicate the minimum + * arch supported alignment of a namespace such that it can freely + * switch modes without violating the arch constraint. Namely, do not + * allow a namespace to be PAGE_SIZE aligned since that namespace may be + * reconfigured into a mode that requires SUBSECTION_SIZE alignment. + */ +#ifndef CONFIG_ARCH_HAS_MEMREMAP_COMPAT_ALIGN +unsigned long memremap_compat_align(void) +{ + return SUBSECTION_SIZE; +} +EXPORT_SYMBOL_GPL(memremap_compat_align); +#endif + #ifdef CONFIG_DEV_PAGEMAP_OPS DEFINE_STATIC_KEY_FALSE(devmap_managed_key); EXPORT_SYMBOL(devmap_managed_key); -- cgit v1.2.3 From 202853595e53f981c86656c49fc1cc1e3620f558 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Fri, 25 Oct 2019 15:00:45 -0400 Subject: PCI: pciehp: Disable in-band presence detect when possible The presence detect state (PDS) is normally a logical OR of in-band and out-of-band (OOB) presence detect. As of PCIe 4.0, there is the option to disable in-band presence so that the PDS bit always reflects the state of the out-of-band presence. The recommendation of the PCIe spec is to disable in-band presence whenever supported (PCIe r5.0, appendix I implementation note): Due to architectural issues, the in-band (Physical-Layer-based) portion of the PD mechanism is deprecated for use with async hot-plug. One issue is that in-band PD as architected does not detect adapter removal during certain LTSSM states, notably the L1 and Disabled States. Another issue is that when both in-band and OOB PD are being used together, the Presence Detect State bit and its associated interrupt mechanism always reflect the logical OR of the inband and OOB PD states, and with some hot-plug hardware configurations, it is important for software to detect and respond to in-band and OOB PD events independently. If OOB PD is being used and the associated DSP supports In-Band PD Disable, it is recommended that the In-Band PD Disable bit be Set, and the Presence Detect State bit and its associated interrupt mechanism be used exclusively for OOB PD. As a substitute for in-band PD with async hot-plug, the reference model uses either the DPC or the DLL Link Active mechanism. Link: https://lore.kernel.org/r/20191025190047.38130-2-stuart.w.hayes@gmail.com [bhelgaas: move PCI_EXP_SLTCAP2 read earlier & print PCI_EXP_SLTCAP2_IBPD value (suggested by Lukas)] Signed-off-by: Alexandru Gagniuc Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko Reviewed-by: Lukas Wunner --- drivers/pci/hotplug/pciehp.h | 1 + drivers/pci/hotplug/pciehp_hpc.c | 12 ++++++++++-- include/uapi/linux/pci_regs.h | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index aa61d4c219d7..ae44f46d1bf3 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -84,6 +84,7 @@ struct controller { struct pcie_device *pcie; u32 slot_cap; /* capabilities and quirks */ + unsigned int inband_presence_disabled:1; u16 slot_ctrl; /* control register access */ struct mutex ctrl_lock; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 8a2cb1764386..a573490289c3 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -848,7 +848,7 @@ static inline void dbg_ctrl(struct controller *ctrl) struct controller *pcie_init(struct pcie_device *dev) { struct controller *ctrl; - u32 slot_cap, link_cap; + u32 slot_cap, slot_cap2, link_cap; u8 poweron; struct pci_dev *pdev = dev->port; struct pci_bus *subordinate = pdev->subordinate; @@ -883,6 +883,13 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; up_read(&pci_bus_sem); + pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP2, &slot_cap2); + if (slot_cap2 & PCI_EXP_SLTCAP2_IBPD) { + pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_IBPD_DISABLE, + PCI_EXP_SLTCTL_IBPD_DISABLE); + ctrl->inband_presence_disabled = 1; + } + /* Check if Data Link Layer Link Active Reporting is implemented */ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); @@ -892,7 +899,7 @@ struct controller *pcie_init(struct pcie_device *dev) PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC); - ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c%s\n", + ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c IbPresDis%c LLActRep%c%s\n", (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), @@ -903,6 +910,7 @@ struct controller *pcie_init(struct pcie_device *dev) FLAG(slot_cap, PCI_EXP_SLTCAP_HPS), FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), + FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD), FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC), pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : ""); diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 5437690483cd..f9701410d3b5 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -605,6 +605,7 @@ #define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */ #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ +#define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */ #define PCI_EXP_SLTSTA 26 /* Slot Status */ #define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ #define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ @@ -680,6 +681,7 @@ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ +#define PCI_EXP_SLTCAP2_IBPD 0x00000001 /* In-band PD Disable Supported */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ #define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */ -- cgit v1.2.3 From dce05aa6eec977f1472abed95ccd71276b9a3864 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 19 Feb 2020 08:39:43 +0100 Subject: vt: selection, introduce vc_is_sel Avoid global variables (namely sel_cons) by introducing vc_is_sel. It checks whether the parameter is the current selection console. This will help putting sel_cons to a struct later. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20200219073951.16151-1-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/selection.c | 5 +++++ drivers/tty/vt/vt.c | 7 ++++--- drivers/tty/vt/vt_ioctl.c | 2 +- include/linux/selection.h | 4 +++- 4 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/selection.c b/drivers/tty/vt/selection.c index 78732feaf65b..f3ec0037a0e3 100644 --- a/drivers/tty/vt/selection.c +++ b/drivers/tty/vt/selection.c @@ -84,6 +84,11 @@ void clear_selection(void) } EXPORT_SYMBOL_GPL(clear_selection); +bool vc_is_sel(struct vc_data *vc) +{ + return vc == sel_cons; +} + /* * User settable table: what characters are to be considered alphabetic? * 128 bits. Locked by the console lock. diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 35d21cdb60d0..a2bbff602999 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -890,8 +890,9 @@ static void hide_softcursor(struct vc_data *vc) static void hide_cursor(struct vc_data *vc) { - if (vc == sel_cons) + if (vc_is_sel(vc)) clear_selection(); + vc->vc_sw->con_cursor(vc, CM_ERASE); hide_softcursor(vc); } @@ -901,7 +902,7 @@ static void set_cursor(struct vc_data *vc) if (!con_is_fg(vc) || console_blanked || vc->vc_mode == KD_GRAPHICS) return; if (vc->vc_deccm) { - if (vc == sel_cons) + if (vc_is_sel(vc)) clear_selection(); add_softcursor(vc); if ((vc->vc_cursor_type & 0x0f) != 1) @@ -1196,7 +1197,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, } } - if (vc == sel_cons) + if (vc_is_sel(vc)) clear_selection(); old_rows = vc->vc_rows; diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 8b0ed139592f..dc8c5a34f8eb 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -43,7 +43,7 @@ char vt_dont_switch; extern struct tty_driver *console_driver; #define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count) -#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_cons[i].d == sel_cons) +#define VT_BUSY(i) (VT_IS_IN_USE(i) || i == fg_console || vc_is_sel(vc_cons[i].d)) /* * Console (vt and kd) routines, as defined by USL SVR4 manual, and by diff --git a/include/linux/selection.h b/include/linux/selection.h index e2c1f96bf059..5b890ef5b59f 100644 --- a/include/linux/selection.h +++ b/include/linux/selection.h @@ -11,8 +11,8 @@ #include #include -extern struct vc_data *sel_cons; struct tty_struct; +struct vc_data; extern void clear_selection(void); extern int set_selection_user(const struct tiocl_selection __user *sel, @@ -24,6 +24,8 @@ extern int sel_loadlut(char __user *p); extern int mouse_reporting(void); extern void mouse_report(struct tty_struct * tty, int butt, int mrx, int mry); +bool vc_is_sel(struct vc_data *vc); + extern int console_blanked; extern const unsigned char color_table[]; -- cgit v1.2.3 From bd56e593da19de22284951c33ce5c419258171bf Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Fri, 21 Feb 2020 16:36:05 +0100 Subject: ASoC: meson: g12a: add toacodec dt-binding documentation Add the DT bindings and documentation of the internal audio DAC glue found on Amlogic g12a and sm1 SoC families Signed-off-by: Jerome Brunet Link: https://lore.kernel.org/r/20200221153607.1585499-2-jbrunet@baylibre.com Signed-off-by: Mark Brown --- .../bindings/sound/amlogic,g12a-toacodec.yaml | 51 ++++++++++++++++++++++ include/dt-bindings/sound/meson-g12a-toacodec.h | 10 +++++ 2 files changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml create mode 100644 include/dt-bindings/sound/meson-g12a-toacodec.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml new file mode 100644 index 000000000000..f778d3371fde --- /dev/null +++ b/Documentation/devicetree/bindings/sound/amlogic,g12a-toacodec.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/amlogic,g12a-toacodec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Amlogic G12a Internal DAC Control Glue + +maintainers: + - Jerome Brunet + +properties: + $nodename: + pattern: "^audio-controller@.*" + + "#sound-dai-cells": + const: 1 + + compatible: + oneOf: + - items: + - const: + amlogic,g12a-toacodec + - items: + - enum: + - amlogic,sm1-toacodec + - const: + amlogic,g12a-toacodec + + reg: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - "#sound-dai-cells" + - compatible + - reg + - resets + +examples: + - | + #include + + toacodec: audio-controller@740 { + compatible = "amlogic,g12a-toacodec"; + reg = <0x0 0x740 0x0 0x4>; + #sound-dai-cells = <1>; + resets = <&clkc_audio AUD_RESET_TOACODEC>; + }; diff --git a/include/dt-bindings/sound/meson-g12a-toacodec.h b/include/dt-bindings/sound/meson-g12a-toacodec.h new file mode 100644 index 000000000000..69d7a75592a2 --- /dev/null +++ b/include/dt-bindings/sound/meson-g12a-toacodec.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __DT_MESON_G12A_TOACODEC_H +#define __DT_MESON_G12A_TOACODEC_H + +#define TOACODEC_IN_A 0 +#define TOACODEC_IN_B 1 +#define TOACODEC_IN_C 2 +#define TOACODEC_OUT 3 + +#endif /* __DT_MESON_G12A_TOACODEC_H */ -- cgit v1.2.3 From 3e27a33932df104f4f9ff811467b0b4ccebde773 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 21 Feb 2020 15:43:42 +0800 Subject: security: remove duplicated include from security.h Remove duplicated include. Signed-off-by: YueHaibing Signed-off-by: James Morris --- include/linux/security.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 9df7547afc0c..896da4429c17 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -30,7 +30,6 @@ #include #include #include -#include struct linux_binprm; struct cred; -- cgit v1.2.3 From 7bb106eb689101dc56cc205d5fb4ecf5c0554348 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 14 Feb 2020 23:23:38 +0100 Subject: cfg80211: remove support for adjacent channel compensation The only driver that used that was iwlwifi and it removed support for this. Remove the feature here as well. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20200214232336.a530de38e511.I393bc395f6037c8cca6421ed550e3072dc248aed@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 7 ------- net/mac80211/scan.c | 3 +-- net/wireless/scan.c | 6 ++---- 3 files changed, 3 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7709b92f5b6b..7ea23caa7301 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4577,12 +4577,6 @@ struct wiphy_iftype_akm_suites { * and probe responses. This value should be set if the driver * wishes to limit the number of csa counters. Default (0) means * infinite. - * @max_adj_channel_rssi_comp: max offset of between the channel on which the - * frame was sent and the channel on which the frame was heard for which - * the reported rssi is still valid. If a driver is able to compensate the - * low rssi when a frame is heard on different channel, then it should set - * this variable to the maximal offset for which it can compensate. - * This value should be set in MHz. * @bss_select_support: bitmask indicating the BSS selection criteria supported * by the driver in the .connect() callback. The bit position maps to the * attribute indices defined in &enum nl80211_bss_select_attr. @@ -4734,7 +4728,6 @@ struct wiphy { u16 max_ap_assoc_sta; u8 max_num_csa_counters; - u8 max_adj_channel_rssi_comp; u32 bss_select_support; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 4d31d9688dc2..fdac8192a519 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -201,8 +201,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, mgmt->bssid, cbss->bssid); /* In case the signal is invalid update the status */ - signal_valid = abs(channel->center_freq - cbss->channel->center_freq) - <= local->hw.wiphy->max_adj_channel_rssi_comp; + signal_valid = channel == cbss->channel; if (!signal_valid) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7f1af8f347b1..dd41e41f9d26 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1433,8 +1433,7 @@ cfg80211_inform_single_bss_data(struct wiphy *wiphy, } rcu_assign_pointer(tmp.pub.ies, ies); - signal_valid = abs(data->chan->center_freq - channel->center_freq) <= - wiphy->max_adj_channel_rssi_comp; + signal_valid = data->chan == channel; res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid, ts); if (!res) return NULL; @@ -1851,8 +1850,7 @@ cfg80211_inform_single_bss_frame_data(struct wiphy *wiphy, memcpy(tmp.pub.chain_signal, data->chain_signal, IEEE80211_MAX_CHAINS); ether_addr_copy(tmp.parent_bssid, data->parent_bssid); - signal_valid = abs(data->chan->center_freq - channel->center_freq) <= - wiphy->max_adj_channel_rssi_comp; + signal_valid = data->chan == channel; res = cfg80211_bss_update(wiphy_to_rdev(wiphy), &tmp, signal_valid, jiffies); if (!res) -- cgit v1.2.3 From b8e202d1d1d0f182f01062804efb523ea9a9008c Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 18 Feb 2020 17:10:13 +0000 Subject: net, sk_msg: Annotate lockless access to sk_prot on clone sk_msg and ULP frameworks override protocol callbacks pointer in sk->sk_prot, while tcp accesses it locklessly when cloning the listening socket, that is with neither sk_lock nor sk_callback_lock held. Once we enable use of listening sockets with sockmap (and hence sk_msg), there will be shared access to sk->sk_prot if socket is getting cloned while being inserted/deleted to/from the sockmap from another CPU: Read side: tcp_v4_rcv sk = __inet_lookup_skb(...) tcp_check_req(sk) inet_csk(sk)->icsk_af_ops->syn_recv_sock tcp_v4_syn_recv_sock tcp_create_openreq_child inet_csk_clone_lock sk_clone_lock READ_ONCE(sk->sk_prot) Write side: sock_map_ops->map_update_elem sock_map_update_elem sock_map_update_common sock_map_link_no_progs tcp_bpf_init tcp_bpf_update_sk_prot sk_psock_update_proto WRITE_ONCE(sk->sk_prot, ops) sock_map_ops->map_delete_elem sock_map_delete_elem __sock_map_delete sock_map_unref sk_psock_put sk_psock_drop sk_psock_restore_proto tcp_update_ulp WRITE_ONCE(sk->sk_prot, proto) Mark the shared access with READ_ONCE/WRITE_ONCE annotations. Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20200218171023.844439-2-jakub@cloudflare.com --- include/linux/skmsg.h | 3 ++- net/core/sock.c | 8 +++++--- net/ipv4/tcp_bpf.c | 4 +++- net/ipv4/tcp_ulp.c | 3 ++- net/tls/tls_main.c | 3 ++- 5 files changed, 14 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index d90ef61712a1..112765bd146d 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -352,7 +352,8 @@ static inline void sk_psock_update_proto(struct sock *sk, psock->saved_write_space = sk->sk_write_space; psock->sk_proto = sk->sk_prot; - sk->sk_prot = ops; + /* Pairs with lockless read in sk_clone_lock() */ + WRITE_ONCE(sk->sk_prot, ops); } static inline void sk_psock_restore_proto(struct sock *sk, diff --git a/net/core/sock.c b/net/core/sock.c index a4c8fac781ff..bf1173b93eda 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1572,13 +1572,14 @@ static inline void sock_lock_init(struct sock *sk) */ static void sock_copy(struct sock *nsk, const struct sock *osk) { + const struct proto *prot = READ_ONCE(osk->sk_prot); #ifdef CONFIG_SECURITY_NETWORK void *sptr = nsk->sk_security; #endif memcpy(nsk, osk, offsetof(struct sock, sk_dontcopy_begin)); memcpy(&nsk->sk_dontcopy_end, &osk->sk_dontcopy_end, - osk->sk_prot->obj_size - offsetof(struct sock, sk_dontcopy_end)); + prot->obj_size - offsetof(struct sock, sk_dontcopy_end)); #ifdef CONFIG_SECURITY_NETWORK nsk->sk_security = sptr; @@ -1792,16 +1793,17 @@ static void sk_init_common(struct sock *sk) */ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) { + struct proto *prot = READ_ONCE(sk->sk_prot); struct sock *newsk; bool is_charged = true; - newsk = sk_prot_alloc(sk->sk_prot, priority, sk->sk_family); + newsk = sk_prot_alloc(prot, priority, sk->sk_family); if (newsk != NULL) { struct sk_filter *filter; sock_copy(newsk, sk); - newsk->sk_prot_creator = sk->sk_prot; + newsk->sk_prot_creator = prot; /* SANITY */ if (likely(newsk->sk_net_refcnt)) diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 8a01428f80c1..dd183b050642 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -645,8 +645,10 @@ static void tcp_bpf_reinit_sk_prot(struct sock *sk, struct sk_psock *psock) /* Reinit occurs when program types change e.g. TCP_BPF_TX is removed * or added requiring sk_prot hook updates. We keep original saved * hooks in this case. + * + * Pairs with lockless read in sk_clone_lock(). */ - sk->sk_prot = &tcp_bpf_prots[family][config]; + WRITE_ONCE(sk->sk_prot, &tcp_bpf_prots[family][config]); } static int tcp_bpf_assert_proto_ops(struct proto *ops) diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c index 38d3ad141161..6c43fa189195 100644 --- a/net/ipv4/tcp_ulp.c +++ b/net/ipv4/tcp_ulp.c @@ -106,7 +106,8 @@ void tcp_update_ulp(struct sock *sk, struct proto *proto, if (!icsk->icsk_ulp_ops) { sk->sk_write_space = write_space; - sk->sk_prot = proto; + /* Pairs with lockless read in sk_clone_lock() */ + WRITE_ONCE(sk->sk_prot, proto); return; } diff --git a/net/tls/tls_main.c b/net/tls/tls_main.c index 94774c0e5ff3..82225bcc1117 100644 --- a/net/tls/tls_main.c +++ b/net/tls/tls_main.c @@ -742,7 +742,8 @@ static void tls_update(struct sock *sk, struct proto *p, ctx->sk_write_space = write_space; ctx->sk_proto = p; } else { - sk->sk_prot = p; + /* Pairs with lockless read in sk_clone_lock(). */ + WRITE_ONCE(sk->sk_prot, p); sk->sk_write_space = write_space; } } -- cgit v1.2.3 From f1ff5ce2cd5ef3335f19c0f6576582c87045b04f Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 18 Feb 2020 17:10:14 +0000 Subject: net, sk_msg: Clear sk_user_data pointer on clone if tagged sk_user_data can hold a pointer to an object that is not intended to be shared between the parent socket and the child that gets a pointer copy on clone. This is the case when sk_user_data points at reference-counted object, like struct sk_psock. One way to resolve it is to tag the pointer with a no-copy flag by repurposing its lowest bit. Based on the bit-flag value we clear the child sk_user_data pointer after cloning the parent socket. The no-copy flag is stored in the pointer itself as opposed to externally, say in socket flags, to guarantee that the pointer and the flag are copied from parent to child socket in an atomic fashion. Parent socket state is subject to change while copying, we don't hold any locks at that time. This approach relies on an assumption that sk_user_data holds a pointer to an object aligned at least 2 bytes. A manual audit of existing users of rcu_dereference_sk_user_data helper confirms our assumption. Also, an RCU-protected sk_user_data is not likely to hold a pointer to a char value or a pathological case of "struct { char c; }". To be safe, warn when the flag-bit is set when setting sk_user_data to catch any future misuses. It is worth considering why clearing sk_user_data unconditionally is not an option. There exist users, DRBD, NVMe, and Xen drivers being among them, that rely on the pointer being copied when cloning the listening socket. Potentially we could distinguish these users by checking if the listening socket has been created in kernel-space via sock_create_kern, and hence has sk_kern_sock flag set. However, this is not the case for NVMe and Xen drivers, which create sockets without marking them as belonging to the kernel. Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20200218171023.844439-3-jakub@cloudflare.com --- include/net/sock.h | 37 +++++++++++++++++++++++++++++++++++-- net/core/skmsg.c | 2 +- net/core/sock.c | 6 ++++++ 3 files changed, 42 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 02162b0378f7..9f37fdfd15d4 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -502,10 +502,43 @@ enum sk_pacing { SK_PACING_FQ = 2, }; +/* Pointer stored in sk_user_data might not be suitable for copying + * when cloning the socket. For instance, it can point to a reference + * counted object. sk_user_data bottom bit is set if pointer must not + * be copied. + */ +#define SK_USER_DATA_NOCOPY 1UL +#define SK_USER_DATA_PTRMASK ~(SK_USER_DATA_NOCOPY) + +/** + * sk_user_data_is_nocopy - Test if sk_user_data pointer must not be copied + * @sk: socket + */ +static inline bool sk_user_data_is_nocopy(const struct sock *sk) +{ + return ((uintptr_t)sk->sk_user_data & SK_USER_DATA_NOCOPY); +} + #define __sk_user_data(sk) ((*((void __rcu **)&(sk)->sk_user_data))) -#define rcu_dereference_sk_user_data(sk) rcu_dereference(__sk_user_data((sk))) -#define rcu_assign_sk_user_data(sk, ptr) rcu_assign_pointer(__sk_user_data((sk)), ptr) +#define rcu_dereference_sk_user_data(sk) \ +({ \ + void *__tmp = rcu_dereference(__sk_user_data((sk))); \ + (void *)((uintptr_t)__tmp & SK_USER_DATA_PTRMASK); \ +}) +#define rcu_assign_sk_user_data(sk, ptr) \ +({ \ + uintptr_t __tmp = (uintptr_t)(ptr); \ + WARN_ON_ONCE(__tmp & ~SK_USER_DATA_PTRMASK); \ + rcu_assign_pointer(__sk_user_data((sk)), __tmp); \ +}) +#define rcu_assign_sk_user_data_nocopy(sk, ptr) \ +({ \ + uintptr_t __tmp = (uintptr_t)(ptr); \ + WARN_ON_ONCE(__tmp & ~SK_USER_DATA_PTRMASK); \ + rcu_assign_pointer(__sk_user_data((sk)), \ + __tmp | SK_USER_DATA_NOCOPY); \ +}) /* * SK_CAN_REUSE and SK_NO_REUSE on a socket mean that the socket is OK diff --git a/net/core/skmsg.c b/net/core/skmsg.c index ded2d5227678..eeb28cb85664 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -512,7 +512,7 @@ struct sk_psock *sk_psock_init(struct sock *sk, int node) sk_psock_set_state(psock, SK_PSOCK_TX_ENABLED); refcount_set(&psock->refcnt, 1); - rcu_assign_sk_user_data(sk, psock); + rcu_assign_sk_user_data_nocopy(sk, psock); sock_hold(sk); return psock; diff --git a/net/core/sock.c b/net/core/sock.c index bf1173b93eda..e4af4dbc1c9e 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1865,6 +1865,12 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) goto out; } + /* Clear sk_user_data if parent had the pointer tagged + * as not suitable for copying when cloning. + */ + if (sk_user_data_is_nocopy(newsk)) + RCU_INIT_POINTER(newsk->sk_user_data, NULL); + newsk->sk_err = 0; newsk->sk_err_soft = 0; newsk->sk_priority = 0; -- cgit v1.2.3 From e80251555f0befd1271e74b080bccf0ff0348bfc Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 18 Feb 2020 17:10:15 +0000 Subject: tcp_bpf: Don't let child socket inherit parent protocol ops on copy Prepare for cloning listening sockets that have their protocol callbacks overridden by sk_msg. Child sockets must not inherit parent callbacks that access state stored in sk_user_data owned by the parent. Restore the child socket protocol callbacks before it gets hashed and any of the callbacks can get invoked. Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200218171023.844439-4-jakub@cloudflare.com --- include/net/tcp.h | 7 +++++++ net/ipv4/tcp_bpf.c | 14 ++++++++++++++ net/ipv4/tcp_minisocks.c | 2 ++ 3 files changed, 23 insertions(+) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index a5ea27df3c2b..07f947cc80e6 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2203,6 +2203,13 @@ int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); int __tcp_bpf_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg, int len, int flags); +#ifdef CONFIG_NET_SOCK_MSG +void tcp_bpf_clone(const struct sock *sk, struct sock *newsk); +#else +static inline void tcp_bpf_clone(const struct sock *sk, struct sock *newsk) +{ +} +#endif /* Call BPF_SOCK_OPS program that returns an int. If the return value * is < 0, then the BPF op failed (for example if the loaded BPF diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index dd183b050642..7d6e1b75d4d4 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -693,3 +693,17 @@ int tcp_bpf_init(struct sock *sk) rcu_read_unlock(); return 0; } + +/* If a child got cloned from a listening socket that had tcp_bpf + * protocol callbacks installed, we need to restore the callbacks to + * the default ones because the child does not inherit the psock state + * that tcp_bpf callbacks expect. + */ +void tcp_bpf_clone(const struct sock *sk, struct sock *newsk) +{ + int family = sk->sk_family == AF_INET6 ? TCP_BPF_IPV6 : TCP_BPF_IPV4; + struct proto *prot = newsk->sk_prot; + + if (prot == &tcp_bpf_prots[family][TCP_BPF_BASE]) + newsk->sk_prot = sk->sk_prot_creator; +} diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index ad3b56d9fa71..c8274371c3d0 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -548,6 +548,8 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->fastopen_req = NULL; RCU_INIT_POINTER(newtp->fastopen_rsk, NULL); + tcp_bpf_clone(sk, newsk); + __TCP_INC_STATS(sock_net(sk), TCP_MIB_PASSIVEOPENS); return newsk; -- cgit v1.2.3 From 035ff358f2d9e2f5e1639ba4defe4dc40ac642dd Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Tue, 18 Feb 2020 17:10:21 +0000 Subject: net: Generate reuseport group ID on group creation Commit 736b46027eb4 ("net: Add ID (if needed) to sock_reuseport and expose reuseport_lock") has introduced lazy generation of reuseport group IDs that survive group resize. By comparing the identifier we check if BPF reuseport program is not trying to select a socket from a BPF map that belongs to a different reuseport group than the one the packet is for. Because SOCKARRAY used to be the only BPF map type that can be used with reuseport BPF, it was possible to delay the generation of reuseport group ID until a socket from the group was inserted into BPF map for the first time. Now that SOCK{MAP,HASH} can be used with reuseport BPF we have two options, either generate the reuseport ID on map update, like SOCKARRAY does, or allocate an ID from the start when reuseport group gets created. This patch takes the latter approach to keep sockmap free of calls into reuseport code. This streamlines the reuseport_id access as its lifetime now matches the longevity of reuseport object. The cost of this simplification, however, is that we allocate reuseport IDs for all SO_REUSEPORT users. Even those that don't use SOCKARRAY in their setups. With the way identifiers are currently generated, we can have at most S32_MAX reuseport groups, which hopefully is sufficient. If we ever get close to the limit, we can switch an u64 counter like sk_cookie. Another change is that we now always call into SOCKARRAY logic to unlink the socket from the map when unhashing or closing the socket. Previously we did it only when at least one socket from the group was in a BPF map. It is worth noting that this doesn't conflict with sockmap tear-down in case a socket is in a SOCK{MAP,HASH} and belongs to a reuseport group. sockmap tear-down happens first: prot->unhash `- tcp_bpf_unhash |- tcp_bpf_remove | `- while (sk_psock_link_pop(psock)) | `- sk_psock_unlink | `- sock_map_delete_from_link | `- __sock_map_delete | `- sock_map_unref | `- sk_psock_put | `- sk_psock_drop | `- rcu_assign_sk_user_data(sk, NULL) `- inet_unhash `- reuseport_detach_sock `- bpf_sk_reuseport_detach `- WRITE_ONCE(sk->sk_user_data, NULL) Suggested-by: Martin Lau Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20200218171023.844439-10-jakub@cloudflare.com --- include/net/sock_reuseport.h | 2 -- kernel/bpf/reuseport_array.c | 5 ----- net/core/filter.c | 12 +---------- net/core/sock_reuseport.c | 50 +++++++++++++++++++------------------------- 4 files changed, 22 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/net/sock_reuseport.h b/include/net/sock_reuseport.h index 43f4a818d88f..3ecaa15d1850 100644 --- a/include/net/sock_reuseport.h +++ b/include/net/sock_reuseport.h @@ -55,6 +55,4 @@ static inline bool reuseport_has_conns(struct sock *sk, bool set) return ret; } -int reuseport_get_id(struct sock_reuseport *reuse); - #endif /* _SOCK_REUSEPORT_H */ diff --git a/kernel/bpf/reuseport_array.c b/kernel/bpf/reuseport_array.c index 50c083ba978c..01badd3eda7a 100644 --- a/kernel/bpf/reuseport_array.c +++ b/kernel/bpf/reuseport_array.c @@ -305,11 +305,6 @@ int bpf_fd_reuseport_array_update_elem(struct bpf_map *map, void *key, if (err) goto put_file_unlock; - /* Ensure reuse->reuseport_id is set */ - err = reuseport_get_id(reuse); - if (err < 0) - goto put_file_unlock; - WRITE_ONCE(nsk->sk_user_data, &array->ptrs[index]); rcu_assign_pointer(array->ptrs[index], nsk); free_osk = osk; diff --git a/net/core/filter.c b/net/core/filter.c index 77d2f471b3bb..925b23de218b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -8641,18 +8641,8 @@ BPF_CALL_4(sk_select_reuseport, struct sk_reuseport_kern *, reuse_kern, } if (unlikely(reuse->reuseport_id != reuse_kern->reuseport_id)) { - struct sock *sk; - - if (unlikely(!reuse_kern->reuseport_id)) - /* There is a small race between adding the - * sk to the map and setting the - * reuse_kern->reuseport_id. - * Treat it as the sk has not been added to - * the bpf map yet. - */ - return -ENOENT; + struct sock *sk = reuse_kern->sk; - sk = reuse_kern->sk; if (sk->sk_protocol != selected_sk->sk_protocol) return -EPROTOTYPE; else if (sk->sk_family != selected_sk->sk_family) diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c index 91e9f2223c39..adcb3aea576d 100644 --- a/net/core/sock_reuseport.c +++ b/net/core/sock_reuseport.c @@ -16,27 +16,8 @@ DEFINE_SPINLOCK(reuseport_lock); -#define REUSEPORT_MIN_ID 1 static DEFINE_IDA(reuseport_ida); -int reuseport_get_id(struct sock_reuseport *reuse) -{ - int id; - - if (reuse->reuseport_id) - return reuse->reuseport_id; - - id = ida_simple_get(&reuseport_ida, REUSEPORT_MIN_ID, 0, - /* Called under reuseport_lock */ - GFP_ATOMIC); - if (id < 0) - return id; - - reuse->reuseport_id = id; - - return reuse->reuseport_id; -} - static struct sock_reuseport *__reuseport_alloc(unsigned int max_socks) { unsigned int size = sizeof(struct sock_reuseport) + @@ -55,6 +36,7 @@ static struct sock_reuseport *__reuseport_alloc(unsigned int max_socks) int reuseport_alloc(struct sock *sk, bool bind_inany) { struct sock_reuseport *reuse; + int id, ret = 0; /* bh lock used since this function call may precede hlist lock in * soft irq of receive path or setsockopt from process context @@ -78,10 +60,18 @@ int reuseport_alloc(struct sock *sk, bool bind_inany) reuse = __reuseport_alloc(INIT_SOCKS); if (!reuse) { - spin_unlock_bh(&reuseport_lock); - return -ENOMEM; + ret = -ENOMEM; + goto out; } + id = ida_alloc(&reuseport_ida, GFP_ATOMIC); + if (id < 0) { + kfree(reuse); + ret = id; + goto out; + } + + reuse->reuseport_id = id; reuse->socks[0] = sk; reuse->num_socks = 1; reuse->bind_inany = bind_inany; @@ -90,7 +80,7 @@ int reuseport_alloc(struct sock *sk, bool bind_inany) out: spin_unlock_bh(&reuseport_lock); - return 0; + return ret; } EXPORT_SYMBOL(reuseport_alloc); @@ -134,8 +124,7 @@ static void reuseport_free_rcu(struct rcu_head *head) reuse = container_of(head, struct sock_reuseport, rcu); sk_reuseport_prog_free(rcu_dereference_protected(reuse->prog, 1)); - if (reuse->reuseport_id) - ida_simple_remove(&reuseport_ida, reuse->reuseport_id); + ida_free(&reuseport_ida, reuse->reuseport_id); kfree(reuse); } @@ -199,12 +188,15 @@ void reuseport_detach_sock(struct sock *sk) reuse = rcu_dereference_protected(sk->sk_reuseport_cb, lockdep_is_held(&reuseport_lock)); - /* At least one of the sk in this reuseport group is added to - * a bpf map. Notify the bpf side. The bpf map logic will - * remove the sk if it is indeed added to a bpf map. + /* Notify the bpf side. The sk may be added to a sockarray + * map. If so, sockarray logic will remove it from the map. + * + * Other bpf map types that work with reuseport, like sockmap, + * don't need an explicit callback from here. They override sk + * unhash/close ops to remove the sk from the map before we + * get to this point. */ - if (reuse->reuseport_id) - bpf_sk_reuseport_detach(sk); + bpf_sk_reuseport_detach(sk); rcu_assign_pointer(sk->sk_reuseport_cb, NULL); -- cgit v1.2.3 From 738981bd74e89d5de012c56a20427e34c6937334 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Wed, 19 Feb 2020 14:01:35 +0100 Subject: scsi: target: fix unmap_zeroes_data boolean initialisation The LIO unmap_zeroes_data device attribute is mapped to the LBPRZ flag in the READ CAPACITY(16) and Thin Provisioning VPD INQUIRY responses. The unmap_zeroes_data attribute is exposed via configfs, where any write value is correctly validated via strtobool(). However, when initialised via target_configure_unmap_from_queue() it takes the value of the device's max_write_zeroes_sectors queue limit, which is non-boolean. A non-boolean value can be read from configfs, but attempting to write the same value back results in -EINVAL, causing problems for configuration utilities such as targetcli. Link: https://marc.info/?l=target-devel&m=158213354011309 Fixes: 2237498f0b5c ("target/iblock: Convert WRITE_SAME to blkdev_issue_zeroout") Reviewed-by: Bart Van Assche Signed-off-by: David Disseldorp Signed-off-by: Martin K. Petersen --- drivers/target/target_core_device.c | 2 +- include/target/target_core_base.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 2d19f0e332b0..2c7ba2f7e13c 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -829,7 +829,7 @@ bool target_configure_unmap_from_queue(struct se_dev_attrib *attrib, attrib->unmap_granularity = q->limits.discard_granularity / block_size; attrib->unmap_granularity_alignment = q->limits.discard_alignment / block_size; - attrib->unmap_zeroes_data = (q->limits.max_write_zeroes_sectors); + attrib->unmap_zeroes_data = !!(q->limits.max_write_zeroes_sectors); return true; } EXPORT_SYMBOL(target_configure_unmap_from_queue); diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 1728e883b7b2..35188e64239e 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -682,7 +682,7 @@ struct se_dev_attrib { int force_pr_aptpl; int is_nonrot; int emulate_rest_reord; - int unmap_zeroes_data; + bool unmap_zeroes_data; u32 hw_block_size; u32 block_size; u32 hw_max_sectors; -- cgit v1.2.3 From 87310c9fb502868798c26c6720837718e94dd479 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Wed, 19 Feb 2020 14:01:36 +0100 Subject: scsi: target: convert boolean se_dev_attrib types to bool This should harden us against configfs API regressions similar to the one fixed by the previous commit. Link: https://marc.info/?l=target-devel&m=158211731505174 Signed-off-by: David Disseldorp Reviewed-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/target/target_core_tmr.c | 6 +++--- include/target/target_core_base.h | 32 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index feeba3966617..afbd492c76a9 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -78,7 +78,7 @@ static int target_check_cdb_and_preempt(struct list_head *list, } static bool __target_check_io_state(struct se_cmd *se_cmd, - struct se_session *tmr_sess, int tas) + struct se_session *tmr_sess, bool tas) { struct se_session *sess = se_cmd->se_sess; @@ -251,7 +251,7 @@ static void core_tmr_drain_state_list( struct se_device *dev, struct se_cmd *prout_cmd, struct se_session *tmr_sess, - int tas, + bool tas, struct list_head *preempt_and_abort_list) { LIST_HEAD(drain_task_list); @@ -334,7 +334,7 @@ int core_tmr_lun_reset( struct se_node_acl *tmr_nacl = NULL; struct se_portal_group *tmr_tpg = NULL; struct se_session *tmr_sess = NULL; - int tas; + bool tas; /* * TASK_ABORTED status bit, this is configurable via ConfigFS * struct se_device attributes. spc4r17 section 7.4.6 Control mode page diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 35188e64239e..bbac0f1c3ba1 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -663,25 +663,25 @@ struct se_dev_entry { }; struct se_dev_attrib { - int emulate_model_alias; - int emulate_dpo; - int emulate_fua_write; - int emulate_fua_read; - int emulate_write_cache; + bool emulate_model_alias; + bool emulate_dpo; /* deprecated */ + bool emulate_fua_write; + bool emulate_fua_read; /* deprecated */ + bool emulate_write_cache; int emulate_ua_intlck_ctrl; - int emulate_tas; - int emulate_tpu; - int emulate_tpws; - int emulate_caw; - int emulate_3pc; - int emulate_pr; + bool emulate_tas; + bool emulate_tpu; + bool emulate_tpws; + bool emulate_caw; + bool emulate_3pc; + bool emulate_pr; enum target_prot_type pi_prot_type; enum target_prot_type hw_pi_prot_type; - int pi_prot_verify; - int enforce_pr_isids; - int force_pr_aptpl; - int is_nonrot; - int emulate_rest_reord; + bool pi_prot_verify; + bool enforce_pr_isids; + bool force_pr_aptpl; + bool is_nonrot; + bool emulate_rest_reord; bool unmap_zeroes_data; u32 hw_block_size; u32 block_size; -- cgit v1.2.3 From 1bf630fddd505e4d3e1c7e857eb54f8da787fe9a Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Fri, 21 Feb 2020 10:44:01 +0100 Subject: scsi: target: use an enum to track emulate_ua_intlck_ctrl The emulate_ua_intlck_ctrl device attribute accepts values of 0, 1 or 2 via ConfigFS, which map to unit attention interlocks control codes in the MODE SENSE control Mode Page. Use an enum to track these values so that it's clear that, unlike the remaining emulate_X attributes, emulate_ua_intlck_ctrl isn't boolean. Link: https://marc.info/?l=target-devel&m=158227825428798 Suggested-by: Bart Van Assche Signed-off-by: David Disseldorp Signed-off-by: Martin K. Petersen --- drivers/target/target_core_configfs.c | 4 +++- drivers/target/target_core_device.c | 2 +- drivers/target/target_core_spc.c | 13 +++++++++++-- drivers/target/target_core_transport.c | 3 ++- drivers/target/target_core_ua.c | 8 +++++--- include/target/target_core_base.h | 11 ++++++++--- 6 files changed, 30 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index e6e175597860..ff82b21fdcce 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -684,7 +684,9 @@ static ssize_t emulate_ua_intlck_ctrl_store(struct config_item *item, if (ret < 0) return ret; - if (val != 0 && val != 1 && val != 2) { + if (val != TARGET_UA_INTLCK_CTRL_CLEAR + && val != TARGET_UA_INTLCK_CTRL_NO_CLEAR + && val != TARGET_UA_INTLCK_CTRL_ESTABLISH_UA) { pr_err("Illegal value %d\n", val); return -EINVAL; } diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 2c7ba2f7e13c..4cee1138284b 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -767,7 +767,7 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->dev_attrib.emulate_fua_write = 1; dev->dev_attrib.emulate_fua_read = 1; dev->dev_attrib.emulate_write_cache = DA_EMULATE_WRITE_CACHE; - dev->dev_attrib.emulate_ua_intlck_ctrl = DA_EMULATE_UA_INTLLCK_CTRL; + dev->dev_attrib.emulate_ua_intlck_ctrl = TARGET_UA_INTLCK_CTRL_CLEAR; dev->dev_attrib.emulate_tas = DA_EMULATE_TAS; dev->dev_attrib.emulate_tpu = DA_EMULATE_TPU; dev->dev_attrib.emulate_tpws = DA_EMULATE_TPWS; diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 6d4cf2643c0a..ca5579ebc81d 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -847,8 +847,17 @@ static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p) * for a BUSY, TASK SET FULL, or RESERVATION CONFLICT status regardless * to the number of commands completed with one of those status codes. */ - p[4] = (dev->dev_attrib.emulate_ua_intlck_ctrl == 2) ? 0x30 : - (dev->dev_attrib.emulate_ua_intlck_ctrl == 1) ? 0x20 : 0x00; + switch (dev->dev_attrib.emulate_ua_intlck_ctrl) { + case TARGET_UA_INTLCK_CTRL_ESTABLISH_UA: + p[4] = 0x30; + break; + case TARGET_UA_INTLCK_CTRL_NO_CLEAR: + p[4] = 0x20; + break; + default: /* TARGET_UA_INTLCK_CTRL_CLEAR */ + p[4] = 0x00; + break; + } /* * From spc4r17, section 7.4.6 Control mode Page * diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index ea482d4b1f00..9681d4ba02d7 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1879,7 +1879,8 @@ void transport_generic_request_failure(struct se_cmd *cmd, * See spc4r17, section 7.4.6 Control Mode Page, Table 349 */ if (cmd->se_sess && - cmd->se_dev->dev_attrib.emulate_ua_intlck_ctrl == 2) { + cmd->se_dev->dev_attrib.emulate_ua_intlck_ctrl + == TARGET_UA_INTLCK_CTRL_ESTABLISH_UA) { target_ua_allocate_lun(cmd->se_sess->se_node_acl, cmd->orig_fe_lun, 0x2C, ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS); diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c index 151b56002da5..4276690fb6cb 100644 --- a/drivers/target/target_core_ua.c +++ b/drivers/target/target_core_ua.c @@ -199,6 +199,8 @@ bool core_scsi3_ua_for_check_condition(struct se_cmd *cmd, u8 *key, u8 *asc, struct se_node_acl *nacl; struct se_ua *ua = NULL, *ua_p; int head = 1; + bool dev_ua_intlck_clear = (dev->dev_attrib.emulate_ua_intlck_ctrl + == TARGET_UA_INTLCK_CTRL_CLEAR); if (WARN_ON_ONCE(!sess)) return false; @@ -229,7 +231,7 @@ bool core_scsi3_ua_for_check_condition(struct se_cmd *cmd, u8 *key, u8 *asc, * highest priority UNIT_ATTENTION and ASC/ASCQ without * clearing it. */ - if (dev->dev_attrib.emulate_ua_intlck_ctrl != 0) { + if (!dev_ua_intlck_clear) { *asc = ua->ua_asc; *ascq = ua->ua_ascq; break; @@ -254,8 +256,8 @@ bool core_scsi3_ua_for_check_condition(struct se_cmd *cmd, u8 *key, u8 *asc, " INTLCK_CTRL: %d, mapped LUN: %llu, got CDB: 0x%02x" " reported ASC: 0x%02x, ASCQ: 0x%02x\n", nacl->se_tpg->se_tpg_tfo->fabric_name, - (dev->dev_attrib.emulate_ua_intlck_ctrl != 0) ? "Reporting" : - "Releasing", dev->dev_attrib.emulate_ua_intlck_ctrl, + dev_ua_intlck_clear ? "Releasing" : "Reporting", + dev->dev_attrib.emulate_ua_intlck_ctrl, cmd->orig_fe_lun, cmd->t_task_cdb[0], *asc, *ascq); return head == 0; diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index bbac0f1c3ba1..6d4a694f6ea7 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -74,8 +74,6 @@ #define DA_EMULATE_MODEL_ALIAS 0 /* Emulation for WriteCache and SYNCHRONIZE_CACHE */ #define DA_EMULATE_WRITE_CACHE 0 -/* Emulation for UNIT ATTENTION Interlock Control */ -#define DA_EMULATE_UA_INTLLCK_CTRL 0 /* Emulation for TASK_ABORTED status (TAS) by default */ #define DA_EMULATE_TAS 1 /* Emulation for Thin Provisioning UNMAP using block/blk-lib.c:blkdev_issue_discard() */ @@ -433,6 +431,13 @@ enum target_prot_type { TARGET_DIF_TYPE3_PROT, }; +/* Emulation for UNIT ATTENTION Interlock Control */ +enum target_ua_intlck_ctrl { + TARGET_UA_INTLCK_CTRL_CLEAR = 0, + TARGET_UA_INTLCK_CTRL_NO_CLEAR = 1, + TARGET_UA_INTLCK_CTRL_ESTABLISH_UA = 2, +}; + enum target_core_dif_check { TARGET_DIF_CHECK_GUARD = 0x1 << 0, TARGET_DIF_CHECK_APPTAG = 0x1 << 1, @@ -668,7 +673,7 @@ struct se_dev_attrib { bool emulate_fua_write; bool emulate_fua_read; /* deprecated */ bool emulate_write_cache; - int emulate_ua_intlck_ctrl; + enum target_ua_intlck_ctrl emulate_ua_intlck_ctrl; bool emulate_tas; bool emulate_tpu; bool emulate_tpws; -- cgit v1.2.3 From 015d239ac0142ad0e26567fd890ef8d171f13709 Mon Sep 17 00:00:00 2001 From: Kenneth Lee Date: Tue, 11 Feb 2020 15:54:23 +0800 Subject: uacce: add uacce driver Uacce (Unified/User-space-access-intended Accelerator Framework) targets to provide Shared Virtual Addressing (SVA) between accelerators and processes. So accelerator can access any data structure of the main cpu. This differs from the data sharing between cpu and io device, which share only data content rather than address. Since unified address, hardware and user space of process can share the same virtual address in the communication. Uacce create a chrdev for every registration, the queue is allocated to the process when the chrdev is opened. Then the process can access the hardware resource by interact with the queue file. By mmap the queue file space to user space, the process can directly put requests to the hardware without syscall to the kernel space. The IOMMU core only tracks mm<->device bonds at the moment, because it only needs to handle IOTLB invalidation and PASID table entries. However uacce needs a finer granularity since multiple queues from the same device can be bound to an mm. When the mm exits, all bound queues must be stopped so that the IOMMU can safely clear the PASID table entry and reallocate the PASID. An intermediate struct uacce_mm links uacce devices and queues. Note that an mm may be bound to multiple devices but an uacce_mm structure only ever belongs to a single device, because we don't need anything more complex (if multiple devices are bound to one mm, then we'll create one uacce_mm for each bond). uacce_device --+-- uacce_mm --+-- uacce_queue | '-- uacce_queue | '-- uacce_mm --+-- uacce_queue +-- uacce_queue '-- uacce_queue Reviewed-by: Greg Kroah-Hartman Reviewed-by: Jonathan Cameron Signed-off-by: Kenneth Lee Signed-off-by: Zaibo Xu Signed-off-by: Zhou Wang Signed-off-by: Jean-Philippe Brucker Signed-off-by: Zhangfei Gao Signed-off-by: Herbert Xu --- Documentation/ABI/testing/sysfs-driver-uacce | 39 ++ drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/uacce/Kconfig | 13 + drivers/misc/uacce/Makefile | 2 + drivers/misc/uacce/uacce.c | 617 +++++++++++++++++++++++++++ include/linux/uacce.h | 161 +++++++ include/uapi/misc/uacce/uacce.h | 38 ++ 8 files changed, 872 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-uacce create mode 100644 drivers/misc/uacce/Kconfig create mode 100644 drivers/misc/uacce/Makefile create mode 100644 drivers/misc/uacce/uacce.c create mode 100644 include/linux/uacce.h create mode 100644 include/uapi/misc/uacce/uacce.h (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-driver-uacce b/Documentation/ABI/testing/sysfs-driver-uacce new file mode 100644 index 000000000000..08f2591138af --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-uacce @@ -0,0 +1,39 @@ +What: /sys/class/uacce//api +Date: Feb 2020 +KernelVersion: 5.7 +Contact: linux-accelerators@lists.ozlabs.org +Description: Api of the device + Can be any string and up to userspace to parse. + Application use the api to match the correct driver + +What: /sys/class/uacce//flags +Date: Feb 2020 +KernelVersion: 5.7 +Contact: linux-accelerators@lists.ozlabs.org +Description: Attributes of the device, see UACCE_DEV_xxx flag defined in uacce.h + +What: /sys/class/uacce//available_instances +Date: Feb 2020 +KernelVersion: 5.7 +Contact: linux-accelerators@lists.ozlabs.org +Description: Available instances left of the device + Return -ENODEV if uacce_ops get_available_instances is not provided + +What: /sys/class/uacce//algorithms +Date: Feb 2020 +KernelVersion: 5.7 +Contact: linux-accelerators@lists.ozlabs.org +Description: Algorithms supported by this accelerator, separated by new line. + Can be any string and up to userspace to parse. + +What: /sys/class/uacce//region_mmio_size +Date: Feb 2020 +KernelVersion: 5.7 +Contact: linux-accelerators@lists.ozlabs.org +Description: Size (bytes) of mmio region queue file + +What: /sys/class/uacce//region_dus_size +Date: Feb 2020 +KernelVersion: 5.7 +Contact: linux-accelerators@lists.ozlabs.org +Description: Size (bytes) of dus region queue file diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 7f0d48f406e3..99e151475d8f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -480,4 +480,5 @@ source "drivers/misc/cxl/Kconfig" source "drivers/misc/ocxl/Kconfig" source "drivers/misc/cardreader/Kconfig" source "drivers/misc/habanalabs/Kconfig" +source "drivers/misc/uacce/Kconfig" endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index c1860d35dc7e..9abf2923d831 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -56,4 +56,5 @@ obj-$(CONFIG_OCXL) += ocxl/ obj-y += cardreader/ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_HABANA_AI) += habanalabs/ +obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o diff --git a/drivers/misc/uacce/Kconfig b/drivers/misc/uacce/Kconfig new file mode 100644 index 000000000000..5e39b6083b0f --- /dev/null +++ b/drivers/misc/uacce/Kconfig @@ -0,0 +1,13 @@ +config UACCE + tristate "Accelerator Framework for User Land" + depends on IOMMU_API + help + UACCE provides interface for the user process to access the hardware + without interaction with the kernel space in data path. + + The user-space interface is described in + include/uapi/misc/uacce/uacce.h + + See Documentation/misc-devices/uacce.rst for more details. + + If you don't know what to do here, say N. diff --git a/drivers/misc/uacce/Makefile b/drivers/misc/uacce/Makefile new file mode 100644 index 000000000000..5b4374e8b5f2 --- /dev/null +++ b/drivers/misc/uacce/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +obj-$(CONFIG_UACCE) += uacce.o diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c new file mode 100644 index 000000000000..ffced4da3c2c --- /dev/null +++ b/drivers/misc/uacce/uacce.c @@ -0,0 +1,617 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include + +static struct class *uacce_class; +static dev_t uacce_devt; +static DEFINE_MUTEX(uacce_mutex); +static DEFINE_XARRAY_ALLOC(uacce_xa); + +static int uacce_start_queue(struct uacce_queue *q) +{ + int ret = 0; + + mutex_lock(&uacce_mutex); + + if (q->state != UACCE_Q_INIT) { + ret = -EINVAL; + goto out_with_lock; + } + + if (q->uacce->ops->start_queue) { + ret = q->uacce->ops->start_queue(q); + if (ret < 0) + goto out_with_lock; + } + + q->state = UACCE_Q_STARTED; + +out_with_lock: + mutex_unlock(&uacce_mutex); + + return ret; +} + +static int uacce_put_queue(struct uacce_queue *q) +{ + struct uacce_device *uacce = q->uacce; + + mutex_lock(&uacce_mutex); + + if (q->state == UACCE_Q_ZOMBIE) + goto out; + + if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue) + uacce->ops->stop_queue(q); + + if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) && + uacce->ops->put_queue) + uacce->ops->put_queue(q); + + q->state = UACCE_Q_ZOMBIE; +out: + mutex_unlock(&uacce_mutex); + + return 0; +} + +static long uacce_fops_unl_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + struct uacce_queue *q = filep->private_data; + struct uacce_device *uacce = q->uacce; + + switch (cmd) { + case UACCE_CMD_START_Q: + return uacce_start_queue(q); + + case UACCE_CMD_PUT_Q: + return uacce_put_queue(q); + + default: + if (!uacce->ops->ioctl) + return -EINVAL; + + return uacce->ops->ioctl(q, cmd, arg); + } +} + +#ifdef CONFIG_COMPAT +static long uacce_fops_compat_ioctl(struct file *filep, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + + return uacce_fops_unl_ioctl(filep, cmd, arg); +} +#endif + +static int uacce_sva_exit(struct device *dev, struct iommu_sva *handle, + void *data) +{ + struct uacce_mm *uacce_mm = data; + struct uacce_queue *q; + + /* + * No new queue can be added concurrently because no caller can have a + * reference to this mm. But there may be concurrent calls to + * uacce_mm_put(), so we need the lock. + */ + mutex_lock(&uacce_mm->lock); + list_for_each_entry(q, &uacce_mm->queues, list) + uacce_put_queue(q); + uacce_mm->mm = NULL; + mutex_unlock(&uacce_mm->lock); + + return 0; +} + +static struct iommu_sva_ops uacce_sva_ops = { + .mm_exit = uacce_sva_exit, +}; + +static struct uacce_mm *uacce_mm_get(struct uacce_device *uacce, + struct uacce_queue *q, + struct mm_struct *mm) +{ + struct uacce_mm *uacce_mm = NULL; + struct iommu_sva *handle = NULL; + int ret; + + lockdep_assert_held(&uacce->mm_lock); + + list_for_each_entry(uacce_mm, &uacce->mm_list, list) { + if (uacce_mm->mm == mm) { + mutex_lock(&uacce_mm->lock); + list_add(&q->list, &uacce_mm->queues); + mutex_unlock(&uacce_mm->lock); + return uacce_mm; + } + } + + uacce_mm = kzalloc(sizeof(*uacce_mm), GFP_KERNEL); + if (!uacce_mm) + return NULL; + + if (uacce->flags & UACCE_DEV_SVA) { + /* + * Safe to pass an incomplete uacce_mm, since mm_exit cannot + * fire while we hold a reference to the mm. + */ + handle = iommu_sva_bind_device(uacce->parent, mm, uacce_mm); + if (IS_ERR(handle)) + goto err_free; + + ret = iommu_sva_set_ops(handle, &uacce_sva_ops); + if (ret) + goto err_unbind; + + uacce_mm->pasid = iommu_sva_get_pasid(handle); + if (uacce_mm->pasid == IOMMU_PASID_INVALID) + goto err_unbind; + } + + uacce_mm->mm = mm; + uacce_mm->handle = handle; + INIT_LIST_HEAD(&uacce_mm->queues); + mutex_init(&uacce_mm->lock); + list_add(&q->list, &uacce_mm->queues); + list_add(&uacce_mm->list, &uacce->mm_list); + + return uacce_mm; + +err_unbind: + if (handle) + iommu_sva_unbind_device(handle); +err_free: + kfree(uacce_mm); + return NULL; +} + +static void uacce_mm_put(struct uacce_queue *q) +{ + struct uacce_mm *uacce_mm = q->uacce_mm; + + lockdep_assert_held(&q->uacce->mm_lock); + + mutex_lock(&uacce_mm->lock); + list_del(&q->list); + mutex_unlock(&uacce_mm->lock); + + if (list_empty(&uacce_mm->queues)) { + if (uacce_mm->handle) + iommu_sva_unbind_device(uacce_mm->handle); + list_del(&uacce_mm->list); + kfree(uacce_mm); + } +} + +static int uacce_fops_open(struct inode *inode, struct file *filep) +{ + struct uacce_mm *uacce_mm = NULL; + struct uacce_device *uacce; + struct uacce_queue *q; + int ret = 0; + + uacce = xa_load(&uacce_xa, iminor(inode)); + if (!uacce) + return -ENODEV; + + q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL); + if (!q) + return -ENOMEM; + + mutex_lock(&uacce->mm_lock); + uacce_mm = uacce_mm_get(uacce, q, current->mm); + mutex_unlock(&uacce->mm_lock); + if (!uacce_mm) { + ret = -ENOMEM; + goto out_with_mem; + } + + q->uacce = uacce; + q->uacce_mm = uacce_mm; + + if (uacce->ops->get_queue) { + ret = uacce->ops->get_queue(uacce, uacce_mm->pasid, q); + if (ret < 0) + goto out_with_mm; + } + + init_waitqueue_head(&q->wait); + filep->private_data = q; + q->state = UACCE_Q_INIT; + + return 0; + +out_with_mm: + mutex_lock(&uacce->mm_lock); + uacce_mm_put(q); + mutex_unlock(&uacce->mm_lock); +out_with_mem: + kfree(q); + return ret; +} + +static int uacce_fops_release(struct inode *inode, struct file *filep) +{ + struct uacce_queue *q = filep->private_data; + struct uacce_device *uacce = q->uacce; + + uacce_put_queue(q); + + mutex_lock(&uacce->mm_lock); + uacce_mm_put(q); + mutex_unlock(&uacce->mm_lock); + + kfree(q); + + return 0; +} + +static void uacce_vma_close(struct vm_area_struct *vma) +{ + struct uacce_queue *q = vma->vm_private_data; + struct uacce_qfile_region *qfr = NULL; + + if (vma->vm_pgoff < UACCE_MAX_REGION) + qfr = q->qfrs[vma->vm_pgoff]; + + kfree(qfr); +} + +static const struct vm_operations_struct uacce_vm_ops = { + .close = uacce_vma_close, +}; + +static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct uacce_queue *q = filep->private_data; + struct uacce_device *uacce = q->uacce; + struct uacce_qfile_region *qfr; + enum uacce_qfrt type = UACCE_MAX_REGION; + int ret = 0; + + if (vma->vm_pgoff < UACCE_MAX_REGION) + type = vma->vm_pgoff; + else + return -EINVAL; + + qfr = kzalloc(sizeof(*qfr), GFP_KERNEL); + if (!qfr) + return -ENOMEM; + + vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK; + vma->vm_ops = &uacce_vm_ops; + vma->vm_private_data = q; + qfr->type = type; + + mutex_lock(&uacce_mutex); + + if (q->state != UACCE_Q_INIT && q->state != UACCE_Q_STARTED) { + ret = -EINVAL; + goto out_with_lock; + } + + if (q->qfrs[type]) { + ret = -EEXIST; + goto out_with_lock; + } + + switch (type) { + case UACCE_QFRT_MMIO: + if (!uacce->ops->mmap) { + ret = -EINVAL; + goto out_with_lock; + } + + ret = uacce->ops->mmap(q, vma, qfr); + if (ret) + goto out_with_lock; + + break; + + case UACCE_QFRT_DUS: + if (!uacce->ops->mmap) { + ret = -EINVAL; + goto out_with_lock; + } + + ret = uacce->ops->mmap(q, vma, qfr); + if (ret) + goto out_with_lock; + break; + + default: + ret = -EINVAL; + goto out_with_lock; + } + + q->qfrs[type] = qfr; + mutex_unlock(&uacce_mutex); + + return ret; + +out_with_lock: + mutex_unlock(&uacce_mutex); + kfree(qfr); + return ret; +} + +static __poll_t uacce_fops_poll(struct file *file, poll_table *wait) +{ + struct uacce_queue *q = file->private_data; + struct uacce_device *uacce = q->uacce; + + poll_wait(file, &q->wait, wait); + if (uacce->ops->is_q_updated && uacce->ops->is_q_updated(q)) + return EPOLLIN | EPOLLRDNORM; + + return 0; +} + +static const struct file_operations uacce_fops = { + .owner = THIS_MODULE, + .open = uacce_fops_open, + .release = uacce_fops_release, + .unlocked_ioctl = uacce_fops_unl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = uacce_fops_compat_ioctl, +#endif + .mmap = uacce_fops_mmap, + .poll = uacce_fops_poll, +}; + +#define to_uacce_device(dev) container_of(dev, struct uacce_device, dev) + +static ssize_t api_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%s\n", uacce->api_ver); +} + +static ssize_t flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%u\n", uacce->flags); +} + +static ssize_t available_instances_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + if (!uacce->ops->get_available_instances) + return -ENODEV; + + return sprintf(buf, "%d\n", + uacce->ops->get_available_instances(uacce)); +} + +static ssize_t algorithms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%s\n", uacce->algs); +} + +static ssize_t region_mmio_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%lu\n", + uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT); +} + +static ssize_t region_dus_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + return sprintf(buf, "%lu\n", + uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT); +} + +static DEVICE_ATTR_RO(api); +static DEVICE_ATTR_RO(flags); +static DEVICE_ATTR_RO(available_instances); +static DEVICE_ATTR_RO(algorithms); +static DEVICE_ATTR_RO(region_mmio_size); +static DEVICE_ATTR_RO(region_dus_size); + +static struct attribute *uacce_dev_attrs[] = { + &dev_attr_api.attr, + &dev_attr_flags.attr, + &dev_attr_available_instances.attr, + &dev_attr_algorithms.attr, + &dev_attr_region_mmio_size.attr, + &dev_attr_region_dus_size.attr, + NULL, +}; + +static umode_t uacce_dev_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct uacce_device *uacce = to_uacce_device(dev); + + if (((attr == &dev_attr_region_mmio_size.attr) && + (!uacce->qf_pg_num[UACCE_QFRT_MMIO])) || + ((attr == &dev_attr_region_dus_size.attr) && + (!uacce->qf_pg_num[UACCE_QFRT_DUS]))) + return 0; + + return attr->mode; +} + +static struct attribute_group uacce_dev_group = { + .is_visible = uacce_dev_is_visible, + .attrs = uacce_dev_attrs, +}; + +__ATTRIBUTE_GROUPS(uacce_dev); + +static void uacce_release(struct device *dev) +{ + struct uacce_device *uacce = to_uacce_device(dev); + + kfree(uacce); +} + +/** + * uacce_alloc() - alloc an accelerator + * @parent: pointer of uacce parent device + * @interface: pointer of uacce_interface for register + * + * Returns uacce pointer if success and ERR_PTR if not + * Need check returned negotiated uacce->flags + */ +struct uacce_device *uacce_alloc(struct device *parent, + struct uacce_interface *interface) +{ + unsigned int flags = interface->flags; + struct uacce_device *uacce; + int ret; + + uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL); + if (!uacce) + return ERR_PTR(-ENOMEM); + + if (flags & UACCE_DEV_SVA) { + ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA); + if (ret) + flags &= ~UACCE_DEV_SVA; + } + + uacce->parent = parent; + uacce->flags = flags; + uacce->ops = interface->ops; + + ret = xa_alloc(&uacce_xa, &uacce->dev_id, uacce, xa_limit_32b, + GFP_KERNEL); + if (ret < 0) + goto err_with_uacce; + + INIT_LIST_HEAD(&uacce->mm_list); + mutex_init(&uacce->mm_lock); + device_initialize(&uacce->dev); + uacce->dev.devt = MKDEV(MAJOR(uacce_devt), uacce->dev_id); + uacce->dev.class = uacce_class; + uacce->dev.groups = uacce_dev_groups; + uacce->dev.parent = uacce->parent; + uacce->dev.release = uacce_release; + dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id); + + return uacce; + +err_with_uacce: + if (flags & UACCE_DEV_SVA) + iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); + kfree(uacce); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(uacce_alloc); + +/** + * uacce_register() - add the accelerator to cdev and export to user space + * @uacce: The initialized uacce device + * + * Return 0 if register succeeded, or an error. + */ +int uacce_register(struct uacce_device *uacce) +{ + if (!uacce) + return -ENODEV; + + uacce->cdev = cdev_alloc(); + if (!uacce->cdev) + return -ENOMEM; + + uacce->cdev->ops = &uacce_fops; + uacce->cdev->owner = THIS_MODULE; + + return cdev_device_add(uacce->cdev, &uacce->dev); +} +EXPORT_SYMBOL_GPL(uacce_register); + +/** + * uacce_remove() - remove the accelerator + * @uacce: the accelerator to remove + */ +void uacce_remove(struct uacce_device *uacce) +{ + struct uacce_mm *uacce_mm; + struct uacce_queue *q; + + if (!uacce) + return; + + /* ensure no open queue remains */ + mutex_lock(&uacce->mm_lock); + list_for_each_entry(uacce_mm, &uacce->mm_list, list) { + /* + * We don't take the uacce_mm->lock here. Since we hold the + * device's mm_lock, no queue can be added to or removed from + * this uacce_mm. We may run concurrently with mm_exit, but + * uacce_put_queue() is serialized and iommu_sva_unbind_device() + * waits for the lock that mm_exit is holding. + */ + list_for_each_entry(q, &uacce_mm->queues, list) + uacce_put_queue(q); + + if (uacce->flags & UACCE_DEV_SVA) { + iommu_sva_unbind_device(uacce_mm->handle); + uacce_mm->handle = NULL; + } + } + mutex_unlock(&uacce->mm_lock); + + /* disable sva now since no opened queues */ + if (uacce->flags & UACCE_DEV_SVA) + iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA); + + if (uacce->cdev) + cdev_device_del(uacce->cdev, &uacce->dev); + xa_erase(&uacce_xa, uacce->dev_id); + put_device(&uacce->dev); +} +EXPORT_SYMBOL_GPL(uacce_remove); + +static int __init uacce_init(void) +{ + int ret; + + uacce_class = class_create(THIS_MODULE, UACCE_NAME); + if (IS_ERR(uacce_class)) + return PTR_ERR(uacce_class); + + ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME); + if (ret) + class_destroy(uacce_class); + + return ret; +} + +static __exit void uacce_exit(void) +{ + unregister_chrdev_region(uacce_devt, MINORMASK); + class_destroy(uacce_class); +} + +subsys_initcall(uacce_init); +module_exit(uacce_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hisilicon Tech. Co., Ltd."); +MODULE_DESCRIPTION("Accelerator interface for Userland applications"); diff --git a/include/linux/uacce.h b/include/linux/uacce.h new file mode 100644 index 000000000000..904a461591a3 --- /dev/null +++ b/include/linux/uacce.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_UACCE_H +#define _LINUX_UACCE_H + +#include +#include + +#define UACCE_NAME "uacce" +#define UACCE_MAX_REGION 2 +#define UACCE_MAX_NAME_SIZE 64 + +struct uacce_queue; +struct uacce_device; + +/** + * struct uacce_qfile_region - structure of queue file region + * @type: type of the region + */ +struct uacce_qfile_region { + enum uacce_qfrt type; +}; + +/** + * struct uacce_ops - uacce device operations + * @get_available_instances: get available instances left of the device + * @get_queue: get a queue from the device + * @put_queue: free a queue to the device + * @start_queue: make the queue start work after get_queue + * @stop_queue: make the queue stop work before put_queue + * @is_q_updated: check whether the task is finished + * @mmap: mmap addresses of queue to user space + * @ioctl: ioctl for user space users of the queue + */ +struct uacce_ops { + int (*get_available_instances)(struct uacce_device *uacce); + int (*get_queue)(struct uacce_device *uacce, unsigned long arg, + struct uacce_queue *q); + void (*put_queue)(struct uacce_queue *q); + int (*start_queue)(struct uacce_queue *q); + void (*stop_queue)(struct uacce_queue *q); + int (*is_q_updated)(struct uacce_queue *q); + int (*mmap)(struct uacce_queue *q, struct vm_area_struct *vma, + struct uacce_qfile_region *qfr); + long (*ioctl)(struct uacce_queue *q, unsigned int cmd, + unsigned long arg); +}; + +/** + * struct uacce_interface - interface required for uacce_register() + * @name: the uacce device name. Will show up in sysfs + * @flags: uacce device attributes + * @ops: pointer to the struct uacce_ops + */ +struct uacce_interface { + char name[UACCE_MAX_NAME_SIZE]; + unsigned int flags; + const struct uacce_ops *ops; +}; + +enum uacce_q_state { + UACCE_Q_ZOMBIE = 0, + UACCE_Q_INIT, + UACCE_Q_STARTED, +}; + +/** + * struct uacce_queue + * @uacce: pointer to uacce + * @priv: private pointer + * @wait: wait queue head + * @list: index into uacce_mm + * @uacce_mm: the corresponding mm + * @qfrs: pointer of qfr regions + * @state: queue state machine + */ +struct uacce_queue { + struct uacce_device *uacce; + void *priv; + wait_queue_head_t wait; + struct list_head list; + struct uacce_mm *uacce_mm; + struct uacce_qfile_region *qfrs[UACCE_MAX_REGION]; + enum uacce_q_state state; +}; + +/** + * struct uacce_device + * @algs: supported algorithms + * @api_ver: api version + * @ops: pointer to the struct uacce_ops + * @qf_pg_num: page numbers of the queue file regions + * @parent: pointer to the parent device + * @is_vf: whether virtual function + * @flags: uacce attributes + * @dev_id: id of the uacce device + * @cdev: cdev of the uacce + * @dev: dev of the uacce + * @priv: private pointer of the uacce + * @mm_list: list head of uacce_mm->list + * @mm_lock: lock for mm_list + */ +struct uacce_device { + const char *algs; + const char *api_ver; + const struct uacce_ops *ops; + unsigned long qf_pg_num[UACCE_MAX_REGION]; + struct device *parent; + bool is_vf; + u32 flags; + u32 dev_id; + struct cdev *cdev; + struct device dev; + void *priv; + struct list_head mm_list; + struct mutex mm_lock; +}; + +/** + * struct uacce_mm - keep track of queues bound to a process + * @list: index into uacce_device + * @queues: list of queues + * @mm: the mm struct + * @lock: protects the list of queues + * @pasid: pasid of the uacce_mm + * @handle: iommu_sva handle return from iommu_sva_bind_device + */ +struct uacce_mm { + struct list_head list; + struct list_head queues; + struct mm_struct *mm; + struct mutex lock; + int pasid; + struct iommu_sva *handle; +}; + +#if IS_ENABLED(CONFIG_UACCE) + +struct uacce_device *uacce_alloc(struct device *parent, + struct uacce_interface *interface); +int uacce_register(struct uacce_device *uacce); +void uacce_remove(struct uacce_device *uacce); + +#else /* CONFIG_UACCE */ + +static inline +struct uacce_device *uacce_alloc(struct device *parent, + struct uacce_interface *interface) +{ + return ERR_PTR(-ENODEV); +} + +static inline int uacce_register(struct uacce_device *uacce) +{ + return -EINVAL; +} + +static inline void uacce_remove(struct uacce_device *uacce) {} + +#endif /* CONFIG_UACCE */ + +#endif /* _LINUX_UACCE_H */ diff --git a/include/uapi/misc/uacce/uacce.h b/include/uapi/misc/uacce/uacce.h new file mode 100644 index 000000000000..cc7185678f47 --- /dev/null +++ b/include/uapi/misc/uacce/uacce.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +#ifndef _UAPIUUACCE_H +#define _UAPIUUACCE_H + +#include +#include + +/* + * UACCE_CMD_START_Q: Start queue + */ +#define UACCE_CMD_START_Q _IO('W', 0) + +/* + * UACCE_CMD_PUT_Q: + * User actively stop queue and free queue resource immediately + * Optimization method since close fd may delay + */ +#define UACCE_CMD_PUT_Q _IO('W', 1) + +/* + * UACCE Device flags: + * UACCE_DEV_SVA: Shared Virtual Addresses + * Support PASID + * Support device page faults (PCI PRI or SMMU Stall) + */ +#define UACCE_DEV_SVA BIT(0) + +/** + * enum uacce_qfrt: queue file region type + * @UACCE_QFRT_MMIO: device mmio region + * @UACCE_QFRT_DUS: device user share region + */ +enum uacce_qfrt { + UACCE_QFRT_MMIO = 0, + UACCE_QFRT_DUS = 1, +}; + +#endif -- cgit v1.2.3 From 9e00df7156e45e42c695ffc596b4bf1328d00516 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Tue, 11 Feb 2020 15:54:25 +0800 Subject: crypto: hisilicon - register zip engine to uacce Register qm to uacce framework for user crypto driver Reviewed-by: Greg Kroah-Hartman Reviewed-by: Jonathan Cameron Signed-off-by: Zhangfei Gao Signed-off-by: Zhou Wang Signed-off-by: Herbert Xu --- drivers/crypto/hisilicon/qm.c | 239 +++++++++++++++++++++++++++++++- drivers/crypto/hisilicon/qm.h | 11 ++ drivers/crypto/hisilicon/zip/zip_main.c | 18 ++- include/uapi/misc/uacce/hisi_qm.h | 23 +++ 4 files changed, 283 insertions(+), 8 deletions(-) create mode 100644 include/uapi/misc/uacce/hisi_qm.h (limited to 'include') diff --git a/drivers/crypto/hisilicon/qm.c b/drivers/crypto/hisilicon/qm.c index 79f84dc63a43..ad7146a0c577 100644 --- a/drivers/crypto/hisilicon/qm.c +++ b/drivers/crypto/hisilicon/qm.c @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include #include "qm.h" /* eq/aeq irq enable */ @@ -466,9 +469,14 @@ static void qm_cq_head_update(struct hisi_qp *qp) static void qm_poll_qp(struct hisi_qp *qp, struct hisi_qm *qm) { - struct qm_cqe *cqe = qp->cqe + qp->qp_status.cq_head; + if (qp->event_cb) { + qp->event_cb(qp); + return; + } if (qp->req_cb) { + struct qm_cqe *cqe = qp->cqe + qp->qp_status.cq_head; + while (QM_CQE_PHASE(cqe) == qp->qp_status.cqc_phase) { dma_rmb(); qp->req_cb(qp, qp->sqe + qm->sqe_size * @@ -1273,7 +1281,7 @@ static int qm_qp_ctx_cfg(struct hisi_qp *qp, int qp_id, int pasid) * @qp: The qp we want to start to run. * @arg: Accelerator specific argument. * - * After this function, qp can receive request from user. Return qp_id if + * After this function, qp can receive request from user. Return 0 if * successful, Return -EBUSY if failed. */ int hisi_qm_start_qp(struct hisi_qp *qp, unsigned long arg) @@ -1318,7 +1326,7 @@ int hisi_qm_start_qp(struct hisi_qp *qp, unsigned long arg) dev_dbg(dev, "queue %d started\n", qp_id); - return qp_id; + return 0; } EXPORT_SYMBOL_GPL(hisi_qm_start_qp); @@ -1399,6 +1407,214 @@ static void hisi_qm_cache_wb(struct hisi_qm *qm) } } +static void qm_qp_event_notifier(struct hisi_qp *qp) +{ + wake_up_interruptible(&qp->uacce_q->wait); +} + +static int hisi_qm_get_available_instances(struct uacce_device *uacce) +{ + int i, ret; + struct hisi_qm *qm = uacce->priv; + + read_lock(&qm->qps_lock); + for (i = 0, ret = 0; i < qm->qp_num; i++) + if (!qm->qp_array[i]) + ret++; + read_unlock(&qm->qps_lock); + + return ret; +} + +static int hisi_qm_uacce_get_queue(struct uacce_device *uacce, + unsigned long arg, + struct uacce_queue *q) +{ + struct hisi_qm *qm = uacce->priv; + struct hisi_qp *qp; + u8 alg_type = 0; + + qp = hisi_qm_create_qp(qm, alg_type); + if (IS_ERR(qp)) + return PTR_ERR(qp); + + q->priv = qp; + q->uacce = uacce; + qp->uacce_q = q; + qp->event_cb = qm_qp_event_notifier; + qp->pasid = arg; + + return 0; +} + +static void hisi_qm_uacce_put_queue(struct uacce_queue *q) +{ + struct hisi_qp *qp = q->priv; + + hisi_qm_cache_wb(qp->qm); + hisi_qm_release_qp(qp); +} + +/* map sq/cq/doorbell to user space */ +static int hisi_qm_uacce_mmap(struct uacce_queue *q, + struct vm_area_struct *vma, + struct uacce_qfile_region *qfr) +{ + struct hisi_qp *qp = q->priv; + struct hisi_qm *qm = qp->qm; + size_t sz = vma->vm_end - vma->vm_start; + struct pci_dev *pdev = qm->pdev; + struct device *dev = &pdev->dev; + unsigned long vm_pgoff; + int ret; + + switch (qfr->type) { + case UACCE_QFRT_MMIO: + if (qm->ver == QM_HW_V2) { + if (sz > PAGE_SIZE * (QM_DOORBELL_PAGE_NR + + QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE)) + return -EINVAL; + } else { + if (sz > PAGE_SIZE * QM_DOORBELL_PAGE_NR) + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + + return remap_pfn_range(vma, vma->vm_start, + qm->phys_base >> PAGE_SHIFT, + sz, pgprot_noncached(vma->vm_page_prot)); + case UACCE_QFRT_DUS: + if (sz != qp->qdma.size) + return -EINVAL; + + /* + * dma_mmap_coherent() requires vm_pgoff as 0 + * restore vm_pfoff to initial value for mmap() + */ + vm_pgoff = vma->vm_pgoff; + vma->vm_pgoff = 0; + ret = dma_mmap_coherent(dev, vma, qp->qdma.va, + qp->qdma.dma, sz); + vma->vm_pgoff = vm_pgoff; + return ret; + + default: + return -EINVAL; + } +} + +static int hisi_qm_uacce_start_queue(struct uacce_queue *q) +{ + struct hisi_qp *qp = q->priv; + + return hisi_qm_start_qp(qp, qp->pasid); +} + +static void hisi_qm_uacce_stop_queue(struct uacce_queue *q) +{ + hisi_qm_stop_qp(q->priv); +} + +static int qm_set_sqctype(struct uacce_queue *q, u16 type) +{ + struct hisi_qm *qm = q->uacce->priv; + struct hisi_qp *qp = q->priv; + + write_lock(&qm->qps_lock); + qp->alg_type = type; + write_unlock(&qm->qps_lock); + + return 0; +} + +static long hisi_qm_uacce_ioctl(struct uacce_queue *q, unsigned int cmd, + unsigned long arg) +{ + struct hisi_qp *qp = q->priv; + struct hisi_qp_ctx qp_ctx; + + if (cmd == UACCE_CMD_QM_SET_QP_CTX) { + if (copy_from_user(&qp_ctx, (void __user *)arg, + sizeof(struct hisi_qp_ctx))) + return -EFAULT; + + if (qp_ctx.qc_type != 0 && qp_ctx.qc_type != 1) + return -EINVAL; + + qm_set_sqctype(q, qp_ctx.qc_type); + qp_ctx.id = qp->qp_id; + + if (copy_to_user((void __user *)arg, &qp_ctx, + sizeof(struct hisi_qp_ctx))) + return -EFAULT; + } else { + return -EINVAL; + } + + return 0; +} + +static const struct uacce_ops uacce_qm_ops = { + .get_available_instances = hisi_qm_get_available_instances, + .get_queue = hisi_qm_uacce_get_queue, + .put_queue = hisi_qm_uacce_put_queue, + .start_queue = hisi_qm_uacce_start_queue, + .stop_queue = hisi_qm_uacce_stop_queue, + .mmap = hisi_qm_uacce_mmap, + .ioctl = hisi_qm_uacce_ioctl, +}; + +static int qm_alloc_uacce(struct hisi_qm *qm) +{ + struct pci_dev *pdev = qm->pdev; + struct uacce_device *uacce; + unsigned long mmio_page_nr; + unsigned long dus_page_nr; + struct uacce_interface interface = { + .flags = UACCE_DEV_SVA, + .ops = &uacce_qm_ops, + }; + + strncpy(interface.name, pdev->driver->name, sizeof(interface.name)); + + uacce = uacce_alloc(&pdev->dev, &interface); + if (IS_ERR(uacce)) + return PTR_ERR(uacce); + + if (uacce->flags & UACCE_DEV_SVA) { + qm->use_sva = true; + } else { + /* only consider sva case */ + uacce_remove(uacce); + qm->uacce = NULL; + return -EINVAL; + } + + uacce->is_vf = pdev->is_virtfn; + uacce->priv = qm; + uacce->algs = qm->algs; + + if (qm->ver == QM_HW_V1) { + mmio_page_nr = QM_DOORBELL_PAGE_NR; + uacce->api_ver = HISI_QM_API_VER_BASE; + } else { + mmio_page_nr = QM_DOORBELL_PAGE_NR + + QM_DOORBELL_SQ_CQ_BASE_V2 / PAGE_SIZE; + uacce->api_ver = HISI_QM_API_VER2_BASE; + } + + dus_page_nr = (PAGE_SIZE - 1 + qm->sqe_size * QM_Q_DEPTH + + sizeof(struct qm_cqe) * QM_Q_DEPTH) >> PAGE_SHIFT; + + uacce->qf_pg_num[UACCE_QFRT_MMIO] = mmio_page_nr; + uacce->qf_pg_num[UACCE_QFRT_DUS] = dus_page_nr; + + qm->uacce = uacce; + + return 0; +} + /** * hisi_qm_get_free_qp_num() - Get free number of qp in qm. * @qm: The qm which want to get free qp. @@ -1441,10 +1657,14 @@ int hisi_qm_init(struct hisi_qm *qm) return -EINVAL; } + ret = qm_alloc_uacce(qm); + if (ret < 0) + dev_warn(&pdev->dev, "fail to alloc uacce (%d)\n", ret); + ret = pci_enable_device_mem(pdev); if (ret < 0) { dev_err(&pdev->dev, "Failed to enable device mem!\n"); - return ret; + goto err_remove_uacce; } ret = pci_request_mem_regions(pdev, qm->dev_name); @@ -1453,8 +1673,9 @@ int hisi_qm_init(struct hisi_qm *qm) goto err_disable_pcidev; } - qm->io_base = ioremap(pci_resource_start(pdev, PCI_BAR_2), - pci_resource_len(qm->pdev, PCI_BAR_2)); + qm->phys_base = pci_resource_start(pdev, PCI_BAR_2); + qm->phys_size = pci_resource_len(qm->pdev, PCI_BAR_2); + qm->io_base = ioremap(qm->phys_base, qm->phys_size); if (!qm->io_base) { ret = -EIO; goto err_release_mem_regions; @@ -1497,6 +1718,9 @@ err_release_mem_regions: pci_release_mem_regions(pdev); err_disable_pcidev: pci_disable_device(pdev); +err_remove_uacce: + uacce_remove(qm->uacce); + qm->uacce = NULL; return ret; } @@ -1513,6 +1737,9 @@ void hisi_qm_uninit(struct hisi_qm *qm) struct pci_dev *pdev = qm->pdev; struct device *dev = &pdev->dev; + uacce_remove(qm->uacce); + qm->uacce = NULL; + if (qm->use_dma_api && qm->qdma.va) { hisi_qm_cache_wb(qm); dma_free_coherent(dev, qm->qdma.size, diff --git a/drivers/crypto/hisilicon/qm.h b/drivers/crypto/hisilicon/qm.h index cae26eabd849..1a4f208a9448 100644 --- a/drivers/crypto/hisilicon/qm.h +++ b/drivers/crypto/hisilicon/qm.h @@ -77,6 +77,9 @@ #define HISI_ACC_SGL_SGE_NR_MAX 255 +/* page number for queue file region */ +#define QM_DOORBELL_PAGE_NR 1 + enum qp_state { QP_STOP, }; @@ -180,7 +183,12 @@ struct hisi_qm { u32 error_mask; u32 msi_mask; + const char *algs; bool use_dma_api; + bool use_sva; + resource_size_t phys_base; + resource_size_t phys_size; + struct uacce_device *uacce; }; struct hisi_qp_status { @@ -210,10 +218,13 @@ struct hisi_qp { struct hisi_qp_ops *hw_ops; void *qp_ctx; void (*req_cb)(struct hisi_qp *qp, void *data); + void (*event_cb)(struct hisi_qp *qp); struct work_struct work; struct workqueue_struct *wq; struct hisi_qm *qm; + u16 pasid; + struct uacce_queue *uacce_q; }; int hisi_qm_init(struct hisi_qm *qm); diff --git a/drivers/crypto/hisilicon/zip/zip_main.c b/drivers/crypto/hisilicon/zip/zip_main.c index 868e32c2d4e5..25a311248fb8 100644 --- a/drivers/crypto/hisilicon/zip/zip_main.c +++ b/drivers/crypto/hisilicon/zip/zip_main.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "zip.h" #define PCI_DEVICE_ID_ZIP_PF 0xa250 @@ -354,8 +355,14 @@ static void hisi_zip_set_user_domain_and_cache(struct hisi_zip *hisi_zip) writel(AXUSER_BASE, base + HZIP_BD_RUSER_32_63); writel(AXUSER_BASE, base + HZIP_SGL_RUSER_32_63); writel(AXUSER_BASE, base + HZIP_BD_WUSER_32_63); - writel(AXUSER_BASE, base + HZIP_DATA_RUSER_32_63); - writel(AXUSER_BASE, base + HZIP_DATA_WUSER_32_63); + + if (hisi_zip->qm.use_sva) { + writel(AXUSER_BASE | AXUSER_SSV, base + HZIP_DATA_RUSER_32_63); + writel(AXUSER_BASE | AXUSER_SSV, base + HZIP_DATA_WUSER_32_63); + } else { + writel(AXUSER_BASE, base + HZIP_DATA_RUSER_32_63); + writel(AXUSER_BASE, base + HZIP_DATA_WUSER_32_63); + } /* let's open all compression/decompression cores */ writel(DECOMP_CHECK_ENABLE | ALL_COMP_DECOMP_EN, @@ -842,6 +849,7 @@ static int hisi_zip_probe(struct pci_dev *pdev, const struct pci_device_id *id) qm->pdev = pdev; qm->ver = rev_id; + qm->algs = "zlib\ngzip"; qm->sqe_size = HZIP_SQE_SIZE; qm->dev_name = hisi_zip_name; qm->fun_type = (pdev->device == PCI_DEVICE_ID_ZIP_PF) ? QM_HW_PF : @@ -885,6 +893,12 @@ static int hisi_zip_probe(struct pci_dev *pdev, const struct pci_device_id *id) hisi_zip_add_to_list(hisi_zip); + if (qm->uacce) { + ret = uacce_register(qm->uacce); + if (ret) + goto err_qm_uninit; + } + if (qm->fun_type == QM_HW_PF && vfs_num > 0) { ret = hisi_zip_sriov_enable(pdev, vfs_num); if (ret < 0) diff --git a/include/uapi/misc/uacce/hisi_qm.h b/include/uapi/misc/uacce/hisi_qm.h new file mode 100644 index 000000000000..6435f0bcb556 --- /dev/null +++ b/include/uapi/misc/uacce/hisi_qm.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +#ifndef _UAPI_HISI_QM_H +#define _UAPI_HISI_QM_H + +#include + +/** + * struct hisi_qp_ctx - User data for hisi qp. + * @id: qp_index return to user space + * @qc_type: Accelerator algorithm type + */ +struct hisi_qp_ctx { + __u16 id; + __u16 qc_type; +}; + +#define HISI_QM_API_VER_BASE "hisi_qm_v1" +#define HISI_QM_API_VER2_BASE "hisi_qm_v2" + +/* UACCE_CMD_QM_SET_QP_CTX: Set qp algorithm type */ +#define UACCE_CMD_QM_SET_QP_CTX _IOWR('H', 10, struct hisi_qp_ctx) + +#endif -- cgit v1.2.3 From 30332eeefec8f83afcea00c360f99ef64b87f220 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 11 Feb 2020 19:18:55 +0100 Subject: debugfs: regset32: Add Runtime PM support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hardware registers of devices under control of power management cannot be accessed at all times. If such a device is suspended, register accesses may lead to undefined behavior, like reading bogus values, or causing exceptions or system lock-ups. Extend struct debugfs_regset32 with an optional field to let device drivers specify the device the registers in the set belong to. This allows debugfs_show_regset32() to make sure the device is resumed while its registers are being read. Signed-off-by: Geert Uytterhoeven Reviewed-by: Niklas Söderlund Reviewed-by: Greg Kroah-Hartman Acked-by: Rafael J. Wysocki Signed-off-by: Herbert Xu --- fs/debugfs/file.c | 8 ++++++++ include/linux/debugfs.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 634b09d18b77..204734f8d1c6 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -1060,7 +1061,14 @@ static int debugfs_show_regset32(struct seq_file *s, void *data) { struct debugfs_regset32 *regset = s->private; + if (regset->dev) + pm_runtime_get_sync(regset->dev); + debugfs_print_regs32(s, regset->regs, regset->nregs, regset->base, ""); + + if (regset->dev) + pm_runtime_put(regset->dev); + return 0; } diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 3d013de64f70..ad416853e722 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -35,6 +35,7 @@ struct debugfs_regset32 { const struct debugfs_reg32 *regs; int nregs; void __iomem *base; + struct device *dev; /* Optional device for Runtime PM */ }; extern struct dentry *arch_debugfs_dir; -- cgit v1.2.3 From 6bc3f3979edce0b11deb685a4c817abb7d74b227 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 20 Feb 2020 07:20:17 -0600 Subject: USB: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200220132017.GA29262@embeddedor Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/usbatm.h | 2 +- drivers/usb/dwc2/hcd.h | 2 +- drivers/usb/host/ehci-tegra.c | 2 +- drivers/usb/host/ehci.h | 4 ++-- drivers/usb/host/fotg210.h | 2 +- drivers/usb/host/ohci.h | 4 ++-- drivers/usb/host/xhci-mtk.h | 2 +- drivers/usb/host/xhci.h | 4 ++-- drivers/usb/serial/io_usbvend.h | 4 ++-- drivers/usb/serial/ti_usb_3410_5052.c | 4 ++-- include/linux/usb.h | 4 ++-- include/linux/usb/audio-v2.h | 2 +- include/linux/usb/audio-v3.h | 2 +- include/linux/usb/gadget.h | 2 +- include/linux/usb/hcd.h | 2 +- include/linux/usbdevice_fs.h | 2 +- 16 files changed, 22 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index d3bdc4cc47aa..8725755bd53d 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -164,7 +164,7 @@ struct usbatm_data { unsigned char *cell_buf; /* holds partial rx cell */ unsigned int buf_usage; - struct urb *urbs[0]; + struct urb *urbs[]; }; static inline void *to_usbatm_driver_data(struct usb_interface *intf) diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 8ca6d12a6f57..1224fa9df604 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -199,7 +199,7 @@ struct dwc2_hcd_urb { u32 flags; u16 interval; struct dwc2_hcd_pipe_info pipe_info; - struct dwc2_hcd_iso_packet_desc iso_descs[0]; + struct dwc2_hcd_iso_packet_desc iso_descs[]; }; /* Phases for control transfers */ diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index d6433f206c17..10d51daa6a1b 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -282,7 +282,7 @@ done: struct dma_aligned_buffer { void *kmalloc_ptr; void *old_xfer_buffer; - u8 data[0]; + u8 data[]; }; static void free_dma_aligned_buffer(struct urb *urb) diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index ac5e967907d1..229b3de319e6 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -255,7 +255,7 @@ struct ehci_hcd { /* one per controller */ struct list_head tt_list; /* platform-specific data -- must come last */ - unsigned long priv[0] __aligned(sizeof(s64)); + unsigned long priv[] __aligned(sizeof(s64)); }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -460,7 +460,7 @@ struct ehci_iso_sched { struct list_head td_list; unsigned span; unsigned first_packet; - struct ehci_iso_packet packet[0]; + struct ehci_iso_packet packet[]; }; /* diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h index 1b4db95e5c43..6cee40ec65b4 100644 --- a/drivers/usb/host/fotg210.h +++ b/drivers/usb/host/fotg210.h @@ -490,7 +490,7 @@ struct fotg210_iso_packet { struct fotg210_iso_sched { struct list_head td_list; unsigned span; - struct fotg210_iso_packet packet[0]; + struct fotg210_iso_packet packet[]; }; /* diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index b015b00774b2..27c26ca10bfd 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -337,7 +337,7 @@ typedef struct urb_priv { u16 length; // # tds in this request u16 td_cnt; // tds already serviced struct list_head pending; - struct td *td [0]; // all TDs in this request + struct td *td[]; // all TDs in this request } urb_priv_t; @@ -435,7 +435,7 @@ struct ohci_hcd { struct dentry *debug_dir; /* platform-specific data -- must come last */ - unsigned long priv[0] __aligned(sizeof(s64)); + unsigned long priv[] __aligned(sizeof(s64)); }; diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 5ac458b7d2e0..acd56517215a 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -95,7 +95,7 @@ struct mu3h_sch_ep_info { u32 pkts; u32 cs_count; u32 burst_mode; - u32 bw_budget_table[0]; + u32 bw_budget_table[]; }; #define MU3C_U3_PORT_MAX 4 diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 13d8838cd552..5f47b18609ee 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1642,7 +1642,7 @@ struct xhci_scratchpad { struct urb_priv { int num_tds; int num_tds_done; - struct xhci_td td[0]; + struct xhci_td td[]; }; /* @@ -1893,7 +1893,7 @@ struct xhci_hcd { void *dbc; /* platform-specific data -- must come last */ - unsigned long priv[0] __aligned(sizeof(s64)); + unsigned long priv[] __aligned(sizeof(s64)); }; /* Platform specific overrides to generic XHCI hc_driver ops */ diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index c38e87ac5ea9..0d1a5bb4636e 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -593,7 +593,7 @@ struct ti_i2c_desc { __u8 Type; // Type of descriptor __le16 Size; // Size of data only not including header __u8 CheckSum; // Checksum (8 bit sum of data only) - __u8 Data[0]; // Data starts here + __u8 Data[]; // Data starts here } __attribute__((packed)); // for 5152 devices only (type 2 record) @@ -601,7 +601,7 @@ struct ti_i2c_desc { struct ti_i2c_firmware_rec { __u8 Ver_Major; // Firmware Major version number __u8 Ver_Minor; // Firmware Minor version number - __u8 Data[0]; // Download starts here + __u8 Data[]; // Download starts here } __attribute__((packed)); diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index ef23acc9b9ce..73075b9351c5 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -219,7 +219,7 @@ struct ti_write_data_bytes { u8 bDataCounter; __be16 wBaseAddrHi; __be16 wBaseAddrLo; - u8 bData[0]; + u8 bData[]; } __packed; struct ti_read_data_request { @@ -234,7 +234,7 @@ struct ti_read_data_bytes { __u8 bCmdCode; __u8 bModuleId; __u8 bErrorCode; - __u8 bData[0]; + __u8 bData[]; } __packed; /* Interrupt struct */ diff --git a/include/linux/usb.h b/include/linux/usb.h index ca1a5f1e1c5e..9f3c721c70dc 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -325,7 +325,7 @@ struct usb_interface_cache { /* variable-length array of alternate settings for this interface, * stored in no particular order */ - struct usb_host_interface altsetting[0]; + struct usb_host_interface altsetting[]; }; #define ref_to_usb_interface_cache(r) \ container_of(r, struct usb_interface_cache, ref) @@ -1589,7 +1589,7 @@ struct urb { int error_count; /* (return) number of ISO errors */ void *context; /* (in) context for completion */ usb_complete_t complete; /* (in) completion routine */ - struct usb_iso_packet_descriptor iso_frame_desc[0]; + struct usb_iso_packet_descriptor iso_frame_desc[]; /* (in) ISO ONLY */ }; diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index ba4b3e3327ff..5e31740c7e40 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -153,7 +153,7 @@ struct uac2_feature_unit_descriptor { __u8 bSourceID; /* bmaControls is actually u32, * but u8 is needed for the hybrid parser */ - __u8 bmaControls[0]; /* variable length */ + __u8 bmaControls[]; /* variable length */ } __attribute__((packed)); /* 4.9.2 Class-Specific AS Interface Descriptor */ diff --git a/include/linux/usb/audio-v3.h b/include/linux/usb/audio-v3.h index 6b708434b7f9..c69a6f2e6837 100644 --- a/include/linux/usb/audio-v3.h +++ b/include/linux/usb/audio-v3.h @@ -109,7 +109,7 @@ struct uac3_feature_unit_descriptor { __u8 bSourceID; /* bmaControls is actually u32, * but u8 is needed for the hybrid parser */ - __u8 bmaControls[0]; /* variable length */ + __u8 bmaControls[]; /* variable length */ /* wFeatureDescrStr omitted */ } __attribute__((packed)); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 124462d65eac..9411c08a5c7e 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -767,7 +767,7 @@ struct usb_gadget_strings { struct usb_gadget_string_container { struct list_head list; - u8 *stash[0]; + u8 *stash[]; }; /* put descriptor for string with that id into buf (buflen >= 256) */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 712b2a603645..e12105ed3834 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -228,7 +228,7 @@ struct usb_hcd { /* The HC driver's private data is stored at the end of * this structure. */ - unsigned long hcd_priv[0] + unsigned long hcd_priv[] __attribute__ ((aligned(sizeof(s64)))); }; diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 79aab0065ec8..14ea197ce37f 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -69,7 +69,7 @@ struct usbdevfs_urb32 { compat_int_t error_count; compat_uint_t signr; compat_caddr_t usercontext; /* unused */ - struct usbdevfs_iso_packet_desc iso_frame_desc[0]; + struct usbdevfs_iso_packet_desc iso_frame_desc[]; }; struct usbdevfs_ioctl32 { -- cgit v1.2.3 From a7495c28c86ab3b31508c5754bc5fb717ab1169c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:35 +0100 Subject: efi/libstub: Simplify efi_high_alloc() and rename to efi_allocate_pages() The implementation of efi_high_alloc() uses a complicated way of traversing the memory map to find an available region that is located as close as possible to the provided upper limit, and calls AllocatePages subsequently to create the allocation at that exact address. This is precisely what the EFI_ALLOCATE_MAX_ADDRESS allocation type argument to AllocatePages() does, and considering that EFI_ALLOC_ALIGN only exceeds EFI_PAGE_SIZE on arm64, let's use AllocatePages() directly and implement the alignment using code that the compiler can remove if it does not exceed EFI_PAGE_SIZE. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 5 +- drivers/firmware/efi/libstub/fdt.c | 3 +- drivers/firmware/efi/libstub/mem.c | 102 +++++-------------------- include/linux/efi.h | 4 +- 4 files changed, 23 insertions(+), 91 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 60d13c7a2e92..7afe31357df3 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -385,8 +385,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, * so allocate enough memory for all the files. This is used * for loading multiple files. */ - status = efi_high_alloc(file_size_total, 0x1000, &file_addr, - max_addr); + status = efi_allocate_pages(file_size_total, &file_addr, max_addr); if (status != EFI_SUCCESS) { pr_efi_err("Failed to alloc highmem for files\n"); goto close_handles; @@ -536,7 +535,7 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, options_bytes++; /* NUL termination */ - status = efi_high_alloc(options_bytes, 0, &cmdline_addr, + status = efi_allocate_pages(options_bytes, &cmdline_addr, MAX_CMDLINE_ADDRESS); if (status != EFI_SUCCESS) return NULL; diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index f71cd54823b7..46cffac7a5f1 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -277,8 +277,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(void *handle, pr_efi("Exiting boot services and installing virtual address map...\n"); map.map = &memory_map; - status = efi_high_alloc(MAX_FDT_SIZE, EFI_PAGE_SIZE, - new_fdt_addr, max_addr); + status = efi_allocate_pages(MAX_FDT_SIZE, new_fdt_addr, max_addr); if (status != EFI_SUCCESS) { pr_efi_err("Unable to allocate memory for new device tree.\n"); goto fail; diff --git a/drivers/firmware/efi/libstub/mem.c b/drivers/firmware/efi/libstub/mem.c index 690648a7ca1e..5808c8764e64 100644 --- a/drivers/firmware/efi/libstub/mem.c +++ b/drivers/firmware/efi/libstub/mem.c @@ -68,100 +68,34 @@ fail: /* * Allocate at the highest possible address that is not above 'max'. */ -efi_status_t efi_high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max) +efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, + unsigned long max) { - unsigned long map_size, desc_size, buff_size; - efi_memory_desc_t *map; + efi_physical_addr_t alloc_addr = ALIGN_DOWN(max + 1, EFI_ALLOC_ALIGN) - 1; + int slack = EFI_ALLOC_ALIGN / EFI_PAGE_SIZE - 1; efi_status_t status; - unsigned long nr_pages; - u64 max_addr = 0; - int i; - struct efi_boot_memmap boot_map; - - boot_map.map = ↦ - boot_map.map_size = &map_size; - boot_map.desc_size = &desc_size; - boot_map.desc_ver = NULL; - boot_map.key_ptr = NULL; - boot_map.buff_size = &buff_size; - - status = efi_get_memory_map(&boot_map); - if (status != EFI_SUCCESS) - goto fail; - - /* - * Enforce minimum alignment that EFI or Linux requires when - * requesting a specific address. We are doing page-based (or - * larger) allocations, and both the address and size must meet - * alignment constraints. - */ - if (align < EFI_ALLOC_ALIGN) - align = EFI_ALLOC_ALIGN; size = round_up(size, EFI_ALLOC_ALIGN); - nr_pages = size / EFI_PAGE_SIZE; -again: - for (i = 0; i < map_size / desc_size; i++) { - efi_memory_desc_t *desc; - unsigned long m = (unsigned long)map; - u64 start, end; - - desc = efi_early_memdesc_ptr(m, desc_size, i); - if (desc->type != EFI_CONVENTIONAL_MEMORY) - continue; - - if (efi_soft_reserve_enabled() && - (desc->attribute & EFI_MEMORY_SP)) - continue; - - if (desc->num_pages < nr_pages) - continue; - - start = desc->phys_addr; - end = start + desc->num_pages * EFI_PAGE_SIZE; - - if (end > max) - end = max; - - if ((start + size) > end) - continue; - - if (round_down(end - size, align) < start) - continue; - - start = round_down(end - size, align); + status = efi_bs_call(allocate_pages, EFI_ALLOCATE_MAX_ADDRESS, + EFI_LOADER_DATA, size / EFI_PAGE_SIZE + slack, + &alloc_addr); + if (status != EFI_SUCCESS) + return status; - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. - */ - if (start == 0x0) - continue; + *addr = ALIGN((unsigned long)alloc_addr, EFI_ALLOC_ALIGN); - if (start > max_addr) - max_addr = start; - } + if (slack > 0) { + int l = (alloc_addr % EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE; - if (!max_addr) - status = EFI_NOT_FOUND; - else { - status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS, - EFI_LOADER_DATA, nr_pages, &max_addr); - if (status != EFI_SUCCESS) { - max = max_addr; - max_addr = 0; - goto again; + if (l) { + efi_bs_call(free_pages, alloc_addr, slack - l + 1); + slack = l - 1; } - - *addr = max_addr; + if (slack) + efi_bs_call(free_pages, *addr + size, slack); } - - efi_bs_call(free_pool, map); -fail: - return status; + return EFI_SUCCESS; } - /* * Allocate at the lowest possible address that is not below 'min'. */ diff --git a/include/linux/efi.h b/include/linux/efi.h index 7efd7072cca5..7e231c3cfb6f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1508,8 +1508,8 @@ efi_status_t efi_low_alloc(unsigned long size, unsigned long align, return efi_low_alloc_above(size, align, addr, 0x8); } -efi_status_t efi_high_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long max); +efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, + unsigned long max); efi_status_t efi_relocate_kernel(unsigned long *image_addr, unsigned long image_size, -- cgit v1.2.3 From a46a290a01149120f40f83a694d3e6041bcf8f70 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:37 +0100 Subject: efi/libstub: Use consistent type names for file I/O protocols Align the naming of efi_file_io_interface_t and efi_file_handle_t with the UEFI spec, and call them efi_simple_file_system_protocol_t and efi_file_protocol_t, respectively, using the same convention we use for all other type definitions that originate in the UEFI spec. While at it, move the definitions to efistub.h, so they are only seen by code that needs them. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efi-stub-helper.c | 16 +++---- drivers/firmware/efi/libstub/efistub.h | 63 ++++++++++++++++++++++++++ include/linux/efi.h | 60 +----------------------- 3 files changed, 72 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 7afe31357df3..6db91655c743 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -54,7 +54,7 @@ bool __pure __efi_soft_reserve_enabled(void) } struct file_info { - efi_file_handle_t *handle; + efi_file_protocol_t *handle; u64 size; }; @@ -113,7 +113,7 @@ unsigned long get_dram_base(void) static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16, void **handle, u64 *file_sz) { - efi_file_handle_t *h, *fh = __fh; + efi_file_protocol_t *h, *fh = __fh; efi_file_info_t *info; efi_status_t status; efi_guid_t info_guid = EFI_FILE_INFO_ID; @@ -159,22 +159,22 @@ grow: return status; } -static efi_status_t efi_file_read(efi_file_handle_t *handle, +static efi_status_t efi_file_read(efi_file_protocol_t *handle, unsigned long *size, void *addr) { return handle->read(handle, size, addr); } -static efi_status_t efi_file_close(efi_file_handle_t *handle) +static efi_status_t efi_file_close(efi_file_protocol_t *handle) { return handle->close(handle); } static efi_status_t efi_open_volume(efi_loaded_image_t *image, - efi_file_handle_t **__fh) + efi_file_protocol_t **__fh) { - efi_file_io_interface_t *io; - efi_file_handle_t *fh; + efi_simple_file_system_protocol_t *io; + efi_file_protocol_t *fh; efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID; efi_status_t status; efi_handle_t handle = image->device_handle; @@ -282,7 +282,7 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image, struct file_info *files; unsigned long file_addr; u64 file_size_total; - efi_file_handle_t *fh = NULL; + efi_file_protocol_t *fh = NULL; efi_status_t status; int nr_files; char *str; diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 55de118e8267..79cdb219f439 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -106,4 +106,67 @@ union efi_uga_draw_protocol { } mixed_mode; }; +typedef struct efi_loaded_image { + u32 revision; + efi_handle_t parent_handle; + efi_system_table_t *system_table; + efi_handle_t device_handle; + void *file_path; + void *reserved; + u32 load_options_size; + void *load_options; + void *image_base; + __aligned_u64 image_size; + unsigned int image_code_type; + unsigned int image_data_type; + efi_status_t (__efiapi *unload)(efi_handle_t image_handle); +} efi_loaded_image_t; + +typedef struct { + u64 size; + u64 file_size; + u64 phys_size; + efi_time_t create_time; + efi_time_t last_access_time; + efi_time_t modification_time; + __aligned_u64 attribute; + efi_char16_t filename[1]; +} efi_file_info_t; + +typedef struct efi_file_protocol efi_file_protocol_t; + +struct efi_file_protocol { + u64 revision; + efi_status_t (__efiapi *open) (efi_file_protocol_t *, + efi_file_protocol_t **, + efi_char16_t *, u64, u64); + efi_status_t (__efiapi *close) (efi_file_protocol_t *); + efi_status_t (__efiapi *delete) (efi_file_protocol_t *); + efi_status_t (__efiapi *read) (efi_file_protocol_t *, + unsigned long *, void *); + efi_status_t (__efiapi *write) (efi_file_protocol_t *, + unsigned long, void *); + efi_status_t (__efiapi *get_position)(efi_file_protocol_t *, u64 *); + efi_status_t (__efiapi *set_position)(efi_file_protocol_t *, u64); + efi_status_t (__efiapi *get_info) (efi_file_protocol_t *, + efi_guid_t *, unsigned long *, + void *); + efi_status_t (__efiapi *set_info) (efi_file_protocol_t *, + efi_guid_t *, unsigned long, + void *); + efi_status_t (__efiapi *flush) (efi_file_protocol_t *); +}; + +typedef struct efi_simple_file_system_protocol efi_simple_file_system_protocol_t; + +struct efi_simple_file_system_protocol { + u64 revision; + int (__efiapi *open_volume)(efi_simple_file_system_protocol_t *, + efi_file_protocol_t **); +}; + +#define EFI_FILE_MODE_READ 0x0000000000000001 +#define EFI_FILE_MODE_WRITE 0x0000000000000002 +#define EFI_FILE_MODE_CREATE 0x8000000000000000 + #endif diff --git a/include/linux/efi.h b/include/linux/efi.h index 7e231c3cfb6f..2b228df18407 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -796,65 +796,7 @@ struct efi_fdt_params { u32 desc_ver; }; -typedef struct { - u32 revision; - efi_handle_t parent_handle; - efi_system_table_t *system_table; - efi_handle_t device_handle; - void *file_path; - void *reserved; - u32 load_options_size; - void *load_options; - void *image_base; - __aligned_u64 image_size; - unsigned int image_code_type; - unsigned int image_data_type; - efi_status_t ( __efiapi *unload)(efi_handle_t image_handle); -} efi_loaded_image_t; - -typedef struct { - u64 size; - u64 file_size; - u64 phys_size; - efi_time_t create_time; - efi_time_t last_access_time; - efi_time_t modification_time; - __aligned_u64 attribute; - efi_char16_t filename[1]; -} efi_file_info_t; - -typedef struct efi_file_handle efi_file_handle_t; - -struct efi_file_handle { - u64 revision; - efi_status_t (__efiapi *open)(efi_file_handle_t *, - efi_file_handle_t **, - efi_char16_t *, u64, u64); - efi_status_t (__efiapi *close)(efi_file_handle_t *); - void *delete; - efi_status_t (__efiapi *read)(efi_file_handle_t *, - unsigned long *, void *); - void *write; - void *get_position; - void *set_position; - efi_status_t (__efiapi *get_info)(efi_file_handle_t *, - efi_guid_t *, unsigned long *, - void *); - void *set_info; - void *flush; -}; - -typedef struct efi_file_io_interface efi_file_io_interface_t; - -struct efi_file_io_interface { - u64 revision; - int (__efiapi *open_volume)(efi_file_io_interface_t *, - efi_file_handle_t **); -}; - -#define EFI_FILE_MODE_READ 0x0000000000000001 -#define EFI_FILE_MODE_WRITE 0x0000000000000002 -#define EFI_FILE_MODE_CREATE 0x8000000000000000 +typedef struct efi_loaded_image efi_loaded_image_t; typedef struct { u32 version; -- cgit v1.2.3 From 8166ec091573fad528d884ede291fd1ec02d0298 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:38 +0100 Subject: efi/libstub: Move stub specific declarations into efistub.h Move all the declarations that are only used in stub code from linux/efi.h to efistub.h which is only included locally. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/efistub.h | 507 +++++++++++++++++++++++++++++++-- include/linux/efi.h | 456 +---------------------------- 2 files changed, 479 insertions(+), 484 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 79cdb219f439..8bb46c1fd2cd 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -43,34 +43,6 @@ extern __pure efi_system_table_t *efi_system_table(void); #define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg) -void efi_char16_printk(efi_char16_t *); -void efi_char16_printk(efi_char16_t *); - -unsigned long get_dram_base(void); - -efi_status_t allocate_new_fdt_and_exit_boot(void *handle, - unsigned long *new_fdt_addr, - unsigned long max_addr, - u64 initrd_addr, u64 initrd_size, - char *cmdline_ptr, - unsigned long fdt_addr, - unsigned long fdt_size); - -void *get_fdt(unsigned long *fdt_size); - -void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, - unsigned long desc_size, efi_memory_desc_t *runtime_map, - int *count); - -efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); - -efi_status_t efi_random_alloc(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long random_seed); - -efi_status_t check_platform_features(void); - -void *get_efi_config_table(efi_guid_t guid); - /* Helper macros for the usual case of using simple C variables: */ #ifndef fdt_setprop_inplace_var #define fdt_setprop_inplace_var(fdt, node_offset, name, var) \ @@ -90,6 +62,156 @@ void *get_efi_config_table(efi_guid_t guid); efi_rt_call(set_variable, (efi_char16_t *)(name), \ (efi_guid_t *)(vendor), __VA_ARGS__) +#define efi_get_handle_at(array, idx) \ + (efi_is_native() ? (array)[idx] \ + : (efi_handle_t)(unsigned long)((u32 *)(array))[idx]) + +#define efi_get_handle_num(size) \ + ((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32))) + +#define for_each_efi_handle(handle, array, size, i) \ + for (i = 0; \ + i < efi_get_handle_num(size) && \ + ((handle = efi_get_handle_at((array), i)) || true); \ + i++) + +/* + * Allocation types for calls to boottime->allocate_pages. + */ +#define EFI_ALLOCATE_ANY_PAGES 0 +#define EFI_ALLOCATE_MAX_ADDRESS 1 +#define EFI_ALLOCATE_ADDRESS 2 +#define EFI_MAX_ALLOCATE_TYPE 3 + +/* + * The type of search to perform when calling boottime->locate_handle + */ +#define EFI_LOCATE_ALL_HANDLES 0 +#define EFI_LOCATE_BY_REGISTER_NOTIFY 1 +#define EFI_LOCATE_BY_PROTOCOL 2 + +struct efi_boot_memmap { + efi_memory_desc_t **map; + unsigned long *map_size; + unsigned long *desc_size; + u32 *desc_ver; + unsigned long *key_ptr; + unsigned long *buff_size; +}; + +/* + * EFI Boot Services table + */ +union efi_boot_services { + struct { + efi_table_hdr_t hdr; + void *raise_tpl; + void *restore_tpl; + efi_status_t (__efiapi *allocate_pages)(int, int, unsigned long, + efi_physical_addr_t *); + efi_status_t (__efiapi *free_pages)(efi_physical_addr_t, + unsigned long); + efi_status_t (__efiapi *get_memory_map)(unsigned long *, void *, + unsigned long *, + unsigned long *, u32 *); + efi_status_t (__efiapi *allocate_pool)(int, unsigned long, + void **); + efi_status_t (__efiapi *free_pool)(void *); + void *create_event; + void *set_timer; + void *wait_for_event; + void *signal_event; + void *close_event; + void *check_event; + void *install_protocol_interface; + void *reinstall_protocol_interface; + void *uninstall_protocol_interface; + efi_status_t (__efiapi *handle_protocol)(efi_handle_t, + efi_guid_t *, void **); + void *__reserved; + void *register_protocol_notify; + efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *, + void *, unsigned long *, + efi_handle_t *); + void *locate_device_path; + efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *, + void *); + void *load_image; + void *start_image; + void *exit; + void *unload_image; + efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, + unsigned long); + void *get_next_monotonic_count; + void *stall; + void *set_watchdog_timer; + void *connect_controller; + efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, + efi_handle_t, + efi_handle_t); + void *open_protocol; + void *close_protocol; + void *open_protocol_information; + void *protocols_per_handle; + void *locate_handle_buffer; + efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *, + void **); + void *install_multiple_protocol_interfaces; + void *uninstall_multiple_protocol_interfaces; + void *calculate_crc32; + void *copy_mem; + void *set_mem; + void *create_event_ex; + }; + struct { + efi_table_hdr_t hdr; + u32 raise_tpl; + u32 restore_tpl; + u32 allocate_pages; + u32 free_pages; + u32 get_memory_map; + u32 allocate_pool; + u32 free_pool; + u32 create_event; + u32 set_timer; + u32 wait_for_event; + u32 signal_event; + u32 close_event; + u32 check_event; + u32 install_protocol_interface; + u32 reinstall_protocol_interface; + u32 uninstall_protocol_interface; + u32 handle_protocol; + u32 __reserved; + u32 register_protocol_notify; + u32 locate_handle; + u32 locate_device_path; + u32 install_configuration_table; + u32 load_image; + u32 start_image; + u32 exit; + u32 unload_image; + u32 exit_boot_services; + u32 get_next_monotonic_count; + u32 stall; + u32 set_watchdog_timer; + u32 connect_controller; + u32 disconnect_controller; + u32 open_protocol; + u32 close_protocol; + u32 open_protocol_information; + u32 protocols_per_handle; + u32 locate_handle_buffer; + u32 locate_protocol; + u32 install_multiple_protocol_interfaces; + u32 uninstall_multiple_protocol_interfaces; + u32 calculate_crc32; + u32 copy_mem; + u32 set_mem; + u32 create_event_ex; + } mixed_mode; +}; + typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t; union efi_uga_draw_protocol { @@ -106,7 +228,81 @@ union efi_uga_draw_protocol { } mixed_mode; }; -typedef struct efi_loaded_image { +union efi_simple_text_output_protocol { + struct { + void *reset; + efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *, + efi_char16_t *); + void *test_string; + }; + struct { + u32 reset; + u32 output_string; + u32 test_string; + } mixed_mode; +}; + +#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 +#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 +#define PIXEL_BIT_MASK 2 +#define PIXEL_BLT_ONLY 3 +#define PIXEL_FORMAT_MAX 4 + +typedef struct { + u32 red_mask; + u32 green_mask; + u32 blue_mask; + u32 reserved_mask; +} efi_pixel_bitmask_t; + +typedef struct { + u32 version; + u32 horizontal_resolution; + u32 vertical_resolution; + int pixel_format; + efi_pixel_bitmask_t pixel_information; + u32 pixels_per_scan_line; +} efi_graphics_output_mode_info_t; + +typedef union efi_graphics_output_protocol_mode efi_graphics_output_protocol_mode_t; + +union efi_graphics_output_protocol_mode { + struct { + u32 max_mode; + u32 mode; + efi_graphics_output_mode_info_t *info; + unsigned long size_of_info; + efi_physical_addr_t frame_buffer_base; + unsigned long frame_buffer_size; + }; + struct { + u32 max_mode; + u32 mode; + u32 info; + u32 size_of_info; + u64 frame_buffer_base; + u32 frame_buffer_size; + } mixed_mode; +}; + +typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t; + +union efi_graphics_output_protocol { + struct { + void *query_mode; + void *set_mode; + void *blt; + efi_graphics_output_protocol_mode_t *mode; + }; + struct { + u32 query_mode; + u32 set_mode; + u32 blt; + u32 mode; + } mixed_mode; +}; + +typedef struct { u32 revision; efi_handle_t parent_handle; efi_system_table_t *system_table; @@ -169,4 +365,257 @@ struct efi_simple_file_system_protocol { #define EFI_FILE_MODE_WRITE 0x0000000000000002 #define EFI_FILE_MODE_CREATE 0x8000000000000000 +typedef enum { + EfiPciIoWidthUint8, + EfiPciIoWidthUint16, + EfiPciIoWidthUint32, + EfiPciIoWidthUint64, + EfiPciIoWidthFifoUint8, + EfiPciIoWidthFifoUint16, + EfiPciIoWidthFifoUint32, + EfiPciIoWidthFifoUint64, + EfiPciIoWidthFillUint8, + EfiPciIoWidthFillUint16, + EfiPciIoWidthFillUint32, + EfiPciIoWidthFillUint64, + EfiPciIoWidthMaximum +} EFI_PCI_IO_PROTOCOL_WIDTH; + +typedef enum { + EfiPciIoAttributeOperationGet, + EfiPciIoAttributeOperationSet, + EfiPciIoAttributeOperationEnable, + EfiPciIoAttributeOperationDisable, + EfiPciIoAttributeOperationSupported, + EfiPciIoAttributeOperationMaximum +} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION; + +typedef struct { + u32 read; + u32 write; +} efi_pci_io_protocol_access_32_t; + +typedef union efi_pci_io_protocol efi_pci_io_protocol_t; + +typedef +efi_status_t (__efiapi *efi_pci_io_protocol_cfg_t)(efi_pci_io_protocol_t *, + EFI_PCI_IO_PROTOCOL_WIDTH, + u32 offset, + unsigned long count, + void *buffer); + +typedef struct { + void *read; + void *write; +} efi_pci_io_protocol_access_t; + +typedef struct { + efi_pci_io_protocol_cfg_t read; + efi_pci_io_protocol_cfg_t write; +} efi_pci_io_protocol_config_access_t; + +union efi_pci_io_protocol { + struct { + void *poll_mem; + void *poll_io; + efi_pci_io_protocol_access_t mem; + efi_pci_io_protocol_access_t io; + efi_pci_io_protocol_config_access_t pci; + void *copy_mem; + void *map; + void *unmap; + void *allocate_buffer; + void *free_buffer; + void *flush; + efi_status_t (__efiapi *get_location)(efi_pci_io_protocol_t *, + unsigned long *segment_nr, + unsigned long *bus_nr, + unsigned long *device_nr, + unsigned long *func_nr); + void *attributes; + void *get_bar_attributes; + void *set_bar_attributes; + uint64_t romsize; + void *romimage; + }; + struct { + u32 poll_mem; + u32 poll_io; + efi_pci_io_protocol_access_32_t mem; + efi_pci_io_protocol_access_32_t io; + efi_pci_io_protocol_access_32_t pci; + u32 copy_mem; + u32 map; + u32 unmap; + u32 allocate_buffer; + u32 free_buffer; + u32 flush; + u32 get_location; + u32 attributes; + u32 get_bar_attributes; + u32 set_bar_attributes; + u64 romsize; + u32 romimage; + } mixed_mode; +}; + +#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001 +#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002 +#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004 +#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008 +#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010 +#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020 +#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080 +#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200 +#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800 +#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000 +#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000 +#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000 +#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000 +#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000 +#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000 +#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000 + +struct efi_dev_path; + +typedef union apple_properties_protocol apple_properties_protocol_t; + +union apple_properties_protocol { + struct { + unsigned long version; + efi_status_t (__efiapi *get)(apple_properties_protocol_t *, + struct efi_dev_path *, + efi_char16_t *, void *, u32 *); + efi_status_t (__efiapi *set)(apple_properties_protocol_t *, + struct efi_dev_path *, + efi_char16_t *, void *, u32); + efi_status_t (__efiapi *del)(apple_properties_protocol_t *, + struct efi_dev_path *, + efi_char16_t *); + efi_status_t (__efiapi *get_all)(apple_properties_protocol_t *, + void *buffer, u32 *); + }; + struct { + u32 version; + u32 get; + u32 set; + u32 del; + u32 get_all; + } mixed_mode; +}; + +typedef u32 efi_tcg2_event_log_format; + +typedef union efi_tcg2_protocol efi_tcg2_protocol_t; + +union efi_tcg2_protocol { + struct { + void *get_capability; + efi_status_t (__efiapi *get_event_log)(efi_handle_t, + efi_tcg2_event_log_format, + efi_physical_addr_t *, + efi_physical_addr_t *, + efi_bool_t *); + void *hash_log_extend_event; + void *submit_command; + void *get_active_pcr_banks; + void *set_active_pcr_banks; + void *get_result_of_set_active_pcr_banks; + }; + struct { + u32 get_capability; + u32 get_event_log; + u32 hash_log_extend_event; + u32 submit_command; + u32 get_active_pcr_banks; + u32 set_active_pcr_banks; + u32 get_result_of_set_active_pcr_banks; + } mixed_mode; +}; + +void efi_pci_disable_bridge_busmaster(void); + +typedef efi_status_t (*efi_exit_boot_map_processing)( + struct efi_boot_memmap *map, + void *priv); + +efi_status_t efi_exit_boot_services(void *handle, + struct efi_boot_memmap *map, + void *priv, + efi_exit_boot_map_processing priv_func); + +void efi_char16_printk(efi_char16_t *); + +unsigned long get_dram_base(void); + +efi_status_t allocate_new_fdt_and_exit_boot(void *handle, + unsigned long *new_fdt_addr, + unsigned long max_addr, + u64 initrd_addr, u64 initrd_size, + char *cmdline_ptr, + unsigned long fdt_addr, + unsigned long fdt_size); + +void *get_fdt(unsigned long *fdt_size); + +void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, + unsigned long desc_size, efi_memory_desc_t *runtime_map, + int *count); + +efi_status_t efi_get_random_bytes(unsigned long size, u8 *out); + +efi_status_t efi_random_alloc(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long random_seed); + +efi_status_t check_platform_features(void); + +void *get_efi_config_table(efi_guid_t guid); + +void efi_printk(char *str); + +void efi_free(unsigned long size, unsigned long addr); + +char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); + +efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); + +efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, + unsigned long *addr, unsigned long min); + +static inline +efi_status_t efi_low_alloc(unsigned long size, unsigned long align, + unsigned long *addr) +{ + /* + * Don't allocate at 0x0. It will confuse code that + * checks pointers against NULL. Skip the first 8 + * bytes so we start at a nice even number. + */ + return efi_low_alloc_above(size, align, addr, 0x8); +} + +efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, + unsigned long max); + +efi_status_t efi_relocate_kernel(unsigned long *image_addr, + unsigned long image_size, + unsigned long alloc_size, + unsigned long preferred_addr, + unsigned long alignment, + unsigned long min_addr); + +efi_status_t handle_cmdline_files(efi_loaded_image_t *image, + char *cmd_line, char *option_string, + unsigned long max_addr, + unsigned long *load_addr, + unsigned long *load_size); + +efi_status_t efi_parse_options(char const *cmdline); + +efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, + unsigned long size); + #endif diff --git a/include/linux/efi.h b/include/linux/efi.h index 2b228df18407..0e047d2738cd 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -56,19 +56,6 @@ typedef void *efi_handle_t; #define __efiapi #endif -#define efi_get_handle_at(array, idx) \ - (efi_is_native() ? (array)[idx] \ - : (efi_handle_t)(unsigned long)((u32 *)(array))[idx]) - -#define efi_get_handle_num(size) \ - ((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32))) - -#define for_each_efi_handle(handle, array, size, i) \ - for (i = 0; \ - i < efi_get_handle_num(size) && \ - ((handle = efi_get_handle_at((array), i)) || true); \ - i++) - /* * The UEFI spec and EDK2 reference implementation both define EFI_GUID as * struct { u32 a; u16; b; u16 c; u8 d[8]; }; and so the implied alignment @@ -157,15 +144,6 @@ typedef struct { u32 imagesize; } efi_capsule_header_t; -struct efi_boot_memmap { - efi_memory_desc_t **map; - unsigned long *map_size; - unsigned long *desc_size; - u32 *desc_ver; - unsigned long *key_ptr; - unsigned long *buff_size; -}; - /* * EFI capsule flags */ @@ -187,14 +165,6 @@ struct capsule_info { int __efi_capsule_setup_info(struct capsule_info *cap_info); -/* - * Allocation types for calls to boottime->allocate_pages. - */ -#define EFI_ALLOCATE_ANY_PAGES 0 -#define EFI_ALLOCATE_MAX_ADDRESS 1 -#define EFI_ALLOCATE_ADDRESS 2 -#define EFI_MAX_ALLOCATE_TYPE 3 - typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg); /* @@ -224,291 +194,7 @@ typedef struct { u8 sets_to_zero; } efi_time_cap_t; -typedef struct { - efi_table_hdr_t hdr; - u32 raise_tpl; - u32 restore_tpl; - u32 allocate_pages; - u32 free_pages; - u32 get_memory_map; - u32 allocate_pool; - u32 free_pool; - u32 create_event; - u32 set_timer; - u32 wait_for_event; - u32 signal_event; - u32 close_event; - u32 check_event; - u32 install_protocol_interface; - u32 reinstall_protocol_interface; - u32 uninstall_protocol_interface; - u32 handle_protocol; - u32 __reserved; - u32 register_protocol_notify; - u32 locate_handle; - u32 locate_device_path; - u32 install_configuration_table; - u32 load_image; - u32 start_image; - u32 exit; - u32 unload_image; - u32 exit_boot_services; - u32 get_next_monotonic_count; - u32 stall; - u32 set_watchdog_timer; - u32 connect_controller; - u32 disconnect_controller; - u32 open_protocol; - u32 close_protocol; - u32 open_protocol_information; - u32 protocols_per_handle; - u32 locate_handle_buffer; - u32 locate_protocol; - u32 install_multiple_protocol_interfaces; - u32 uninstall_multiple_protocol_interfaces; - u32 calculate_crc32; - u32 copy_mem; - u32 set_mem; - u32 create_event_ex; -} __packed efi_boot_services_32_t; - -/* - * EFI Boot Services table - */ -typedef union { - struct { - efi_table_hdr_t hdr; - void *raise_tpl; - void *restore_tpl; - efi_status_t (__efiapi *allocate_pages)(int, int, unsigned long, - efi_physical_addr_t *); - efi_status_t (__efiapi *free_pages)(efi_physical_addr_t, - unsigned long); - efi_status_t (__efiapi *get_memory_map)(unsigned long *, void *, - unsigned long *, - unsigned long *, u32 *); - efi_status_t (__efiapi *allocate_pool)(int, unsigned long, - void **); - efi_status_t (__efiapi *free_pool)(void *); - void *create_event; - void *set_timer; - void *wait_for_event; - void *signal_event; - void *close_event; - void *check_event; - void *install_protocol_interface; - void *reinstall_protocol_interface; - void *uninstall_protocol_interface; - efi_status_t (__efiapi *handle_protocol)(efi_handle_t, - efi_guid_t *, void **); - void *__reserved; - void *register_protocol_notify; - efi_status_t (__efiapi *locate_handle)(int, efi_guid_t *, - void *, unsigned long *, - efi_handle_t *); - void *locate_device_path; - efi_status_t (__efiapi *install_configuration_table)(efi_guid_t *, - void *); - void *load_image; - void *start_image; - void *exit; - void *unload_image; - efi_status_t (__efiapi *exit_boot_services)(efi_handle_t, - unsigned long); - void *get_next_monotonic_count; - void *stall; - void *set_watchdog_timer; - void *connect_controller; - efi_status_t (__efiapi *disconnect_controller)(efi_handle_t, - efi_handle_t, - efi_handle_t); - void *open_protocol; - void *close_protocol; - void *open_protocol_information; - void *protocols_per_handle; - void *locate_handle_buffer; - efi_status_t (__efiapi *locate_protocol)(efi_guid_t *, void *, - void **); - void *install_multiple_protocol_interfaces; - void *uninstall_multiple_protocol_interfaces; - void *calculate_crc32; - void *copy_mem; - void *set_mem; - void *create_event_ex; - }; - efi_boot_services_32_t mixed_mode; -} efi_boot_services_t; - -typedef enum { - EfiPciIoWidthUint8, - EfiPciIoWidthUint16, - EfiPciIoWidthUint32, - EfiPciIoWidthUint64, - EfiPciIoWidthFifoUint8, - EfiPciIoWidthFifoUint16, - EfiPciIoWidthFifoUint32, - EfiPciIoWidthFifoUint64, - EfiPciIoWidthFillUint8, - EfiPciIoWidthFillUint16, - EfiPciIoWidthFillUint32, - EfiPciIoWidthFillUint64, - EfiPciIoWidthMaximum -} EFI_PCI_IO_PROTOCOL_WIDTH; - -typedef enum { - EfiPciIoAttributeOperationGet, - EfiPciIoAttributeOperationSet, - EfiPciIoAttributeOperationEnable, - EfiPciIoAttributeOperationDisable, - EfiPciIoAttributeOperationSupported, - EfiPciIoAttributeOperationMaximum -} EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION; - -typedef struct { - u32 read; - u32 write; -} efi_pci_io_protocol_access_32_t; - -typedef union efi_pci_io_protocol efi_pci_io_protocol_t; - -typedef -efi_status_t (__efiapi *efi_pci_io_protocol_cfg_t)(efi_pci_io_protocol_t *, - EFI_PCI_IO_PROTOCOL_WIDTH, - u32 offset, - unsigned long count, - void *buffer); - -typedef struct { - void *read; - void *write; -} efi_pci_io_protocol_access_t; - -typedef struct { - efi_pci_io_protocol_cfg_t read; - efi_pci_io_protocol_cfg_t write; -} efi_pci_io_protocol_config_access_t; - -union efi_pci_io_protocol { - struct { - void *poll_mem; - void *poll_io; - efi_pci_io_protocol_access_t mem; - efi_pci_io_protocol_access_t io; - efi_pci_io_protocol_config_access_t pci; - void *copy_mem; - void *map; - void *unmap; - void *allocate_buffer; - void *free_buffer; - void *flush; - efi_status_t (__efiapi *get_location)(efi_pci_io_protocol_t *, - unsigned long *segment_nr, - unsigned long *bus_nr, - unsigned long *device_nr, - unsigned long *func_nr); - void *attributes; - void *get_bar_attributes; - void *set_bar_attributes; - uint64_t romsize; - void *romimage; - }; - struct { - u32 poll_mem; - u32 poll_io; - efi_pci_io_protocol_access_32_t mem; - efi_pci_io_protocol_access_32_t io; - efi_pci_io_protocol_access_32_t pci; - u32 copy_mem; - u32 map; - u32 unmap; - u32 allocate_buffer; - u32 free_buffer; - u32 flush; - u32 get_location; - u32 attributes; - u32 get_bar_attributes; - u32 set_bar_attributes; - u64 romsize; - u32 romimage; - } mixed_mode; -}; - -#define EFI_PCI_IO_ATTRIBUTE_ISA_MOTHERBOARD_IO 0x0001 -#define EFI_PCI_IO_ATTRIBUTE_ISA_IO 0x0002 -#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO 0x0004 -#define EFI_PCI_IO_ATTRIBUTE_VGA_MEMORY 0x0008 -#define EFI_PCI_IO_ATTRIBUTE_VGA_IO 0x0010 -#define EFI_PCI_IO_ATTRIBUTE_IDE_PRIMARY_IO 0x0020 -#define EFI_PCI_IO_ATTRIBUTE_IDE_SECONDARY_IO 0x0040 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY_WRITE_COMBINE 0x0080 -#define EFI_PCI_IO_ATTRIBUTE_IO 0x0100 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY 0x0200 -#define EFI_PCI_IO_ATTRIBUTE_BUS_MASTER 0x0400 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY_CACHED 0x0800 -#define EFI_PCI_IO_ATTRIBUTE_MEMORY_DISABLE 0x1000 -#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_DEVICE 0x2000 -#define EFI_PCI_IO_ATTRIBUTE_EMBEDDED_ROM 0x4000 -#define EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE 0x8000 -#define EFI_PCI_IO_ATTRIBUTE_ISA_IO_16 0x10000 -#define EFI_PCI_IO_ATTRIBUTE_VGA_PALETTE_IO_16 0x20000 -#define EFI_PCI_IO_ATTRIBUTE_VGA_IO_16 0x40000 - -struct efi_dev_path; - -typedef union apple_properties_protocol apple_properties_protocol_t; - -union apple_properties_protocol { - struct { - unsigned long version; - efi_status_t (__efiapi *get)(apple_properties_protocol_t *, - struct efi_dev_path *, - efi_char16_t *, void *, u32 *); - efi_status_t (__efiapi *set)(apple_properties_protocol_t *, - struct efi_dev_path *, - efi_char16_t *, void *, u32); - efi_status_t (__efiapi *del)(apple_properties_protocol_t *, - struct efi_dev_path *, - efi_char16_t *); - efi_status_t (__efiapi *get_all)(apple_properties_protocol_t *, - void *buffer, u32 *); - }; - struct { - u32 version; - u32 get; - u32 set; - u32 del; - u32 get_all; - } mixed_mode; -}; - -typedef u32 efi_tcg2_event_log_format; - -typedef union efi_tcg2_protocol efi_tcg2_protocol_t; - -union efi_tcg2_protocol { - struct { - void *get_capability; - efi_status_t (__efiapi *get_event_log)(efi_handle_t, - efi_tcg2_event_log_format, - efi_physical_addr_t *, - efi_physical_addr_t *, - efi_bool_t *); - void *hash_log_extend_event; - void *submit_command; - void *get_active_pcr_banks; - void *set_active_pcr_banks; - void *get_result_of_set_active_pcr_banks; - }; - struct { - u32 get_capability; - u32 get_event_log; - u32 hash_log_extend_event; - u32 submit_command; - u32 get_active_pcr_banks; - u32 set_active_pcr_banks; - u32 get_result_of_set_active_pcr_banks; - } mixed_mode; -}; +typedef union efi_boot_services efi_boot_services_t; /* * Types and defines for EFI ResetSystem @@ -796,8 +482,6 @@ struct efi_fdt_params { u32 desc_ver; }; -typedef struct efi_loaded_image efi_loaded_image_t; - typedef struct { u32 version; u32 length; @@ -1130,13 +814,6 @@ extern int efi_status_to_err(efi_status_t status); */ #define EFI_VARIABLE_GUID_LEN UUID_STRING_LEN -/* - * The type of search to perform when calling boottime->locate_handle - */ -#define EFI_LOCATE_ALL_HANDLES 0 -#define EFI_LOCATE_BY_REGISTER_NOTIFY 1 -#define EFI_LOCATE_BY_PROTOCOL 2 - /* * EFI Device Path information */ @@ -1254,80 +931,6 @@ struct efivar_entry { bool deleting; }; -union efi_simple_text_output_protocol { - struct { - void *reset; - efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *, - efi_char16_t *); - void *test_string; - }; - struct { - u32 reset; - u32 output_string; - u32 test_string; - } mixed_mode; -}; - -#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0 -#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1 -#define PIXEL_BIT_MASK 2 -#define PIXEL_BLT_ONLY 3 -#define PIXEL_FORMAT_MAX 4 - -typedef struct { - u32 red_mask; - u32 green_mask; - u32 blue_mask; - u32 reserved_mask; -} efi_pixel_bitmask_t; - -typedef struct { - u32 version; - u32 horizontal_resolution; - u32 vertical_resolution; - int pixel_format; - efi_pixel_bitmask_t pixel_information; - u32 pixels_per_scan_line; -} efi_graphics_output_mode_info_t; - -typedef union efi_graphics_output_protocol_mode efi_graphics_output_protocol_mode_t; - -union efi_graphics_output_protocol_mode { - struct { - u32 max_mode; - u32 mode; - efi_graphics_output_mode_info_t *info; - unsigned long size_of_info; - efi_physical_addr_t frame_buffer_base; - unsigned long frame_buffer_size; - }; - struct { - u32 max_mode; - u32 mode; - u32 info; - u32 size_of_info; - u64 frame_buffer_base; - u32 frame_buffer_size; - } mixed_mode; -}; - -typedef union efi_graphics_output_protocol efi_graphics_output_protocol_t; - -union efi_graphics_output_protocol { - struct { - void *query_mode; - void *set_mode; - void *blt; - efi_graphics_output_protocol_mode_t *mode; - }; - struct { - u32 query_mode; - u32 set_mode; - u32 blt; - u32 mode; - } mixed_mode; -}; - extern struct list_head efivar_sysfs_list; static inline void @@ -1425,52 +1028,6 @@ static inline int efi_runtime_map_copy(void *buf, size_t bufsz) #endif -/* prototypes shared between arch specific and generic stub code */ - -void efi_printk(char *str); - -void efi_free(unsigned long size, unsigned long addr); - -char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len); - -efi_status_t efi_get_memory_map(struct efi_boot_memmap *map); - -efi_status_t efi_low_alloc_above(unsigned long size, unsigned long align, - unsigned long *addr, unsigned long min); - -static inline -efi_status_t efi_low_alloc(unsigned long size, unsigned long align, - unsigned long *addr) -{ - /* - * Don't allocate at 0x0. It will confuse code that - * checks pointers against NULL. Skip the first 8 - * bytes so we start at a nice even number. - */ - return efi_low_alloc_above(size, align, addr, 0x8); -} - -efi_status_t efi_allocate_pages(unsigned long size, unsigned long *addr, - unsigned long max); - -efi_status_t efi_relocate_kernel(unsigned long *image_addr, - unsigned long image_size, - unsigned long alloc_size, - unsigned long preferred_addr, - unsigned long alignment, - unsigned long min_addr); - -efi_status_t handle_cmdline_files(efi_loaded_image_t *image, - char *cmd_line, char *option_string, - unsigned long max_addr, - unsigned long *load_addr, - unsigned long *load_size); - -efi_status_t efi_parse_options(char const *cmdline); - -efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, - unsigned long size); - #ifdef CONFIG_EFI extern bool efi_runtime_disabled(void); #else @@ -1548,15 +1105,6 @@ void efi_retrieve_tpm2_eventlog(void); arch_efi_call_virt_teardown(); \ }) -typedef efi_status_t (*efi_exit_boot_map_processing)( - struct efi_boot_memmap *map, - void *priv); - -efi_status_t efi_exit_boot_services(void *handle, - struct efi_boot_memmap *map, - void *priv, - efi_exit_boot_map_processing priv_func); - #define EFI_RANDOM_SEED_SIZE 64U struct linux_efi_random_seed { @@ -1643,6 +1191,4 @@ struct linux_efi_memreserve { #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \ / sizeof(((struct linux_efi_memreserve *)0)->entry[0])) -void efi_pci_disable_bridge_busmaster(void); - #endif /* _LINUX_EFI_H */ -- cgit v1.2.3 From 2931d526d5674940d916a4b513a681ee3562e574 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 17:02:48 +0100 Subject: efi/libstub: Make the LoadFile EFI protocol accessible Add the protocol definitions, GUIDs and mixed mode glue so that the EFI loadfile protocol can be used from the stub. This will be used in a future patch to load the initrd. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 4 ++++ drivers/firmware/efi/libstub/efistub.h | 14 ++++++++++++++ include/linux/efi.h | 2 ++ 3 files changed, 20 insertions(+) (limited to 'include') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 9ced980b123b..fcb21e3d13c5 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -291,6 +291,10 @@ static inline void *efi64_zero_upper(void *p) ((protocol), efi64_zero_upper(seg), efi64_zero_upper(bus), \ efi64_zero_upper(dev), efi64_zero_upper(func)) +/* LoadFile */ +#define __efi64_argmap_load_file(protocol, path, policy, bufsize, buf) \ + ((protocol), (path), (policy), efi64_zero_upper(bufsize), (buf)) + /* * The macros below handle the plumbing for the argument mapping. To add a * mapping for a specific EFI method, simply define a macro diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index afa774a312f5..34fe3fad042f 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -541,6 +541,20 @@ union efi_tcg2_protocol { } mixed_mode; }; +typedef union efi_load_file_protocol efi_load_file_protocol_t; +typedef union efi_load_file_protocol efi_load_file2_protocol_t; + +union efi_load_file_protocol { + struct { + efi_status_t (__efiapi *load_file)(efi_load_file_protocol_t *, + efi_device_path_protocol_t *, + bool, unsigned long *, void *); + }; + struct { + u32 load_file; + } mixed_mode; +}; + void efi_pci_disable_bridge_busmaster(void); typedef efi_status_t (*efi_exit_boot_map_processing)( diff --git a/include/linux/efi.h b/include/linux/efi.h index 0e047d2738cd..9ccf313fe9de 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -332,6 +332,8 @@ void efi_native_runtime_setup(void); #define EFI_CONSOLE_OUT_DEVICE_GUID EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d) #define APPLE_PROPERTIES_PROTOCOL_GUID EFI_GUID(0x91bd12fe, 0xf6c3, 0x44fb, 0xa5, 0xb7, 0x51, 0x22, 0xab, 0x30, 0x3a, 0xe0) #define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) +#define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) +#define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d) #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) -- cgit v1.2.3 From db8952e7094fde3a397321240d5d57ec111258d8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 10 Feb 2020 08:46:57 +0000 Subject: efi/dev-path-parser: Add struct definition for vendor type device path nodes In preparation of adding support for loading the initrd via a special device path, add the struct definition of a vendor GUIDed device path node to efi.h. Since we will be producing these data structures rather than just consumsing the ones instantiated by the firmware, refactor the various device path node definitions so we can take the size of each node using sizeof() rather than having to resort to opaque arithmetic in the static initializers. While at it, drop the #if IS_ENABLED() check for the declaration of efi_get_device_by_path(), which is unnecessary, and constify its first argument as well. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/apple-properties.c | 8 +++--- drivers/firmware/efi/dev-path-parser.c | 38 +++++++++++++------------- include/linux/efi.h | 48 ++++++++++++++++++++------------- 3 files changed, 52 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index 084942846f4d..34f53d898acb 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -46,7 +46,7 @@ struct properties_header { }; static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, - struct device *dev, void *ptr, + struct device *dev, const void *ptr, struct property_entry entry[]) { int i; @@ -117,10 +117,10 @@ static int __init unmarshal_devices(struct properties_header *properties) while (offset + sizeof(struct dev_header) < properties->len) { struct dev_header *dev_header = (void *)properties + offset; struct property_entry *entry = NULL; + const struct efi_dev_path *ptr; struct device *dev; size_t len; int ret, i; - void *ptr; if (offset + dev_header->len > properties->len || dev_header->len <= sizeof(*dev_header)) { @@ -131,10 +131,10 @@ static int __init unmarshal_devices(struct properties_header *properties) ptr = dev_header->path; len = dev_header->len - sizeof(*dev_header); - dev = efi_get_device_by_path((struct efi_dev_path **)&ptr, &len); + dev = efi_get_device_by_path(&ptr, &len); if (IS_ERR(dev)) { pr_err("device path parse error %ld at %#zx:\n", - PTR_ERR(dev), ptr - (void *)dev_header); + PTR_ERR(dev), (void *)ptr - (void *)dev_header); print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, 16, 1, dev_header, dev_header->len, true); dev = NULL; diff --git a/drivers/firmware/efi/dev-path-parser.c b/drivers/firmware/efi/dev-path-parser.c index 20123384271c..5c9625e552f4 100644 --- a/drivers/firmware/efi/dev-path-parser.c +++ b/drivers/firmware/efi/dev-path-parser.c @@ -31,13 +31,13 @@ static int __init match_acpi_dev(struct device *dev, const void *data) return !strcmp("0", hid_uid.uid); } -static long __init parse_acpi_path(struct efi_dev_path *node, +static long __init parse_acpi_path(const struct efi_dev_path *node, struct device *parent, struct device **child) { struct acpi_hid_uid hid_uid = {}; struct device *phys_dev; - if (node->length != 12) + if (node->header.length != 12) return -EINVAL; sprintf(hid_uid.hid[0].id, "%c%c%c%04X", @@ -69,12 +69,12 @@ static int __init match_pci_dev(struct device *dev, void *data) return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn; } -static long __init parse_pci_path(struct efi_dev_path *node, +static long __init parse_pci_path(const struct efi_dev_path *node, struct device *parent, struct device **child) { unsigned int devfn; - if (node->length != 6) + if (node->header.length != 6) return -EINVAL; if (!parent) return -EINVAL; @@ -105,19 +105,19 @@ static long __init parse_pci_path(struct efi_dev_path *node, * search for a device. */ -static long __init parse_end_path(struct efi_dev_path *node, +static long __init parse_end_path(const struct efi_dev_path *node, struct device *parent, struct device **child) { - if (node->length != 4) + if (node->header.length != 4) return -EINVAL; - if (node->sub_type != EFI_DEV_END_INSTANCE && - node->sub_type != EFI_DEV_END_ENTIRE) + if (node->header.sub_type != EFI_DEV_END_INSTANCE && + node->header.sub_type != EFI_DEV_END_ENTIRE) return -EINVAL; if (!parent) return -ENODEV; *child = get_device(parent); - return node->sub_type; + return node->header.sub_type; } /** @@ -156,7 +156,7 @@ static long __init parse_end_path(struct efi_dev_path *node, * %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len, * %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented. */ -struct device * __init efi_get_device_by_path(struct efi_dev_path **node, +struct device * __init efi_get_device_by_path(const struct efi_dev_path **node, size_t *len) { struct device *parent = NULL, *child; @@ -166,16 +166,16 @@ struct device * __init efi_get_device_by_path(struct efi_dev_path **node, return NULL; while (!ret) { - if (*len < 4 || *len < (*node)->length) + if (*len < 4 || *len < (*node)->header.length) ret = -EINVAL; - else if ((*node)->type == EFI_DEV_ACPI && - (*node)->sub_type == EFI_DEV_BASIC_ACPI) + else if ((*node)->header.type == EFI_DEV_ACPI && + (*node)->header.sub_type == EFI_DEV_BASIC_ACPI) ret = parse_acpi_path(*node, parent, &child); - else if ((*node)->type == EFI_DEV_HW && - (*node)->sub_type == EFI_DEV_PCI) + else if ((*node)->header.type == EFI_DEV_HW && + (*node)->header.sub_type == EFI_DEV_PCI) ret = parse_pci_path(*node, parent, &child); - else if (((*node)->type == EFI_DEV_END_PATH || - (*node)->type == EFI_DEV_END_PATH2)) + else if (((*node)->header.type == EFI_DEV_END_PATH || + (*node)->header.type == EFI_DEV_END_PATH2)) ret = parse_end_path(*node, parent, &child); else ret = -ENOTSUPP; @@ -185,8 +185,8 @@ struct device * __init efi_get_device_by_path(struct efi_dev_path **node, return ERR_PTR(ret); parent = child; - *node = (void *)*node + (*node)->length; - *len -= (*node)->length; + *node = (void *)*node + (*node)->header.length; + *len -= (*node)->header.length; } if (ret == EFI_DEV_END_ENTIRE) diff --git a/include/linux/efi.h b/include/linux/efi.h index 9ccf313fe9de..0976e57b4caa 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -855,30 +855,40 @@ extern int efi_status_to_err(efi_status_t status); #define EFI_DEV_END_ENTIRE 0xFF struct efi_generic_dev_path { - u8 type; - u8 sub_type; - u16 length; -} __attribute ((packed)); + u8 type; + u8 sub_type; + u16 length; +} __packed; + +struct efi_acpi_dev_path { + struct efi_generic_dev_path header; + u32 hid; + u32 uid; +} __packed; + +struct efi_pci_dev_path { + struct efi_generic_dev_path header; + u8 fn; + u8 dev; +} __packed; + +struct efi_vendor_dev_path { + struct efi_generic_dev_path header; + efi_guid_t vendorguid; + u8 vendordata[]; +} __packed; struct efi_dev_path { - u8 type; /* can be replaced with unnamed */ - u8 sub_type; /* struct efi_generic_dev_path; */ - u16 length; /* once we've moved to -std=c11 */ union { - struct { - u32 hid; - u32 uid; - } acpi; - struct { - u8 fn; - u8 dev; - } pci; + struct efi_generic_dev_path header; + struct efi_acpi_dev_path acpi; + struct efi_pci_dev_path pci; + struct efi_vendor_dev_path vendor; }; -} __attribute ((packed)); +} __packed; -#if IS_ENABLED(CONFIG_EFI_DEV_PATH_PARSER) -struct device *efi_get_device_by_path(struct efi_dev_path **node, size_t *len); -#endif +struct device *efi_get_device_by_path(const struct efi_dev_path **node, + size_t *len); static inline void memrange_efi_to_native(u64 *addr, u64 *npages) { -- cgit v1.2.3 From ec93fc371f014a6fb483e3556061ecad4b40735c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 3 Feb 2020 23:45:14 +0000 Subject: efi/libstub: Add support for loading the initrd from a device path There are currently two ways to specify the initrd to be passed to the Linux kernel when booting via the EFI stub: - it can be passed as a initrd= command line option when doing a pure PE boot (as opposed to the EFI handover protocol that exists for x86) - otherwise, the bootloader or firmware can load the initrd into memory, and pass the address and size via the bootparams struct (x86) or device tree (ARM) In the first case, we are limited to loading from the same file system that the kernel was loaded from, and it is also problematic in a trusted boot context, given that we cannot easily protect the command line from tampering without either adding complicated white/blacklisting of boot arguments or locking down the command line altogether. In the second case, we force the bootloader to duplicate knowledge about the boot protocol which is already encoded in the stub, and which may be subject to change over time, e.g., bootparams struct definitions, memory allocation/alignment requirements for the placement of the initrd etc etc. In the ARM case, it also requires the bootloader to modify the hardware description provided by the firmware, as it is passed in the same file. On systems where the initrd is measured after loading, it creates a time window where the initrd contents might be manipulated in memory before handing over to the kernel. Address these concerns by adding support for loading the initrd into memory by invoking the EFI LoadFile2 protocol installed on a vendor GUIDed device path that specifically designates a Linux initrd. This addresses the above concerns, by putting the EFI stub in charge of placement in memory and of passing the base and size to the kernel proper (via whatever means it desires) while still leaving it up to the firmware or bootloader to obtain the file contents, potentially from other file systems than the one the kernel itself was loaded from. On platforms that implement measured boot, it permits the firmware to take the measurement right before the kernel actually consumes the contents. Acked-by: Laszlo Ersek Tested-by: Ilias Apalodimas Acked-by: Ilias Apalodimas Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/arm-stub.c | 15 ++++- drivers/firmware/efi/libstub/efi-stub-helper.c | 86 ++++++++++++++++++++++++++ drivers/firmware/efi/libstub/efistub.h | 4 ++ drivers/firmware/efi/libstub/x86-stub.c | 23 +++++++ include/linux/efi.h | 1 + 5 files changed, 126 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index a11257a90dd0..11c673a7e95b 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -165,6 +165,7 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) enum efi_secureboot_mode secure_boot; struct screen_info *si; efi_properties_table_t *prop_tbl; + unsigned long max_addr; sys_table = sys_table_arg; @@ -267,10 +268,18 @@ efi_status_t efi_entry(efi_handle_t handle, efi_system_table_t *sys_table_arg) if (!fdt_addr) pr_efi("Generating empty DTB\n"); - status = efi_load_initrd(image, &initrd_addr, &initrd_size, ULONG_MAX, - efi_get_max_initrd_addr(dram_base, image_addr)); + max_addr = efi_get_max_initrd_addr(dram_base, image_addr); + status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); + if (status == EFI_SUCCESS) { + pr_efi("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); + } else if (status == EFI_NOT_FOUND) { + status = efi_load_initrd(image, &initrd_addr, &initrd_size, + ULONG_MAX, max_addr); + if (status == EFI_SUCCESS) + pr_efi("Loaded initrd from command line option\n"); + } if (status != EFI_SUCCESS) - pr_efi_err("Failed initrd from command line!\n"); + pr_efi_err("Failed to load initrd!\n"); efi_random_get_seed(); diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index 49008ac88b63..6017b968cef7 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -299,3 +299,89 @@ void efi_char16_printk(efi_char16_t *str) efi_call_proto(efi_table_attr(efi_system_table(), con_out), output_string, str); } + +/* + * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way + * for the firmware or bootloader to expose the initrd data directly to the stub + * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is + * very easy to implement. It is a simple Linux initrd specific conduit between + * kernel and firmware, allowing us to put the EFI stub (being part of the + * kernel) in charge of where and when to load the initrd, while leaving it up + * to the firmware to decide whether it needs to expose its filesystem hierarchy + * via EFI protocols. + */ +static const struct { + struct efi_vendor_dev_path vendor; + struct efi_generic_dev_path end; +} __packed initrd_dev_path = { + { + { + EFI_DEV_MEDIA, + EFI_DEV_MEDIA_VENDOR, + sizeof(struct efi_vendor_dev_path), + }, + LINUX_EFI_INITRD_MEDIA_GUID + }, { + EFI_DEV_END_PATH, + EFI_DEV_END_ENTIRE, + sizeof(struct efi_generic_dev_path) + } +}; + +/** + * efi_load_initrd_dev_path - load the initrd from the Linux initrd device path + * @load_addr: pointer to store the address where the initrd was loaded + * @load_size: pointer to store the size of the loaded initrd + * @max: upper limit for the initrd memory allocation + * @return: %EFI_SUCCESS if the initrd was loaded successfully, in which + * case @load_addr and @load_size are assigned accordingly + * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd + * device path + * %EFI_INVALID_PARAMETER if load_addr == NULL or load_size == NULL + * %EFI_OUT_OF_RESOURCES if memory allocation failed + * %EFI_LOAD_ERROR in all other cases + */ +efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, + unsigned long *load_size, + unsigned long max) +{ + efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; + efi_device_path_protocol_t *dp; + efi_load_file2_protocol_t *lf2; + unsigned long initrd_addr; + unsigned long initrd_size; + efi_handle_t handle; + efi_status_t status; + + if (!load_addr || !load_size) + return EFI_INVALID_PARAMETER; + + dp = (efi_device_path_protocol_t *)&initrd_dev_path; + status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); + if (status != EFI_SUCCESS) + return status; + + status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, + (void **)&lf2); + if (status != EFI_SUCCESS) + return status; + + status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, NULL); + if (status != EFI_BUFFER_TOO_SMALL) + return EFI_LOAD_ERROR; + + status = efi_allocate_pages(initrd_size, &initrd_addr, max); + if (status != EFI_SUCCESS) + return status; + + status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, + (void *)initrd_addr); + if (status != EFI_SUCCESS) { + efi_free(initrd_size, initrd_addr); + return EFI_LOAD_ERROR; + } + + *load_addr = initrd_addr; + *load_size = initrd_size; + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 34fe3fad042f..b58cb2c4474e 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -640,4 +640,8 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image, unsigned long soft_limit, unsigned long hard_limit); +efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, + unsigned long *load_size, + unsigned long max); + #endif diff --git a/drivers/firmware/efi/libstub/x86-stub.c b/drivers/firmware/efi/libstub/x86-stub.c index 681b620d8d40..16bf4ed21f1f 100644 --- a/drivers/firmware/efi/libstub/x86-stub.c +++ b/drivers/firmware/efi/libstub/x86-stub.c @@ -699,9 +699,14 @@ struct boot_params *efi_main(efi_handle_t handle, { unsigned long bzimage_addr = (unsigned long)startup_32; struct setup_header *hdr = &boot_params->hdr; + unsigned long max_addr = hdr->initrd_addr_max; + unsigned long initrd_addr, initrd_size; efi_status_t status; unsigned long cmdline_paddr; + if (hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) + max_addr = ULONG_MAX; + sys_table = sys_table_arg; /* Check if we were booted by the EFI firmware */ @@ -734,6 +739,24 @@ struct boot_params *efi_main(efi_handle_t handle, ((u64)boot_params->ext_cmd_line_ptr << 32)); efi_parse_options((char *)cmdline_paddr); + /* + * At this point, an initrd may already have been loaded, either by + * the bootloader and passed via bootparams, or loaded from a initrd= + * command line option by efi_pe_entry() above. In either case, we + * permit an initrd loaded from the LINUX_EFI_INITRD_MEDIA_GUID device + * path to supersede it. + */ + status = efi_load_initrd_dev_path(&initrd_addr, &initrd_size, max_addr); + if (status == EFI_SUCCESS) { + hdr->ramdisk_image = (u32)initrd_addr; + hdr->ramdisk_size = (u32)initrd_size; + boot_params->ext_ramdisk_image = (u64)initrd_addr >> 32; + boot_params->ext_ramdisk_size = (u64)initrd_size >> 32; + } else if (status != EFI_NOT_FOUND) { + efi_printk("efi_load_initrd_dev_path() failed!\n"); + goto fail; + } + /* * If the boot loader gave us a value for secure_boot then we use that, * otherwise we ask the BIOS. diff --git a/include/linux/efi.h b/include/linux/efi.h index 0976e57b4caa..1bf482daa22d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -353,6 +353,7 @@ void efi_native_runtime_setup(void); #define LINUX_EFI_TPM_EVENT_LOG_GUID EFI_GUID(0xb7799cb0, 0xeca2, 0x4943, 0x96, 0x67, 0x1f, 0xae, 0x07, 0xb7, 0x47, 0xfa) #define LINUX_EFI_TPM_FINAL_LOG_GUID EFI_GUID(0x1e2ed096, 0x30e2, 0x4254, 0xbd, 0x89, 0x86, 0x3b, 0xbe, 0xf8, 0x23, 0x25) #define LINUX_EFI_MEMRESERVE_TABLE_GUID EFI_GUID(0x888eb0c6, 0x8ede, 0x4ff5, 0xa8, 0xf0, 0x9a, 0xee, 0x5c, 0xb9, 0x77, 0xc2) +#define LINUX_EFI_INITRD_MEDIA_GUID EFI_GUID(0x5568e427, 0x68fc, 0x4f3d, 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68) /* OEM GUIDs */ #define DELLEMC_EFI_RCI2_TABLE_GUID EFI_GUID(0x2d9f28a2, 0xa886, 0x456a, 0x97, 0xa8, 0xf1, 0x1e, 0xf2, 0x4f, 0xf4, 0x55) -- cgit v1.2.3 From 50d53c58dd77d3b0b6a5afe391eaac3722fc3153 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Jan 2020 15:29:21 +0100 Subject: efi: Drop handling of 'boot_info' configuration table Some plumbing exists to handle a UEFI configuration table of type BOOT_INFO but since we never match it to a GUID anywhere, we never actually register such a table, or access it, for that matter. So simply drop all mentions of it. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 1 - drivers/firmware/efi/efi.c | 3 --- include/linux/efi.h | 1 - 3 files changed, 5 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 52067ed7fd59..4970229fd822 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -70,7 +70,6 @@ static const unsigned long * const efi_tables[] = { &efi.acpi20, &efi.smbios, &efi.smbios3, - &efi.boot_info, &efi.hcdp, &efi.uga, #ifdef CONFIG_X86_UV diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 621220ab3d0e..5464e3849ee4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -40,7 +40,6 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .boot_info = EFI_INVALID_TABLE_ADDR, .hcdp = EFI_INVALID_TABLE_ADDR, .uga = EFI_INVALID_TABLE_ADDR, .fw_vendor = EFI_INVALID_TABLE_ADDR, @@ -139,8 +138,6 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); if (efi.hcdp != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); - if (efi.boot_info != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "BOOTINFO=0x%lx\n", efi.boot_info); if (efi.uga != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "UGA=0x%lx\n", efi.uga); diff --git a/include/linux/efi.h b/include/linux/efi.h index 1bf482daa22d..c517d3b7986b 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -536,7 +536,6 @@ extern struct efi { unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long boot_info; /* boot info table */ unsigned long hcdp; /* HCDP table */ unsigned long uga; /* UGA table */ unsigned long fw_vendor; /* fw_vendor */ -- cgit v1.2.3 From 120540f230d5d2d32846adc0156b58961c8c59d1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Jan 2020 15:43:53 +0100 Subject: efi/ia64: Move HCDP and MPS table handling into IA64 arch code The HCDP and MPS tables are Itanium specific EFI config tables, so move their handling to ia64 arch code. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 13 +++++++++++++ arch/x86/platform/efi/efi.c | 2 -- drivers/firmware/efi/efi.c | 14 ++++++-------- drivers/firmware/pcdp.c | 8 +++++--- include/linux/efi.h | 2 -- 5 files changed, 24 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 0a34dcc435c6..312308967a9d 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -45,11 +45,15 @@ #define EFI_DEBUG 0 +static unsigned long mps_phys = EFI_INVALID_TABLE_ADDR; static __initdata unsigned long palo_phys; +unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; static __initdata efi_config_table_type_t arch_tables[] = { + {HCDP_TABLE_GUID, "HCDP", &hcdp_phys}, + {MPS_TABLE_GUID, "MPS", &mps_phys}, {PROCESSOR_ABSTRACTION_LAYER_OVERWRITE_GUID, "PALO", &palo_phys}, {SAL_SYSTEM_TABLE_GUID, "SALsystab", &sal_systab_phys}, {NULL_GUID, NULL, 0}, @@ -1351,3 +1355,12 @@ vmcore_find_descriptor_size (unsigned long address) return ret; } #endif + +char *efi_systab_show_arch(char *str) +{ + if (mps_phys != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "MPS=0x%lx\n", mps_phys); + if (hcdp_phys != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "HCDP=0x%lx\n", hcdp_phys); + return str; +} diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 4970229fd822..61ebaae62894 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -65,12 +65,10 @@ static efi_config_table_type_t arch_tables[] __initdata = { }; static const unsigned long * const efi_tables[] = { - &efi.mps, &efi.acpi, &efi.acpi20, &efi.smbios, &efi.smbios3, - &efi.hcdp, &efi.uga, #ifdef CONFIG_X86_UV &uv_systab_phys, diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 5464e3849ee4..8129a52f8ef5 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -35,12 +35,10 @@ #include struct efi __read_mostly efi = { - .mps = EFI_INVALID_TABLE_ADDR, .acpi = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .hcdp = EFI_INVALID_TABLE_ADDR, .uga = EFI_INVALID_TABLE_ADDR, .fw_vendor = EFI_INVALID_TABLE_ADDR, .runtime = EFI_INVALID_TABLE_ADDR, @@ -121,8 +119,6 @@ static ssize_t systab_show(struct kobject *kobj, if (!kobj || !buf) return -EINVAL; - if (efi.mps != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "MPS=0x%lx\n", efi.mps); if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "ACPI20=0x%lx\n", efi.acpi20); if (efi.acpi != EFI_INVALID_TABLE_ADDR) @@ -136,11 +132,15 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); if (efi.smbios != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); - if (efi.hcdp != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "HCDP=0x%lx\n", efi.hcdp); if (efi.uga != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "UGA=0x%lx\n", efi.uga); + if (IS_ENABLED(CONFIG_IA64)) { + extern char *efi_systab_show_arch(char *str); + + str = efi_systab_show_arch(str); + } + return str - buf; } @@ -467,8 +467,6 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size) static __initdata efi_config_table_type_t common_tables[] = { {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, - {HCDP_TABLE_GUID, "HCDP", &efi.hcdp}, - {MPS_TABLE_GUID, "MPS", &efi.mps}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index 4adeb7a2bdf5..715a45442d1c 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -80,6 +80,8 @@ setup_vga_console(struct pcdp_device *dev) #endif } +extern unsigned long hcdp_phys; + int __init efi_setup_pcdp_console(char *cmdline) { @@ -89,11 +91,11 @@ efi_setup_pcdp_console(char *cmdline) int i, serial = 0; int rc = -ENODEV; - if (efi.hcdp == EFI_INVALID_TABLE_ADDR) + if (hcdp_phys == EFI_INVALID_TABLE_ADDR) return -ENODEV; - pcdp = early_memremap(efi.hcdp, 4096); - printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, efi.hcdp); + pcdp = early_memremap(hcdp_phys, 4096); + printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, hcdp_phys); if (strstr(cmdline, "console=hcdp")) { if (pcdp->rev < 3) diff --git a/include/linux/efi.h b/include/linux/efi.h index c517d3b7986b..45443932104f 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -531,12 +531,10 @@ typedef struct { extern struct efi { efi_system_table_t *systab; /* EFI system table */ unsigned int runtime_version; /* Runtime services version */ - unsigned long mps; /* MPS table */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long hcdp; /* HCDP table */ unsigned long uga; /* UGA table */ unsigned long fw_vendor; /* fw_vendor */ unsigned long runtime; /* runtime table */ -- cgit v1.2.3 From fd506e0cf9fd4306aa0eb57cbff5f00514da8179 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Jan 2020 16:17:59 +0100 Subject: efi: Move UGA and PROP table handling to x86 code The UGA table is x86 specific (its handling was introduced when the EFI support code was modified to accommodate IA32), so there is no need to handle it in generic code. The EFI properties table is not strictly x86 specific, but it was deprecated almost immediately after having been introduced, due to implementation difficulties. Only x86 takes it into account today, and this is not going to change, so make this table x86 only as well. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 32 ++++++++++++++++++++++++++++++-- drivers/firmware/efi/efi.c | 25 +------------------------ include/linux/efi.h | 2 -- 3 files changed, 31 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 61ebaae62894..421f082535c5 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -57,7 +57,12 @@ static efi_system_table_t efi_systab __initdata; static u64 efi_systab_phys __initdata; +static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; +static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; + static efi_config_table_type_t arch_tables[] __initdata = { + {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, + {UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys}, #ifdef CONFIG_X86_UV {UV_SYSTEM_TABLE_GUID, "UVsystab", &uv_systab_phys}, #endif @@ -69,7 +74,7 @@ static const unsigned long * const efi_tables[] = { &efi.acpi20, &efi.smbios, &efi.smbios3, - &efi.uga, + &uga_phys, #ifdef CONFIG_X86_UV &uv_systab_phys, #endif @@ -77,7 +82,7 @@ static const unsigned long * const efi_tables[] = { &efi.runtime, &efi.config_table, &efi.esrt, - &efi.properties_table, + &prop_phys, &efi.mem_attr_table, #ifdef CONFIG_EFI_RCI2_TABLE &rci2_table_phys, @@ -490,6 +495,22 @@ void __init efi_init(void) return; } + /* Parse the EFI Properties table if it exists */ + if (prop_phys != EFI_INVALID_TABLE_ADDR) { + efi_properties_table_t *tbl; + + tbl = early_memremap_ro(prop_phys, sizeof(*tbl)); + if (tbl == NULL) { + pr_err("Could not map Properties table!\n"); + } else { + if (tbl->memory_protection_attribute & + EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) + set_bit(EFI_NX_PE_DATA, &efi.flags); + + early_memunmap(tbl, sizeof(*tbl)); + } + } + set_bit(EFI_RUNTIME_SERVICES, &efi.flags); efi_clean_memmap(); @@ -993,3 +1014,10 @@ bool efi_is_table_address(unsigned long phys_addr) return false; } + +char *efi_systab_show_arch(char *str) +{ + if (uga_phys != EFI_INVALID_TABLE_ADDR) + str += sprintf(str, "UGA=0x%lx\n", uga_phys); + return str; +} diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 8129a52f8ef5..68527fbbe01c 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -39,12 +39,10 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .uga = EFI_INVALID_TABLE_ADDR, .fw_vendor = EFI_INVALID_TABLE_ADDR, .runtime = EFI_INVALID_TABLE_ADDR, .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, - .properties_table = EFI_INVALID_TABLE_ADDR, .mem_attr_table = EFI_INVALID_TABLE_ADDR, .rng_seed = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, @@ -132,10 +130,8 @@ static ssize_t systab_show(struct kobject *kobj, str += sprintf(str, "SMBIOS3=0x%lx\n", efi.smbios3); if (efi.smbios != EFI_INVALID_TABLE_ADDR) str += sprintf(str, "SMBIOS=0x%lx\n", efi.smbios); - if (efi.uga != EFI_INVALID_TABLE_ADDR) - str += sprintf(str, "UGA=0x%lx\n", efi.uga); - if (IS_ENABLED(CONFIG_IA64)) { + if (IS_ENABLED(CONFIG_IA64) || IS_ENABLED(CONFIG_X86)) { extern char *efi_systab_show_arch(char *str); str = efi_systab_show_arch(str); @@ -469,9 +465,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, - {UGA_IO_PROTOCOL_GUID, "UGA", &efi.uga}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, - {EFI_PROPERTIES_TABLE_GUID, "PROP", &efi.properties_table}, {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, @@ -570,23 +564,6 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, efi_tpm_eventlog_init(); - /* Parse the EFI Properties table if it exists */ - if (efi.properties_table != EFI_INVALID_TABLE_ADDR) { - efi_properties_table_t *tbl; - - tbl = early_memremap(efi.properties_table, sizeof(*tbl)); - if (tbl == NULL) { - pr_err("Could not map Properties table!\n"); - return -ENOMEM; - } - - if (tbl->memory_protection_attribute & - EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) - set_bit(EFI_NX_PE_DATA, &efi.flags); - - early_memunmap(tbl, sizeof(*tbl)); - } - if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { unsigned long prsv = efi.mem_reserve; diff --git a/include/linux/efi.h b/include/linux/efi.h index 45443932104f..e091f2aff61d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -535,12 +535,10 @@ extern struct efi { unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long uga; /* UGA table */ unsigned long fw_vendor; /* fw_vendor */ unsigned long runtime; /* runtime table */ unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ - unsigned long properties_table; /* properties table */ unsigned long mem_attr_table; /* memory attributes table */ unsigned long rng_seed; /* UEFI firmware random seed */ unsigned long tpm_log; /* TPM2 Event Log table */ -- cgit v1.2.3 From 5d288dbd88606d8f215c7138b10649115d79cadd Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 14:58:15 +0100 Subject: efi: Make rng_seed table handling local to efi.c Move the rng_seed table address from struct efi into a static global variable in efi.c, which is the only place we ever refer to it anyway. This reduces the footprint of struct efi, which is a r/w data structure that is shared with the world. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 21 ++++++++++----------- include/linux/efi.h | 1 - 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 68527fbbe01c..bbb6246d08be 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -44,13 +44,14 @@ struct efi __read_mostly efi = { .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, .mem_attr_table = EFI_INVALID_TABLE_ADDR, - .rng_seed = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, .mem_reserve = EFI_INVALID_TABLE_ADDR, }; EXPORT_SYMBOL(efi); +static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; + struct mm_struct efi_mm = { .mm_rb = RB_ROOT, .mm_users = ATOMIC_INIT(2), @@ -467,7 +468,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, - {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, + {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, @@ -535,11 +536,11 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, pr_cont("\n"); set_bit(EFI_CONFIG_TABLES, &efi.flags); - if (efi.rng_seed != EFI_INVALID_TABLE_ADDR) { + if (rng_seed != EFI_INVALID_TABLE_ADDR) { struct linux_efi_random_seed *seed; u32 size = 0; - seed = early_memremap(efi.rng_seed, sizeof(*seed)); + seed = early_memremap(rng_seed, sizeof(*seed)); if (seed != NULL) { size = seed->size; early_memunmap(seed, sizeof(*seed)); @@ -547,8 +548,7 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, pr_err("Could not map UEFI random seed!\n"); } if (size > 0) { - seed = early_memremap(efi.rng_seed, - sizeof(*seed) + size); + seed = early_memremap(rng_seed, sizeof(*seed) + size); if (seed != NULL) { pr_notice("seeding entropy pool\n"); add_bootloader_randomness(seed->bits, seed->size); @@ -1048,7 +1048,7 @@ static int update_efi_random_seed(struct notifier_block *nb, if (!kexec_in_progress) return NOTIFY_DONE; - seed = memremap(efi.rng_seed, sizeof(*seed), MEMREMAP_WB); + seed = memremap(rng_seed, sizeof(*seed), MEMREMAP_WB); if (seed != NULL) { size = min(seed->size, EFI_RANDOM_SEED_SIZE); memunmap(seed); @@ -1056,8 +1056,7 @@ static int update_efi_random_seed(struct notifier_block *nb, pr_err("Could not map UEFI random seed!\n"); } if (size > 0) { - seed = memremap(efi.rng_seed, sizeof(*seed) + size, - MEMREMAP_WB); + seed = memremap(rng_seed, sizeof(*seed) + size, MEMREMAP_WB); if (seed != NULL) { seed->size = size; get_random_bytes(seed->bits, seed->size); @@ -1073,9 +1072,9 @@ static struct notifier_block efi_random_seed_nb = { .notifier_call = update_efi_random_seed, }; -static int register_update_efi_random_seed(void) +static int __init register_update_efi_random_seed(void) { - if (efi.rng_seed == EFI_INVALID_TABLE_ADDR) + if (rng_seed == EFI_INVALID_TABLE_ADDR) return 0; return register_reboot_notifier(&efi_random_seed_nb); } diff --git a/include/linux/efi.h b/include/linux/efi.h index e091f2aff61d..36380542e054 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -540,7 +540,6 @@ extern struct efi { unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ unsigned long mem_attr_table; /* memory attributes table */ - unsigned long rng_seed; /* UEFI firmware random seed */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ unsigned long mem_reserve; /* Linux EFI memreserve table */ -- cgit v1.2.3 From a17e809ea573e69474064ba2bbff06d212861e19 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 15:05:12 +0100 Subject: efi: Move mem_attr_table out of struct efi The memory attributes table is only used at init time by the core EFI code, so there is no need to carry its address in struct efi that is shared with the world. So move it out, and make it __ro_after_init as well, considering that the value is set during early boot. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 2 +- drivers/firmware/efi/efi.c | 3 +-- drivers/firmware/efi/memattr.c | 13 +++++++------ include/linux/efi.h | 3 ++- 4 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 421f082535c5..22dc3678cdba 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -83,7 +83,7 @@ static const unsigned long * const efi_tables[] = { &efi.config_table, &efi.esrt, &prop_phys, - &efi.mem_attr_table, + &efi_mem_attr_table, #ifdef CONFIG_EFI_RCI2_TABLE &rci2_table_phys, #endif diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index bbb6246d08be..1fc4e174f11d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -43,7 +43,6 @@ struct efi __read_mostly efi = { .runtime = EFI_INVALID_TABLE_ADDR, .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, - .mem_attr_table = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, .mem_reserve = EFI_INVALID_TABLE_ADDR, @@ -467,7 +466,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, - {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, + {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c index 58452fde92cc..5737cb0fcd44 100644 --- a/drivers/firmware/efi/memattr.c +++ b/drivers/firmware/efi/memattr.c @@ -13,6 +13,7 @@ #include static int __initdata tbl_size; +unsigned long __ro_after_init efi_mem_attr_table = EFI_INVALID_TABLE_ADDR; /* * Reserve the memory associated with the Memory Attributes configuration @@ -22,13 +23,13 @@ int __init efi_memattr_init(void) { efi_memory_attributes_table_t *tbl; - if (efi.mem_attr_table == EFI_INVALID_TABLE_ADDR) + if (efi_mem_attr_table == EFI_INVALID_TABLE_ADDR) return 0; - tbl = early_memremap(efi.mem_attr_table, sizeof(*tbl)); + tbl = early_memremap(efi_mem_attr_table, sizeof(*tbl)); if (!tbl) { pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", - efi.mem_attr_table); + efi_mem_attr_table); return -ENOMEM; } @@ -39,7 +40,7 @@ int __init efi_memattr_init(void) } tbl_size = sizeof(*tbl) + tbl->num_entries * tbl->desc_size; - memblock_reserve(efi.mem_attr_table, tbl_size); + memblock_reserve(efi_mem_attr_table, tbl_size); set_bit(EFI_MEM_ATTR, &efi.flags); unmap: @@ -147,10 +148,10 @@ int __init efi_memattr_apply_permissions(struct mm_struct *mm, if (WARN_ON(!efi_enabled(EFI_MEMMAP))) return 0; - tbl = memremap(efi.mem_attr_table, tbl_size, MEMREMAP_WB); + tbl = memremap(efi_mem_attr_table, tbl_size, MEMREMAP_WB); if (!tbl) { pr_err("Failed to map EFI Memory Attributes table @ 0x%lx\n", - efi.mem_attr_table); + efi_mem_attr_table); return -ENOMEM; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 36380542e054..b093fce1cf59 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -539,7 +539,6 @@ extern struct efi { unsigned long runtime; /* runtime table */ unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ - unsigned long mem_attr_table; /* memory attributes table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ unsigned long mem_reserve; /* Linux EFI memreserve table */ @@ -641,6 +640,8 @@ extern void __init efi_fake_memmap(void); static inline void efi_fake_memmap(void) { } #endif +extern unsigned long efi_mem_attr_table; + /* * efi_memattr_perm_setter - arch specific callback function passed into * efi_memattr_apply_permissions() that updates the -- cgit v1.2.3 From b7846e6be235c4a19337a32168b27ed836a1504e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 15:06:54 +0100 Subject: efi: Make memreserve table handling local to efi.c There is no need for struct efi to carry the address of the memreserve table and share it with the world. So move it out and make it __initdata as well. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 12 ++++++------ include/linux/efi.h | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 1fc4e174f11d..41bb2c44cea4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -45,11 +45,11 @@ struct efi __read_mostly efi = { .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, - .mem_reserve = EFI_INVALID_TABLE_ADDR, }; EXPORT_SYMBOL(efi); static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; +static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; struct mm_struct efi_mm = { .mm_rb = RB_ROOT, @@ -470,7 +470,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, - {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, + {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve}, #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys}, #endif @@ -563,8 +563,8 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, efi_tpm_eventlog_init(); - if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { - unsigned long prsv = efi.mem_reserve; + if (mem_reserve != EFI_INVALID_TABLE_ADDR) { + unsigned long prsv = mem_reserve; while (prsv) { struct linux_efi_memreserve *rsv; @@ -939,10 +939,10 @@ static struct linux_efi_memreserve *efi_memreserve_root __ro_after_init; static int __init efi_memreserve_map_root(void) { - if (efi.mem_reserve == EFI_INVALID_TABLE_ADDR) + if (mem_reserve == EFI_INVALID_TABLE_ADDR) return -ENODEV; - efi_memreserve_root = memremap(efi.mem_reserve, + efi_memreserve_root = memremap(mem_reserve, sizeof(*efi_memreserve_root), MEMREMAP_WB); if (WARN_ON_ONCE(!efi_memreserve_root)) diff --git a/include/linux/efi.h b/include/linux/efi.h index b093fce1cf59..a5e210abe4ca 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -541,7 +541,6 @@ extern struct efi { unsigned long esrt; /* ESRT table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ - unsigned long mem_reserve; /* Linux EFI memreserve table */ efi_get_time_t *get_time; efi_set_time_t *set_time; efi_get_wakeup_time_t *get_wakeup_time; -- cgit v1.2.3 From 14fb4209094355928d5a742e35afabdf7b404c17 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 10:49:11 +0100 Subject: efi: Merge EFI system table revision and vendor checks We have three different versions of the code that checks the EFI system table revision and copies the firmware vendor string, and they are mostly equivalent, with the exception of the use of early_memremap_ro vs. __va() and the lowest major revision to warn about. Let's move this into common code and factor out the commonalities. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 23 ++-------------- arch/x86/platform/efi/efi.c | 46 +++++++------------------------ drivers/firmware/efi/arm-init.c | 32 ++++----------------- drivers/firmware/efi/efi.c | 61 +++++++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 4 +++ 5 files changed, 83 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 312308967a9d..292fe354158d 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -479,10 +479,8 @@ void __init efi_init (void) { void *efi_map_start, *efi_map_end; - efi_char16_t *c16; u64 efi_desc_size; - char *cp, vendor[100] = "unknown"; - int i; + char *cp; set_bit(EFI_BOOT, &efi.flags); set_bit(EFI_64BIT, &efi.flags); @@ -519,25 +517,10 @@ efi_init (void) */ if (efi.systab == NULL) panic("Whoa! Can't find EFI system table.\n"); - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) + if (efi_systab_check_header(&efi.systab->hdr, 1)) panic("Whoa! EFI system table signature incorrect\n"); - if ((efi.systab->hdr.revision >> 16) == 0) - printk(KERN_WARNING "Warning: EFI system table version " - "%d.%02d, expected 1.00 or greater\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); - - /* Show what we know for posterity */ - c16 = __va(efi.systab->fw_vendor); - if (c16) { - for (i = 0;i < (int) sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = *c16++; - vendor[i] = '\0'; - } - printk(KERN_INFO "EFI v%u.%.02u by %s:", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); + efi_systab_report_header(&efi.systab->hdr, efi.systab->fw_vendor); palo_phys = EFI_INVALID_TABLE_ADDR; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 22dc3678cdba..5bb53da48a4b 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -336,15 +336,23 @@ static int __init efi_systab_init(u64 phys) { int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t) : sizeof(efi_system_table_32_t); + const efi_table_hdr_t *hdr; bool over4g = false; void *p; + int ret; - p = early_memremap_ro(phys, size); + hdr = p = early_memremap_ro(phys, size); if (p == NULL) { pr_err("Couldn't map the system table!\n"); return -ENOMEM; } + ret = efi_systab_check_header(hdr, 1); + if (ret) { + early_memunmap(p, size); + return ret; + } + if (efi_enabled(EFI_64BIT)) { const efi_system_table_64_t *systab64 = p; @@ -411,6 +419,7 @@ static int __init efi_systab_init(u64 phys) efi_systab.tables = systab32->tables; } + efi_systab_report_header(hdr, efi_systab.fw_vendor); early_memunmap(p, size); if (IS_ENABLED(CONFIG_X86_32) && over4g) { @@ -419,28 +428,11 @@ static int __init efi_systab_init(u64 phys) } efi.systab = &efi_systab; - - /* - * Verify the EFI Table - */ - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - pr_err("System table signature incorrect!\n"); - return -EINVAL; - } - if ((efi.systab->hdr.revision >> 16) == 0) - pr_err("Warning: System table version %d.%02d, expected 1.00 or greater!\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); - return 0; } void __init efi_init(void) { - efi_char16_t *c16; - char vendor[100] = "unknown"; - int i = 0; - if (IS_ENABLED(CONFIG_X86_32) && (boot_params.efi_info.efi_systab_hi || boot_params.efi_info.efi_memmap_hi)) { @@ -458,24 +450,6 @@ void __init efi_init(void) efi.fw_vendor = (unsigned long)efi.systab->fw_vendor; efi.runtime = (unsigned long)efi.systab->runtime; - /* - * Show what we know for posterity - */ - c16 = early_memremap_ro(efi.systab->fw_vendor, - sizeof(vendor) * sizeof(efi_char16_t)); - if (c16) { - for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) - vendor[i] = c16[i]; - vendor[i] = '\0'; - early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); - } else { - pr_err("Could not map the firmware vendor!\n"); - } - - pr_info("EFI v%u.%.02u by %s\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); - if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables)) return; diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index d99f5b0c8a09..a656bfcd7e27 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -85,11 +85,9 @@ static void __init init_screen_info(void) static int __init uefi_init(void) { - efi_char16_t *c16; void *config_tables; size_t table_size; - char vendor[100] = "unknown"; - int i, retval; + int retval; efi.systab = early_memremap_ro(efi_system_table, sizeof(efi_system_table_t)); @@ -102,34 +100,14 @@ static int __init uefi_init(void) if (IS_ENABLED(CONFIG_64BIT)) set_bit(EFI_64BIT, &efi.flags); - /* - * Verify the EFI Table - */ - if (efi.systab->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) { - pr_err("System table signature incorrect\n"); - retval = -EINVAL; + retval = efi_systab_check_header(&efi.systab->hdr, 2); + if (retval) goto out; - } - if ((efi.systab->hdr.revision >> 16) < 2) - pr_warn("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff); efi.runtime_version = efi.systab->hdr.revision; - /* Show what we know for posterity */ - c16 = early_memremap_ro(efi_to_phys(efi.systab->fw_vendor), - sizeof(vendor) * sizeof(efi_char16_t)); - if (c16) { - for (i = 0; i < (int) sizeof(vendor) - 1 && *c16; ++i) - vendor[i] = c16[i]; - vendor[i] = '\0'; - early_memunmap(c16, sizeof(vendor) * sizeof(efi_char16_t)); - } - - pr_info("EFI v%u.%.02u by %s\n", - efi.systab->hdr.revision >> 16, - efi.systab->hdr.revision & 0xffff, vendor); + efi_systab_report_header(&efi.systab->hdr, + efi_to_phys(efi.systab->fw_vendor)); table_size = sizeof(efi_config_table_64_t) * efi.systab->nr_tables; config_tables = early_memremap_ro(efi_to_phys(efi.systab->tables), diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 41bb2c44cea4..80fe0044f2e2 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -631,6 +631,67 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) return ret; } + +int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, + int min_major_version) +{ + if (systab_hdr->signature != EFI_SYSTEM_TABLE_SIGNATURE) { + pr_err("System table signature incorrect!\n"); + return -EINVAL; + } + + if ((systab_hdr->revision >> 16) < min_major_version) + pr_err("Warning: System table version %d.%02d, expected %d.00 or greater!\n", + systab_hdr->revision >> 16, + systab_hdr->revision & 0xffff, + min_major_version); + + return 0; +} + +#ifndef CONFIG_IA64 +static const efi_char16_t *__init map_fw_vendor(unsigned long fw_vendor, + size_t size) +{ + const efi_char16_t *ret; + + ret = early_memremap_ro(fw_vendor, size); + if (!ret) + pr_err("Could not map the firmware vendor!\n"); + return ret; +} + +static void __init unmap_fw_vendor(const void *fw_vendor, size_t size) +{ + early_memunmap((void *)fw_vendor, size); +} +#else +#define map_fw_vendor(p, s) __va(p) +#define unmap_fw_vendor(v, s) +#endif + +void __init efi_systab_report_header(const efi_table_hdr_t *systab_hdr, + unsigned long fw_vendor) +{ + char vendor[100] = "unknown"; + const efi_char16_t *c16; + size_t i; + + c16 = map_fw_vendor(fw_vendor, sizeof(vendor) * sizeof(efi_char16_t)); + if (c16) { + for (i = 0; i < sizeof(vendor) - 1 && c16[i]; ++i) + vendor[i] = c16[i]; + vendor[i] = '\0'; + + unmap_fw_vendor(c16, sizeof(vendor) * sizeof(efi_char16_t)); + } + + pr_info("EFI v%u.%.02u by %s\n", + systab_hdr->revision >> 16, + systab_hdr->revision & 0xffff, + vendor); +} + #ifdef CONFIG_EFI_VARS_MODULE static int __init efi_load_efivars(void) { diff --git a/include/linux/efi.h b/include/linux/efi.h index a5e210abe4ca..287510e84dfb 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -616,6 +616,10 @@ static inline void efi_esrt_init(void) { } #endif extern int efi_config_parse_tables(void *config_tables, int count, int sz, efi_config_table_type_t *arch_tables); +extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr, + int min_major_version); +extern void efi_systab_report_header(const efi_table_hdr_t *systab_hdr, + unsigned long fw_vendor); extern u64 efi_get_iobase (void); extern int efi_mem_type(unsigned long phys_addr); extern u64 efi_mem_attributes (unsigned long phys_addr); -- cgit v1.2.3 From 3a0701dc7ff8ebe1031a9f64c99c638929cd2d70 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 17:17:27 +0100 Subject: efi: Make efi_config_init() x86 only The efi_config_init() routine is no longer shared with ia64 so let's move it into the x86 arch code before making further x86 specific changes to it. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 30 ++++++++++++++++++++++++++++++ drivers/firmware/efi/efi.c | 31 ------------------------------- include/linux/efi.h | 1 - 3 files changed, 30 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 5bb53da48a4b..f1033f7f9e39 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -431,6 +431,36 @@ static int __init efi_systab_init(u64 phys) return 0; } +static int __init efi_config_init(efi_config_table_type_t *arch_tables) +{ + void *config_tables; + int sz, ret; + + if (efi.systab->nr_tables == 0) + return 0; + + if (efi_enabled(EFI_64BIT)) + sz = sizeof(efi_config_table_64_t); + else + sz = sizeof(efi_config_table_32_t); + + /* + * Let's see what config tables the firmware passed to us. + */ + config_tables = early_memremap(efi.systab->tables, + efi.systab->nr_tables * sz); + if (config_tables == NULL) { + pr_err("Could not map Configuration table!\n"); + return -ENOMEM; + } + + ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, + arch_tables); + + early_memunmap(config_tables, efi.systab->nr_tables * sz); + return ret; +} + void __init efi_init(void) { if (IS_ENABLED(CONFIG_X86_32) && diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 80fe0044f2e2..2bfd6c0806ce 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -601,37 +601,6 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, return 0; } -int __init efi_config_init(efi_config_table_type_t *arch_tables) -{ - void *config_tables; - int sz, ret; - - if (efi.systab->nr_tables == 0) - return 0; - - if (efi_enabled(EFI_64BIT)) - sz = sizeof(efi_config_table_64_t); - else - sz = sizeof(efi_config_table_32_t); - - /* - * Let's see what config tables the firmware passed to us. - */ - config_tables = early_memremap(efi.systab->tables, - efi.systab->nr_tables * sz); - if (config_tables == NULL) { - pr_err("Could not map Configuration table!\n"); - return -ENOMEM; - } - - ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, - arch_tables); - - early_memunmap(config_tables, efi.systab->nr_tables * sz); - return ret; -} - - int __init efi_systab_check_header(const efi_table_hdr_t *systab_hdr, int min_major_version) { diff --git a/include/linux/efi.h b/include/linux/efi.h index 287510e84dfb..d61c25fd5824 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -608,7 +608,6 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md, extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf, struct efi_mem_range *mem); -extern int efi_config_init(efi_config_table_type_t *arch_tables); #ifdef CONFIG_EFI_ESRT extern void __init efi_esrt_init(void); #else -- cgit v1.2.3 From 06c0bd93434c5b9b284773f90bb054aff591d5be Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Jan 2020 14:40:57 +0100 Subject: efi: Clean up config_parse_tables() config_parse_tables() is a jumble of pointer arithmetic, due to the fact that on x86, we may be dealing with firmware whose native word size differs from the kernel's. This is not a concern on other architectures, and doesn't quite justify the state of the code, so let's clean it up by adding a non-x86 code path, constifying statically allocated tables and replacing preprocessor conditionals with IS_ENABLED() checks. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/ia64/kernel/efi.c | 3 +-- arch/x86/platform/efi/efi.c | 6 +++--- drivers/firmware/efi/arm-init.c | 5 ++--- drivers/firmware/efi/efi.c | 47 ++++++++++++++++++++--------------------- include/linux/efi.h | 5 +++-- 5 files changed, 32 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 3b5cf551489c..f69f3fe0532e 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c @@ -56,7 +56,7 @@ unsigned long __initdata esi_phys = EFI_INVALID_TABLE_ADDR; unsigned long hcdp_phys = EFI_INVALID_TABLE_ADDR; unsigned long sal_systab_phys = EFI_INVALID_TABLE_ADDR; -static __initdata efi_config_table_type_t arch_tables[] = { +static const efi_config_table_type_t arch_tables[] __initconst = { {ESI_TABLE_GUID, "ESI", &esi_phys}, {HCDP_TABLE_GUID, "HCDP", &hcdp_phys}, {MPS_TABLE_GUID, "MPS", &mps_phys}, @@ -533,7 +533,6 @@ efi_init (void) if (efi_config_parse_tables(__va(efi_systab->tables), efi_systab->nr_tables, - sizeof(efi_config_table_t), arch_tables) != 0) return; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index f1033f7f9e39..47367f4d82d0 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -60,7 +60,7 @@ static u64 efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; -static efi_config_table_type_t arch_tables[] __initdata = { +static const efi_config_table_type_t arch_tables[] __initconst = { {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, {UGA_IO_PROTOCOL_GUID, "UGA", &uga_phys}, #ifdef CONFIG_X86_UV @@ -431,7 +431,7 @@ static int __init efi_systab_init(u64 phys) return 0; } -static int __init efi_config_init(efi_config_table_type_t *arch_tables) +static int __init efi_config_init(const efi_config_table_type_t *arch_tables) { void *config_tables; int sz, ret; @@ -454,7 +454,7 @@ static int __init efi_config_init(efi_config_table_type_t *arch_tables) return -ENOMEM; } - ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, sz, + ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, arch_tables); early_memunmap(config_tables, efi.systab->nr_tables * sz); diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index a656bfcd7e27..d1f44c847841 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -55,7 +55,7 @@ static phys_addr_t efi_to_phys(unsigned long addr) static __initdata unsigned long screen_info_table = EFI_INVALID_TABLE_ADDR; -static __initdata efi_config_table_type_t arch_tables[] = { +static const efi_config_table_type_t arch_tables[] __initconst = { {LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID, NULL, &screen_info_table}, {NULL_GUID, NULL, NULL} }; @@ -85,7 +85,7 @@ static void __init init_screen_info(void) static int __init uefi_init(void) { - void *config_tables; + efi_config_table_t *config_tables; size_t table_size; int retval; @@ -118,7 +118,6 @@ static int __init uefi_init(void) goto out; } retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, - sizeof(efi_config_table_t), arch_tables); if (!retval) diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 2bfd6c0806ce..45de4c4a696b 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -460,7 +460,7 @@ void __init efi_mem_reserve(phys_addr_t addr, u64 size) efi_arch_mem_reserve(addr, size); } -static __initdata efi_config_table_type_t common_tables[] = { +static const efi_config_table_type_t common_tables[] __initconst = { {ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20}, {ACPI_TABLE_GUID, "ACPI", &efi.acpi}, {SMBIOS_TABLE_GUID, "SMBIOS", &efi.smbios}, @@ -477,9 +477,9 @@ static __initdata efi_config_table_type_t common_tables[] = { {NULL_GUID, NULL, NULL}, }; -static __init int match_config_table(efi_guid_t *guid, +static __init int match_config_table(const efi_guid_t *guid, unsigned long table, - efi_config_table_type_t *table_types) + const efi_config_table_type_t *table_types) { int i; @@ -498,39 +498,38 @@ static __init int match_config_table(efi_guid_t *guid, return 0; } -int __init efi_config_parse_tables(void *config_tables, int count, int sz, - efi_config_table_type_t *arch_tables) +int __init efi_config_parse_tables(const efi_config_table_t *config_tables, + int count, + const efi_config_table_type_t *arch_tables) { - void *tablep; + const efi_config_table_64_t *tbl64 = (void *)config_tables; + const efi_config_table_32_t *tbl32 = (void *)config_tables; + const efi_guid_t *guid; + unsigned long table; int i; - tablep = config_tables; pr_info(""); for (i = 0; i < count; i++) { - efi_guid_t guid; - unsigned long table; - - if (efi_enabled(EFI_64BIT)) { - u64 table64; - guid = ((efi_config_table_64_t *)tablep)->guid; - table64 = ((efi_config_table_64_t *)tablep)->table; - table = table64; -#ifndef CONFIG_64BIT - if (table64 >> 32) { + if (!IS_ENABLED(CONFIG_X86)) { + guid = &config_tables[i].guid; + table = (unsigned long)config_tables[i].table; + } else if (efi_enabled(EFI_64BIT)) { + guid = &tbl64[i].guid; + table = tbl64[i].table; + + if (IS_ENABLED(CONFIG_X86_32) && + tbl64[i].table > U32_MAX) { pr_cont("\n"); pr_err("Table located above 4GB, disabling EFI.\n"); return -EINVAL; } -#endif } else { - guid = ((efi_config_table_32_t *)tablep)->guid; - table = ((efi_config_table_32_t *)tablep)->table; + guid = &tbl32[i].guid; + table = tbl32[i].table; } - if (!match_config_table(&guid, table, common_tables)) - match_config_table(&guid, table, arch_tables); - - tablep += sz; + if (!match_config_table(guid, table, common_tables)) + match_config_table(guid, table, arch_tables); } pr_cont("\n"); set_bit(EFI_CONFIG_TABLES, &efi.flags); diff --git a/include/linux/efi.h b/include/linux/efi.h index d61c25fd5824..99a7fcbe5e9b 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -613,8 +613,9 @@ extern void __init efi_esrt_init(void); #else static inline void efi_esrt_init(void) { } #endif -extern int efi_config_parse_tables(void *config_tables, int count, int sz, - efi_config_table_type_t *arch_tables); +extern int efi_config_parse_tables(const efi_config_table_t *config_tables, + int count, + const efi_config_table_type_t *arch_tables); extern int efi_systab_check_header(const efi_table_hdr_t *systab_hdr, int min_major_version); extern void efi_systab_report_header(const efi_table_hdr_t *systab_hdr, -- cgit v1.2.3 From 9cd437ac0ef4f324a92e2579784b03bb487ae7fb Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 20 Jan 2020 17:23:21 +0100 Subject: efi/x86: Make fw_vendor, config_table and runtime sysfs nodes x86 specific There is some code that exposes physical addresses of certain parts of the EFI firmware implementation via sysfs nodes. These nodes are only used on x86, and are of dubious value to begin with, so let's move their handling into the x86 arch code. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 2 ++ arch/x86/kernel/kexec-bzimage64.c | 4 +-- arch/x86/platform/efi/efi.c | 60 +++++++++++++++++++++++++++++++-------- arch/x86/platform/efi/quirks.c | 2 +- drivers/firmware/efi/arm-init.c | 3 -- drivers/firmware/efi/efi.c | 40 ++++---------------------- include/linux/efi.h | 3 -- 7 files changed, 59 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index ee867f01b2f6..78fc28da2e29 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -10,6 +10,8 @@ #include #include +extern unsigned long efi_fw_vendor, efi_config_table; + /* * We map the EFI regions needed for runtime services non-contiguously, * with preserved alignment on virtual addresses starting from -4G down diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c index f400678bd345..db6578d45157 100644 --- a/arch/x86/kernel/kexec-bzimage64.c +++ b/arch/x86/kernel/kexec-bzimage64.c @@ -141,8 +141,8 @@ prepare_add_efi_setup_data(struct boot_params *params, struct setup_data *sd = (void *)params + efi_setup_data_offset; struct efi_setup_data *esd = (void *)sd + sizeof(struct setup_data); - esd->fw_vendor = efi.fw_vendor; - esd->tables = efi.config_table; + esd->fw_vendor = efi_fw_vendor; + esd->tables = efi_config_table; esd->smbios = efi.smbios; sd->type = SETUP_EFI; diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 7d932452a40f..6fa412e156c7 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -59,6 +59,9 @@ static u64 efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; +static unsigned long efi_runtime, efi_nr_tables; + +unsigned long efi_fw_vendor, efi_config_table; static const efi_config_table_type_t arch_tables[] __initconst = { {EFI_PROPERTIES_TABLE_GUID, "PROP", &prop_phys}, @@ -78,9 +81,9 @@ static const unsigned long * const efi_tables[] = { #ifdef CONFIG_X86_UV &uv_systab_phys, #endif - &efi.fw_vendor, - &efi.runtime, - &efi.config_table, + &efi_fw_vendor, + &efi_runtime, + &efi_config_table, &efi.esrt, &prop_phys, &efi_mem_attr_table, @@ -434,7 +437,7 @@ static int __init efi_config_init(const efi_config_table_type_t *arch_tables) void *config_tables; int sz, ret; - if (efi.systab->nr_tables == 0) + if (efi_nr_tables == 0) return 0; if (efi_enabled(EFI_64BIT)) @@ -445,17 +448,16 @@ static int __init efi_config_init(const efi_config_table_type_t *arch_tables) /* * Let's see what config tables the firmware passed to us. */ - config_tables = early_memremap(efi.systab->tables, - efi.systab->nr_tables * sz); + config_tables = early_memremap(efi_config_table, efi_nr_tables * sz); if (config_tables == NULL) { pr_err("Could not map Configuration table!\n"); return -ENOMEM; } - ret = efi_config_parse_tables(config_tables, efi.systab->nr_tables, + ret = efi_config_parse_tables(config_tables, efi_nr_tables, arch_tables); - early_memunmap(config_tables, efi.systab->nr_tables * sz); + early_memunmap(config_tables, efi_nr_tables * sz); return ret; } @@ -474,11 +476,12 @@ void __init efi_init(void) if (efi_systab_init(efi_systab_phys)) return; - efi.config_table = (unsigned long)efi.systab->tables; - efi.fw_vendor = (unsigned long)efi.systab->fw_vendor; - efi.runtime = (unsigned long)efi.systab->runtime; + efi_config_table = (unsigned long)efi.systab->tables; + efi_nr_tables = efi.systab->nr_tables; + efi_fw_vendor = (unsigned long)efi.systab->fw_vendor; + efi_runtime = (unsigned long)efi.systab->runtime; - if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables)) + if (efi_reuse_config(efi_config_table, efi_nr_tables)) return; if (efi_config_init(arch_tables)) @@ -1023,3 +1026,36 @@ char *efi_systab_show_arch(char *str) str += sprintf(str, "UGA=0x%lx\n", uga_phys); return str; } + +#define EFI_FIELD(var) efi_ ## var + +#define EFI_ATTR_SHOW(name) \ +static ssize_t name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ +} + +EFI_ATTR_SHOW(fw_vendor); +EFI_ATTR_SHOW(runtime); +EFI_ATTR_SHOW(config_table); + +struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); +struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); +struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); + +umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + if (attr == &efi_attr_fw_vendor.attr) { + if (efi_enabled(EFI_PARAVIRT) || + efi_fw_vendor == EFI_INVALID_TABLE_ADDR) + return 0; + } else if (attr == &efi_attr_runtime.attr) { + if (efi_runtime == EFI_INVALID_TABLE_ADDR) + return 0; + } else if (attr == &efi_attr_config_table.attr) { + if (efi_config_table == EFI_INVALID_TABLE_ADDR) + return 0; + } + return attr->mode; +} diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 88d32c06cffa..b0e0161e2e8e 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -537,7 +537,7 @@ int __init efi_reuse_config(u64 tables, int nr_tables) goto out_memremap; } - for (i = 0; i < efi.systab->nr_tables; i++) { + for (i = 0; i < nr_tables; i++) { efi_guid_t guid; guid = ((efi_config_table_64_t *)p)->guid; diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index d1f44c847841..5fc2f6813b84 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -120,9 +120,6 @@ static int __init uefi_init(void) retval = efi_config_parse_tables(config_tables, efi.systab->nr_tables, arch_tables); - if (!retval) - efi.config_table = (unsigned long)efi.systab->tables; - early_memunmap(config_tables, table_size); out: early_memunmap(efi.systab, sizeof(efi_system_table_t)); diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 45de4c4a696b..718dddfa0a0b 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -39,9 +39,6 @@ struct efi __read_mostly efi = { .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, .smbios3 = EFI_INVALID_TABLE_ADDR, - .fw_vendor = EFI_INVALID_TABLE_ADDR, - .runtime = EFI_INVALID_TABLE_ADDR, - .config_table = EFI_INVALID_TABLE_ADDR, .esrt = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, .tpm_final_log = EFI_INVALID_TABLE_ADDR, @@ -142,55 +139,30 @@ static ssize_t systab_show(struct kobject *kobj, static struct kobj_attribute efi_attr_systab = __ATTR_RO_MODE(systab, 0400); -#define EFI_FIELD(var) efi.var - -#define EFI_ATTR_SHOW(name) \ -static ssize_t name##_show(struct kobject *kobj, \ - struct kobj_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "0x%lx\n", EFI_FIELD(name)); \ -} - -EFI_ATTR_SHOW(fw_vendor); -EFI_ATTR_SHOW(runtime); -EFI_ATTR_SHOW(config_table); - static ssize_t fw_platform_size_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); } -static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); -static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); -static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); +extern __weak struct kobj_attribute efi_attr_fw_vendor; +extern __weak struct kobj_attribute efi_attr_runtime; +extern __weak struct kobj_attribute efi_attr_config_table; static struct kobj_attribute efi_attr_fw_platform_size = __ATTR_RO(fw_platform_size); static struct attribute *efi_subsys_attrs[] = { &efi_attr_systab.attr, + &efi_attr_fw_platform_size.attr, &efi_attr_fw_vendor.attr, &efi_attr_runtime.attr, &efi_attr_config_table.attr, - &efi_attr_fw_platform_size.attr, NULL, }; -static umode_t efi_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) +umode_t __weak efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, + int n) { - if (attr == &efi_attr_fw_vendor.attr) { - if (efi_enabled(EFI_PARAVIRT) || - efi.fw_vendor == EFI_INVALID_TABLE_ADDR) - return 0; - } else if (attr == &efi_attr_runtime.attr) { - if (efi.runtime == EFI_INVALID_TABLE_ADDR) - return 0; - } else if (attr == &efi_attr_config_table.attr) { - if (efi.config_table == EFI_INVALID_TABLE_ADDR) - return 0; - } - return attr->mode; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 99a7fcbe5e9b..a42045568df3 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -535,9 +535,6 @@ extern struct efi { unsigned long acpi20; /* ACPI table (ACPI 2.0) */ unsigned long smbios; /* SMBIOS table (32 bit entry point) */ unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long fw_vendor; /* fw_vendor */ - unsigned long runtime; /* runtime table */ - unsigned long config_table; /* config tables */ unsigned long esrt; /* ESRT table */ unsigned long tpm_log; /* TPM2 Event Log table */ unsigned long tpm_final_log; /* TPM2 Final Events Log table */ -- cgit v1.2.3 From 59f2a619a2db86111e8bb30f349aebff6eb75baa Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Jan 2020 09:44:43 +0100 Subject: efi: Add 'runtime' pointer to struct efi Instead of going through the EFI system table each time, just copy the runtime services table pointer into struct efi directly. This is the last use of the system table pointer in struct efi, allowing us to drop it in a future patch, along with a fair amount of quirky handling of the translated address. Note that usually, the runtime services pointer changes value during the call to SetVirtualAddressMap(), so grab the updated value as soon as that call returns. (Mixed mode uses a 1:1 mapping, and kexec boot enters with the updated address in the system table, so in those cases, we don't need to do anything here) Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 3 ++- arch/x86/kernel/asm-offsets_32.c | 5 +++++ arch/x86/platform/efi/efi.c | 9 ++++++--- arch/x86/platform/efi/efi_32.c | 13 ++++++++----- arch/x86/platform/efi/efi_64.c | 14 ++++++++------ arch/x86/platform/efi/efi_stub_32.S | 21 ++++++++++++++++----- drivers/firmware/efi/arm-init.c | 1 + drivers/firmware/efi/runtime-wrappers.c | 4 ++-- include/linux/efi.h | 1 + 9 files changed, 49 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index 78fc28da2e29..0de57151c732 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -218,7 +218,8 @@ extern void efi_thunk_runtime_setup(void); efi_status_t efi_set_virtual_address_map(unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, - efi_memory_desc_t *virtual_map); + efi_memory_desc_t *virtual_map, + unsigned long systab_phys); /* arch specific definitions used by the stub code */ diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c index 82826f2275cc..2b4256ebe86e 100644 --- a/arch/x86/kernel/asm-offsets_32.c +++ b/arch/x86/kernel/asm-offsets_32.c @@ -3,6 +3,8 @@ # error "Please do not build this file directly, build asm-offsets.c instead" #endif +#include + #include #define __SYSCALL_I386(nr, sym, qual) [nr] = 1, @@ -64,4 +66,7 @@ void foo(void) BLANK(); DEFINE(__NR_syscall_max, sizeof(syscalls) - 1); DEFINE(NR_syscalls, sizeof(syscalls)); + + BLANK(); + DEFINE(EFI_svam, offsetof(efi_runtime_services_t, set_virtual_address_map)); } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index e4ee9a37254a..6bd8aae235d2 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -55,8 +55,8 @@ #include static efi_system_table_t efi_systab __initdata; -static u64 efi_systab_phys __initdata; +static unsigned long efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; static unsigned long efi_runtime, efi_nr_tables; @@ -335,7 +335,7 @@ void __init efi_print_memmap(void) } } -static int __init efi_systab_init(u64 phys) +static int __init efi_systab_init(unsigned long phys) { int size = efi_enabled(EFI_64BIT) ? sizeof(efi_system_table_64_t) : sizeof(efi_system_table_32_t); @@ -949,7 +949,8 @@ static void __init __efi_enter_virtual_mode(void) status = efi_set_virtual_address_map(efi.memmap.desc_size * count, efi.memmap.desc_size, efi.memmap.desc_version, - (efi_memory_desc_t *)pa); + (efi_memory_desc_t *)pa, + efi_systab_phys); if (status != EFI_SUCCESS) { pr_err("Unable to switch EFI into virtual mode (status=%lx)!\n", status); @@ -983,6 +984,8 @@ void __init efi_enter_virtual_mode(void) if (efi_enabled(EFI_PARAVIRT)) return; + efi.runtime = (efi_runtime_services_t *)efi_runtime; + if (efi_setup) kexec_enter_virtual_mode(); else diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index 081d466002c9..c049c432745d 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c @@ -66,14 +66,16 @@ void __init efi_map_region(efi_memory_desc_t *md) void __init efi_map_region_fixed(efi_memory_desc_t *md) {} void __init parse_efi_setup(u64 phys_addr, u32 data_len) {} -efi_status_t efi_call_svam(efi_set_virtual_address_map_t *__efiapi *, - u32, u32, u32, void *); +efi_status_t efi_call_svam(efi_runtime_services_t * const *, + u32, u32, u32, void *, u32); efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, - efi_memory_desc_t *virtual_map) + efi_memory_desc_t *virtual_map, + unsigned long systab_phys) { + const efi_system_table_t *systab = (efi_system_table_t *)systab_phys; struct desc_ptr gdt_descr; efi_status_t status; unsigned long flags; @@ -90,9 +92,10 @@ efi_status_t __init efi_set_virtual_address_map(unsigned long memory_map_size, /* Disable interrupts around EFI calls: */ local_irq_save(flags); - status = efi_call_svam(&efi.systab->runtime->set_virtual_address_map, + status = efi_call_svam(&systab->runtime, memory_map_size, descriptor_size, - descriptor_version, virtual_map); + descriptor_version, virtual_map, + __pa(&efi.runtime)); local_irq_restore(flags); load_fixmap_gdt(0); diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index fa8506e76bbe..f78f7da666fb 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -500,12 +500,9 @@ static DEFINE_SPINLOCK(efi_runtime_lock); */ #define __efi_thunk(func, ...) \ ({ \ - efi_runtime_services_32_t *__rt; \ unsigned short __ds, __es; \ efi_status_t ____s; \ \ - __rt = (void *)(unsigned long)efi.systab->mixed_mode.runtime; \ - \ savesegment(ds, __ds); \ savesegment(es, __es); \ \ @@ -513,7 +510,7 @@ static DEFINE_SPINLOCK(efi_runtime_lock); loadsegment(ds, __KERNEL_DS); \ loadsegment(es, __KERNEL_DS); \ \ - ____s = efi64_thunk(__rt->func, __VA_ARGS__); \ + ____s = efi64_thunk(efi.runtime->mixed_mode.func, __VA_ARGS__); \ \ loadsegment(ds, __ds); \ loadsegment(es, __es); \ @@ -886,8 +883,10 @@ efi_status_t __init __no_sanitize_address efi_set_virtual_address_map(unsigned long memory_map_size, unsigned long descriptor_size, u32 descriptor_version, - efi_memory_desc_t *virtual_map) + efi_memory_desc_t *virtual_map, + unsigned long systab_phys) { + const efi_system_table_t *systab = (efi_system_table_t *)systab_phys; efi_status_t status; unsigned long flags; pgd_t *save_pgd = NULL; @@ -910,13 +909,16 @@ efi_set_virtual_address_map(unsigned long memory_map_size, /* Disable interrupts around EFI calls: */ local_irq_save(flags); - status = efi_call(efi.systab->runtime->set_virtual_address_map, + status = efi_call(efi.runtime->set_virtual_address_map, memory_map_size, descriptor_size, descriptor_version, virtual_map); local_irq_restore(flags); kernel_fpu_end(); + /* grab the virtually remapped EFI runtime services table pointer */ + efi.runtime = READ_ONCE(systab->runtime); + if (save_pgd) efi_uv1_memmap_phys_epilog(save_pgd); else diff --git a/arch/x86/platform/efi/efi_stub_32.S b/arch/x86/platform/efi/efi_stub_32.S index 75c46e7a809f..09237236fb25 100644 --- a/arch/x86/platform/efi/efi_stub_32.S +++ b/arch/x86/platform/efi/efi_stub_32.S @@ -8,14 +8,20 @@ #include #include +#include #include __INIT SYM_FUNC_START(efi_call_svam) - push 8(%esp) - push 8(%esp) + push %ebp + movl %esp, %ebp + push %ebx + + push 16(%esp) + push 16(%esp) push %ecx push %edx + movl %eax, %ebx // &systab_phys->runtime /* * Switch to the flat mapped alias of this routine, by jumping to the @@ -35,15 +41,20 @@ SYM_FUNC_START(efi_call_svam) subl $__PAGE_OFFSET, %esp /* call the EFI routine */ - call *(%eax) + movl (%eax), %eax + call *EFI_svam(%eax) - /* convert ESP back to a kernel VA, and pop the outgoing args */ - addl $__PAGE_OFFSET + 16, %esp + /* grab the virtually remapped EFI runtime services table pointer */ + movl (%ebx), %ecx + movl 36(%esp), %edx // &efi.runtime + movl %ecx, (%edx) /* re-enable paging */ movl %cr0, %edx orl $0x80000000, %edx movl %edx, %cr0 + pop %ebx + leave ret SYM_FUNC_END(efi_call_svam) diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 5fc2f6813b84..77048f7a9659 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -104,6 +104,7 @@ static int __init uefi_init(void) if (retval) goto out; + efi.runtime = efi.systab->runtime; efi.runtime_version = efi.systab->hdr.revision; efi_systab_report_header(&efi.systab->hdr, diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 65fffaa22210..1410beaef5c3 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -40,9 +40,9 @@ * code doesn't get too cluttered: */ #define efi_call_virt(f, args...) \ - efi_call_virt_pointer(efi.systab->runtime, f, args) + efi_call_virt_pointer(efi.runtime, f, args) #define __efi_call_virt(f, args...) \ - __efi_call_virt_pointer(efi.systab->runtime, f, args) + __efi_call_virt_pointer(efi.runtime, f, args) struct efi_runtime_work efi_rts_work; diff --git a/include/linux/efi.h b/include/linux/efi.h index a42045568df3..1f69c4c2dd5c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -529,6 +529,7 @@ typedef struct { * All runtime access to EFI goes through this structure: */ extern struct efi { + const efi_runtime_services_t *runtime; /* EFI runtime services table */ efi_system_table_t *systab; /* EFI system table */ unsigned int runtime_version; /* Runtime services version */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ -- cgit v1.2.3 From fd26830423e5f7442001f090cd4a53f4b6c3d9fa Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Jan 2020 10:16:32 +0100 Subject: efi/x86: Drop 'systab' member from struct efi The systab member in struct efi has outlived its usefulness, now that we have better ways to access the only piece of information we are interested in after init, which is the EFI runtime services table address. So instead of instantiating a doctored copy at early boot with lots of mangled values, and switching the pointer when switching into virtual mode, let's grab the values we need directly, and get rid of the systab pointer entirely. Tested-by: Tony Luck # arch/ia64 Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 87 ++++++++------------------------------------- include/linux/efi.h | 52 ++++++++++++++------------- 2 files changed, 41 insertions(+), 98 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 6bd8aae235d2..43b24e149312 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -54,8 +54,6 @@ #include #include -static efi_system_table_t efi_systab __initdata; - static unsigned long efi_systab_phys __initdata; static unsigned long prop_phys = EFI_INVALID_TABLE_ADDR; static unsigned long uga_phys = EFI_INVALID_TABLE_ADDR; @@ -359,28 +357,8 @@ static int __init efi_systab_init(unsigned long phys) if (efi_enabled(EFI_64BIT)) { const efi_system_table_64_t *systab64 = p; - efi_systab.hdr = systab64->hdr; - efi_systab.fw_vendor = systab64->fw_vendor; - efi_systab.fw_revision = systab64->fw_revision; - efi_systab.con_in_handle = systab64->con_in_handle; - efi_systab.con_in = systab64->con_in; - efi_systab.con_out_handle = systab64->con_out_handle; - efi_systab.con_out = (void *)(unsigned long)systab64->con_out; - efi_systab.stderr_handle = systab64->stderr_handle; - efi_systab.stderr = systab64->stderr; - efi_systab.runtime = (void *)(unsigned long)systab64->runtime; - efi_systab.boottime = (void *)(unsigned long)systab64->boottime; - efi_systab.nr_tables = systab64->nr_tables; - efi_systab.tables = systab64->tables; - - over4g = systab64->con_in_handle > U32_MAX || - systab64->con_in > U32_MAX || - systab64->con_out_handle > U32_MAX || - systab64->con_out > U32_MAX || - systab64->stderr_handle > U32_MAX || - systab64->stderr > U32_MAX || - systab64->runtime > U32_MAX || - systab64->boottime > U32_MAX; + efi_runtime = systab64->runtime; + over4g = systab64->runtime > U32_MAX; if (efi_setup) { struct efi_setup_data *data; @@ -391,38 +369,33 @@ static int __init efi_systab_init(unsigned long phys) return -ENOMEM; } - efi_systab.fw_vendor = (unsigned long)data->fw_vendor; - efi_systab.tables = (unsigned long)data->tables; + efi_fw_vendor = (unsigned long)data->fw_vendor; + efi_config_table = (unsigned long)data->tables; over4g |= data->fw_vendor > U32_MAX || data->tables > U32_MAX; early_memunmap(data, sizeof(*data)); } else { + efi_fw_vendor = systab64->fw_vendor; + efi_config_table = systab64->tables; + over4g |= systab64->fw_vendor > U32_MAX || systab64->tables > U32_MAX; } + efi_nr_tables = systab64->nr_tables; } else { const efi_system_table_32_t *systab32 = p; - efi_systab.hdr = systab32->hdr; - efi_systab.fw_vendor = systab32->fw_vendor; - efi_systab.fw_revision = systab32->fw_revision; - efi_systab.con_in_handle = systab32->con_in_handle; - efi_systab.con_in = systab32->con_in; - efi_systab.con_out_handle = systab32->con_out_handle; - efi_systab.con_out = (void *)(unsigned long)systab32->con_out; - efi_systab.stderr_handle = systab32->stderr_handle; - efi_systab.stderr = systab32->stderr; - efi_systab.runtime = (void *)(unsigned long)systab32->runtime; - efi_systab.boottime = (void *)(unsigned long)systab32->boottime; - efi_systab.nr_tables = systab32->nr_tables; - efi_systab.tables = systab32->tables; + efi_fw_vendor = systab32->fw_vendor; + efi_runtime = systab32->runtime; + efi_config_table = systab32->tables; + efi_nr_tables = systab32->nr_tables; } efi.runtime_version = hdr->revision; - efi_systab_report_header(hdr, efi_systab.fw_vendor); + efi_systab_report_header(hdr, efi_fw_vendor); early_memunmap(p, size); if (IS_ENABLED(CONFIG_X86_32) && over4g) { @@ -430,7 +403,6 @@ static int __init efi_systab_init(unsigned long phys) return -EINVAL; } - efi.systab = &efi_systab; return 0; } @@ -478,11 +450,6 @@ void __init efi_init(void) if (efi_systab_init(efi_systab_phys)) return; - efi_config_table = (unsigned long)efi.systab->tables; - efi_nr_tables = efi.systab->nr_tables; - efi_fw_vendor = (unsigned long)efi.systab->fw_vendor; - efi_runtime = (unsigned long)efi.systab->runtime; - if (efi_reuse_config(efi_config_table, efi_nr_tables)) return; @@ -624,20 +591,6 @@ static void __init efi_merge_regions(void) } } -static void __init get_systab_virt_addr(efi_memory_desc_t *md) -{ - unsigned long size; - u64 end, systab; - - size = md->num_pages << EFI_PAGE_SHIFT; - end = md->phys_addr + size; - systab = efi_systab_phys; - if (md->phys_addr <= systab && systab < end) { - systab += md->virt_addr - md->phys_addr; - efi.systab = (efi_system_table_t *)(unsigned long)systab; - } -} - static void *realloc_pages(void *old_memmap, int old_shift) { void *ret; @@ -793,7 +746,6 @@ static void * __init efi_map_regions(int *count, int *pg_shift) continue; efi_map_region(md); - get_systab_virt_addr(md); if (left < desc_size) { new_memmap = realloc_pages(new_memmap, *pg_shift); @@ -819,8 +771,6 @@ static void __init kexec_enter_virtual_mode(void) efi_memory_desc_t *md; unsigned int num_pages; - efi.systab = NULL; - /* * We don't do virtual mode, since we don't do runtime services, on * non-native EFI. With the UV1 memmap, we don't do runtime services in @@ -843,10 +793,8 @@ static void __init kexec_enter_virtual_mode(void) * Map efi regions which were passed via setup_data. The virt_addr is a * fixed addr which was used in first kernel of a kexec boot. */ - for_each_efi_memory_desc(md) { + for_each_efi_memory_desc(md) efi_map_region_fixed(md); /* FIXME: add error handling */ - get_systab_virt_addr(md); - } /* * Unregister the early EFI memmap from efi_init() and install @@ -861,8 +809,6 @@ static void __init kexec_enter_virtual_mode(void) return; } - BUG_ON(!efi.systab); - num_pages = ALIGN(efi.memmap.nr_map * efi.memmap.desc_size, PAGE_SIZE); num_pages >>= PAGE_SHIFT; @@ -905,8 +851,6 @@ static void __init __efi_enter_virtual_mode(void) efi_status_t status; unsigned long pa; - efi.systab = NULL; - if (efi_alloc_page_tables()) { pr_err("Failed to allocate EFI page tables\n"); goto err; @@ -938,9 +882,6 @@ static void __init __efi_enter_virtual_mode(void) efi_print_memmap(); } - if (WARN_ON(!efi.systab)) - goto err; - if (efi_setup_page_tables(pa, 1 << pg_shift)) goto err; diff --git a/include/linux/efi.h b/include/linux/efi.h index 1f69c4c2dd5c..575e6aa39514 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -530,31 +530,33 @@ typedef struct { */ extern struct efi { const efi_runtime_services_t *runtime; /* EFI runtime services table */ - efi_system_table_t *systab; /* EFI system table */ - unsigned int runtime_version; /* Runtime services version */ - unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ - unsigned long acpi20; /* ACPI table (ACPI 2.0) */ - unsigned long smbios; /* SMBIOS table (32 bit entry point) */ - unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ - unsigned long esrt; /* ESRT table */ - unsigned long tpm_log; /* TPM2 Event Log table */ - unsigned long tpm_final_log; /* TPM2 Final Events Log table */ - efi_get_time_t *get_time; - efi_set_time_t *set_time; - efi_get_wakeup_time_t *get_wakeup_time; - efi_set_wakeup_time_t *set_wakeup_time; - efi_get_variable_t *get_variable; - efi_get_next_variable_t *get_next_variable; - efi_set_variable_t *set_variable; - efi_set_variable_t *set_variable_nonblocking; - efi_query_variable_info_t *query_variable_info; - efi_query_variable_info_t *query_variable_info_nonblocking; - efi_update_capsule_t *update_capsule; - efi_query_capsule_caps_t *query_capsule_caps; - efi_get_next_high_mono_count_t *get_next_high_mono_count; - efi_reset_system_t *reset_system; - struct efi_memory_map memmap; - unsigned long flags; + unsigned int runtime_version; /* Runtime services version */ + + unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ + unsigned long acpi20; /* ACPI table (ACPI 2.0) */ + unsigned long smbios; /* SMBIOS table (32 bit entry point) */ + unsigned long smbios3; /* SMBIOS table (64 bit entry point) */ + unsigned long esrt; /* ESRT table */ + unsigned long tpm_log; /* TPM2 Event Log table */ + unsigned long tpm_final_log; /* TPM2 Final Events Log table */ + + efi_get_time_t *get_time; + efi_set_time_t *set_time; + efi_get_wakeup_time_t *get_wakeup_time; + efi_set_wakeup_time_t *set_wakeup_time; + efi_get_variable_t *get_variable; + efi_get_next_variable_t *get_next_variable; + efi_set_variable_t *set_variable; + efi_set_variable_t *set_variable_nonblocking; + efi_query_variable_info_t *query_variable_info; + efi_query_variable_info_t *query_variable_info_nonblocking; + efi_update_capsule_t *update_capsule; + efi_query_capsule_caps_t *query_capsule_caps; + efi_get_next_high_mono_count_t *get_next_high_mono_count; + efi_reset_system_t *reset_system; + + struct efi_memory_map memmap; + unsigned long flags; } efi; extern struct mm_struct efi_mm; -- cgit v1.2.3 From 3b2e4b4c634cc7dd4730ce3e1c75b8206dcc4b04 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 18 Feb 2020 10:19:34 +0100 Subject: efi/arm: Move FDT specific definitions into fdtparams.c Push the FDT params specific types and definition into fdtparams.c, and instead, pass a reference to the memory map data structure and populate it directly, and return the system table address as the return value. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/arm-init.c | 17 ++++++----------- drivers/firmware/efi/fdtparams.c | 30 +++++++++++++++++++++++------- include/linux/efi.h | 10 +--------- 3 files changed, 30 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 76bf5b22e49e..2791a8048f30 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -205,17 +205,13 @@ static __init void reserve_regions(void) void __init efi_init(void) { struct efi_memory_map_data data; - struct efi_fdt_params params; + u64 efi_system_table; /* Grab UEFI information placed in FDT by stub */ - if (!efi_get_fdt_params(¶ms)) + efi_system_table = efi_get_fdt_params(&data); + if (!efi_system_table) return; - data.desc_version = params.desc_ver; - data.desc_size = params.desc_size; - data.size = params.mmap_size; - data.phys_map = params.mmap; - if (efi_memmap_init_early(&data) < 0) { /* * If we are booting via UEFI, the UEFI memory map is the only @@ -229,7 +225,7 @@ void __init efi_init(void) "Unexpected EFI_MEMORY_DESCRIPTOR version %ld", efi.memmap.desc_version); - if (uefi_init(params.system_table) < 0) { + if (uefi_init(efi_system_table) < 0) { efi_memmap_unmap(); return; } @@ -237,9 +233,8 @@ void __init efi_init(void) reserve_regions(); efi_esrt_init(); - memblock_reserve(params.mmap & PAGE_MASK, - PAGE_ALIGN(params.mmap_size + - (params.mmap & ~PAGE_MASK))); + memblock_reserve(data.phys_map & PAGE_MASK, + PAGE_ALIGN(data.size + (data.phys_map & ~PAGE_MASK))); init_screen_info(); diff --git a/drivers/firmware/efi/fdtparams.c b/drivers/firmware/efi/fdtparams.c index 3de343faffc6..7a384b307c56 100644 --- a/drivers/firmware/efi/fdtparams.c +++ b/drivers/firmware/efi/fdtparams.c @@ -18,6 +18,14 @@ sizeof_field(struct efi_fdt_params, field) \ } +struct efi_fdt_params { + u64 system_table; + u64 mmap; + u32 mmap_size; + u32 desc_size; + u32 desc_ver; +}; + struct params { const char name[32]; const char propname[32]; @@ -121,22 +129,30 @@ static int __init fdt_find_uefi_params(unsigned long node, const char *uname, return 0; } -int __init efi_get_fdt_params(struct efi_fdt_params *params) +u64 __init efi_get_fdt_params(struct efi_memory_map_data *memmap) { + struct efi_fdt_params params; struct param_info info; int ret; pr_info("Getting EFI parameters from FDT:\n"); info.found = 0; - info.params = params; + info.params = ¶ms; ret = of_scan_flat_dt(fdt_find_uefi_params, &info); - if (!info.found) + if (!info.found) { pr_info("UEFI not found.\n"); - else if (!ret) - pr_err("Can't find '%s' in device tree!\n", - info.missing); + return 0; + } else if (!ret) { + pr_err("Can't find '%s' in device tree!\n", info.missing); + return 0; + } + + memmap->desc_version = params.desc_ver; + memmap->desc_size = params.desc_size; + memmap->size = params.mmap_size; + memmap->phys_map = params.mmap; - return ret; + return params.system_table; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 575e6aa39514..a0008e3d4e9d 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -477,14 +477,6 @@ struct efi_mem_range { u64 attribute; }; -struct efi_fdt_params { - u64 system_table; - u64 mmap; - u32 mmap_size; - u32 desc_size; - u32 desc_ver; -}; - typedef struct { u32 version; u32 length; @@ -631,7 +623,7 @@ extern void efi_mem_reserve(phys_addr_t addr, u64 size); extern int efi_mem_reserve_persistent(phys_addr_t addr, u64 size); extern void efi_initialize_iomem_resources(struct resource *code_resource, struct resource *data_resource, struct resource *bss_resource); -extern int efi_get_fdt_params(struct efi_fdt_params *params); +extern u64 efi_get_fdt_params(struct efi_memory_map_data *data); extern struct kobject *efi_kobj; extern int efi_reboot_quirk_mode; -- cgit v1.2.3 From 96a3dd3dece8134ba19b0ded7e6663136d3107b9 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 21 Jan 2020 11:17:47 +0100 Subject: efi: Store mask of supported runtime services in struct efi Revision 2.8 of the UEFI spec introduces provisions for firmware to advertise lack of support for certain runtime services at OS runtime. Let's store this mask in struct efi for easy access. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 27 +++++++++++++++++---------- include/linux/efi.h | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 7dbe1487b111..703a019d81b4 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -34,6 +34,7 @@ #include struct efi __read_mostly efi = { + .runtime_supported_mask = EFI_RT_SUPPORTED_ALL, .acpi = EFI_INVALID_TABLE_ADDR, .acpi20 = EFI_INVALID_TABLE_ADDR, .smbios = EFI_INVALID_TABLE_ADDR, @@ -301,16 +302,22 @@ static int __init efisubsys_init(void) if (!efi_enabled(EFI_BOOT)) return 0; - /* - * Since we process only one efi_runtime_service() at a time, an - * ordered workqueue (which creates only one execution context) - * should suffice all our needs. - */ - efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); - if (!efi_rts_wq) { - pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); - clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); - return 0; + if (!efi_enabled(EFI_RUNTIME_SERVICES)) + efi.runtime_supported_mask = 0; + + if (efi.runtime_supported_mask) { + /* + * Since we process only one efi_runtime_service() at a time, an + * ordered workqueue (which creates only one execution context) + * should suffice for all our needs. + */ + efi_rts_wq = alloc_ordered_workqueue("efi_rts_wq", 0); + if (!efi_rts_wq) { + pr_err("Creating efi_rts_wq failed, EFI runtime services disabled.\n"); + clear_bit(EFI_RUNTIME_SERVICES, &efi.flags); + efi.runtime_supported_mask = 0; + return 0; + } } /* We register the efi directory at /sys/firmware/efi */ diff --git a/include/linux/efi.h b/include/linux/efi.h index a0008e3d4e9d..57695f400044 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -523,6 +523,7 @@ typedef struct { extern struct efi { const efi_runtime_services_t *runtime; /* EFI runtime services table */ unsigned int runtime_version; /* Runtime services version */ + unsigned int runtime_supported_mask; unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ @@ -551,6 +552,26 @@ extern struct efi { unsigned long flags; } efi; +#define EFI_RT_SUPPORTED_GET_TIME 0x0001 +#define EFI_RT_SUPPORTED_SET_TIME 0x0002 +#define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 +#define EFI_RT_SUPPORTED_SET_WAKEUP_TIME 0x0008 +#define EFI_RT_SUPPORTED_GET_VARIABLE 0x0010 +#define EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME 0x0020 +#define EFI_RT_SUPPORTED_SET_VARIABLE 0x0040 +#define EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP 0x0080 +#define EFI_RT_SUPPORTED_CONVERT_POINTER 0x0100 +#define EFI_RT_SUPPORTED_GET_NEXT_HIGH_MONOTONIC_COUNT 0x0200 +#define EFI_RT_SUPPORTED_RESET_SYSTEM 0x0400 +#define EFI_RT_SUPPORTED_UPDATE_CAPSULE 0x0800 +#define EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES 0x1000 +#define EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO 0x2000 + +#define EFI_RT_SUPPORTED_ALL 0x3fff + +#define EFI_RT_SUPPORTED_TIME_SERVICES 0x000f +#define EFI_RT_SUPPORTED_VARIABLE_SERVICES 0x0070 + extern struct mm_struct efi_mm; static inline int @@ -761,6 +782,11 @@ static inline bool __pure efi_soft_reserve_enabled(void) return IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && __efi_soft_reserve_enabled(); } + +static inline bool efi_rt_services_supported(unsigned int mask) +{ + return (efi.runtime_supported_mask & mask) == mask; +} #else static inline bool efi_enabled(int feature) { @@ -779,6 +805,11 @@ static inline bool efi_soft_reserve_enabled(void) { return false; } + +static inline bool efi_rt_services_supported(unsigned int mask) +{ + return false; +} #endif extern int efi_status_to_err(efi_status_t status); -- cgit v1.2.3 From fe4db90a80cd12ebe4efe385d40d6636330149ed Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 23 Jan 2020 13:10:25 +0100 Subject: efi: Add support for EFI_RT_PROPERTIES table Take the newly introduced EFI_RT_PROPERTIES_TABLE configuration table into account, which carries a mask of which EFI runtime services are still functional after ExitBootServices() has been called by the OS. Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/efi.c | 12 ++++++++++++ include/linux/efi.h | 9 +++++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 703a019d81b4..a35230517f9c 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -47,6 +47,7 @@ EXPORT_SYMBOL(efi); static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; +static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; struct mm_struct efi_mm = { .mm_rb = RB_ROOT, @@ -449,6 +450,7 @@ static const efi_config_table_type_t common_tables[] __initconst = { {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve}, + {EFI_RT_PROPERTIES_TABLE_GUID, "RTPROP", &rt_prop}, #ifdef CONFIG_EFI_RCI2_TABLE {DELLEMC_EFI_RCI2_TABLE_GUID, NULL, &rci2_table_phys}, #endif @@ -575,6 +577,16 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, } } + if (rt_prop != EFI_INVALID_TABLE_ADDR) { + efi_rt_properties_table_t *tbl; + + tbl = early_memremap(rt_prop, sizeof(*tbl)); + if (tbl) { + efi.runtime_supported_mask &= tbl->runtime_services_supported; + early_memunmap(tbl, sizeof(*tbl)); + } + } + return 0; } diff --git a/include/linux/efi.h b/include/linux/efi.h index 57695f400044..2ab33d5d6ca5 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -334,6 +334,7 @@ void efi_native_runtime_setup(void); #define EFI_TCG2_PROTOCOL_GUID EFI_GUID(0x607f766c, 0x7455, 0x42be, 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f) #define EFI_LOAD_FILE_PROTOCOL_GUID EFI_GUID(0x56ec3091, 0x954c, 0x11d2, 0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b) #define EFI_LOAD_FILE2_PROTOCOL_GUID EFI_GUID(0x4006c0c1, 0xfcb3, 0x403e, 0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d) +#define EFI_RT_PROPERTIES_TABLE_GUID EFI_GUID(0xeb66918a, 0x7eef, 0x402a, 0x84, 0x2e, 0x93, 0x1d, 0x21, 0xc3, 0x8a, 0xe9) #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) @@ -486,6 +487,14 @@ typedef struct { #define EFI_PROPERTIES_TABLE_VERSION 0x00010000 #define EFI_PROPERTIES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA 0x1 +typedef struct { + u16 version; + u16 length; + u32 runtime_services_supported; +} efi_rt_properties_table_t; + +#define EFI_RT_PROPERTIES_TABLE_VERSION 0x1 + #define EFI_INVALID_TABLE_ADDR (~0UL) typedef struct { -- cgit v1.2.3 From 148d3f716c208738d2c7ff8c80be18d89ec8a06e Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Feb 2020 11:06:00 +0100 Subject: efi/libstub: Introduce symbolic constants for the stub major/minor version Now that we have added new ways to load the initrd or the mixed mode kernel, we will also need a way to tell the loader about this. Add symbolic constants for the PE/COFF major/minor version numbers (which fortunately have always been 0x0 for all architectures), so that we can bump them later to document the capabilities of the stub. Signed-off-by: Ard Biesheuvel --- arch/arm/boot/compressed/efi-header.S | 4 ++-- arch/arm64/kernel/efi-header.S | 4 ++-- arch/x86/boot/header.S | 4 ++-- include/linux/pe.h | 3 +++ 4 files changed, 9 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/arm/boot/compressed/efi-header.S b/arch/arm/boot/compressed/efi-header.S index e38fbda02b93..62286da318e7 100644 --- a/arch/arm/boot/compressed/efi-header.S +++ b/arch/arm/boot/compressed/efi-header.S @@ -70,8 +70,8 @@ extra_header_fields: .long SZ_512 @ FileAlignment .short 0 @ MajorOsVersion .short 0 @ MinorOsVersion - .short 0 @ MajorImageVersion - .short 0 @ MinorImageVersion + .short LINUX_EFISTUB_MAJOR_VERSION @ MajorImageVersion + .short LINUX_EFISTUB_MINOR_VERSION @ MinorImageVersion .short 0 @ MajorSubsystemVersion .short 0 @ MinorSubsystemVersion .long 0 @ Win32VersionValue diff --git a/arch/arm64/kernel/efi-header.S b/arch/arm64/kernel/efi-header.S index 40c704c5d3a5..914999ccaf8a 100644 --- a/arch/arm64/kernel/efi-header.S +++ b/arch/arm64/kernel/efi-header.S @@ -36,8 +36,8 @@ extra_header_fields: .long PECOFF_FILE_ALIGNMENT // FileAlignment .short 0 // MajorOperatingSystemVersion .short 0 // MinorOperatingSystemVersion - .short 0 // MajorImageVersion - .short 0 // MinorImageVersion + .short LINUX_EFISTUB_MAJOR_VERSION // MajorImageVersion + .short LINUX_EFISTUB_MINOR_VERSION // MinorImageVersion .short 0 // MajorSubsystemVersion .short 0 // MinorSubsystemVersion .long 0 // Win32VersionValue diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index f65661a58cf8..4ee25e28996f 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -147,8 +147,8 @@ extra_header_fields: .long 0x20 # FileAlignment .word 0 # MajorOperatingSystemVersion .word 0 # MinorOperatingSystemVersion - .word 0 # MajorImageVersion - .word 0 # MinorImageVersion + .word LINUX_EFISTUB_MAJOR_VERSION # MajorImageVersion + .word LINUX_EFISTUB_MINOR_VERSION # MinorImageVersion .word 0 # MajorSubsystemVersion .word 0 # MinorSubsystemVersion .long 0 # Win32VersionValue diff --git a/include/linux/pe.h b/include/linux/pe.h index c86bd3a2f70f..e0869f3eadd6 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -10,6 +10,9 @@ #include +#define LINUX_EFISTUB_MAJOR_VERSION 0x0 +#define LINUX_EFISTUB_MINOR_VERSION 0x0 + #define MZ_MAGIC 0x5a4d /* "MZ" */ #define PE_MAGIC 0x00004550 /* "PE\0\0" */ -- cgit v1.2.3 From dc235d62fc60a6549238eda7ff29769457fe5663 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 20 Feb 2020 11:57:20 +0100 Subject: efi: Bump the Linux EFI stub major version number to #1 Now that we have introduced new, generic ways for the OS loader to interface with Linux kernels during boot, we need to record this fact in a way that allows loaders to discover this information, and fall back to the existing methods for older kernels. Signed-off-by: Ard Biesheuvel --- include/linux/pe.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/pe.h b/include/linux/pe.h index e0869f3eadd6..8ad71d763a77 100644 --- a/include/linux/pe.h +++ b/include/linux/pe.h @@ -10,7 +10,25 @@ #include -#define LINUX_EFISTUB_MAJOR_VERSION 0x0 +/* + * Linux EFI stub v1.0 adds the following functionality: + * - Loading initrd from the LINUX_EFI_INITRD_MEDIA_GUID device path, + * - Loading/starting the kernel from firmware that targets a different + * machine type, via the entrypoint exposed in the .compat PE/COFF section. + * + * The recommended way of loading and starting v1.0 or later kernels is to use + * the LoadImage() and StartImage() EFI boot services, and expose the initrd + * via the LINUX_EFI_INITRD_MEDIA_GUID device path. + * + * Versions older than v1.0 support initrd loading via the image load options + * (using initrd=, limited to the volume from which the kernel itself was + * loaded), or via arch specific means (bootparams, DT, etc). + * + * On x86, LoadImage() and StartImage() can be omitted if the EFI handover + * protocol is implemented, which can be inferred from the version, + * handover_offset and xloadflags fields in the bootparams structure. + */ +#define LINUX_EFISTUB_MAJOR_VERSION 0x1 #define LINUX_EFISTUB_MINOR_VERSION 0x0 #define MZ_MAGIC 0x5a4d /* "MZ" */ -- cgit v1.2.3 From c267bd443f38972ab4ad29976f15f5eee9ed6f45 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 19 Feb 2020 14:04:09 +0800 Subject: clk: imx8mp: Rename the IMX8MP_CLK_HDMI_27M clock On i.MX8MP, internal HDMI 27M clock is actually 24MHz, so rename the IMX8MP_CLK_HDMI_27M to IMX8MP_CLK_HDMI_24M. Signed-off-by: Anson Huang Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mp.c | 4 ++-- include/dt-bindings/clock/imx8mp-clock.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index 3adc8aa46200..a6313cf4a30c 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -342,7 +342,7 @@ static const char * const imx8mp_hdmi_fdcc_tst_sels[] = {"osc_24m", "sys_pll1_26 "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", "audio_pll2_out", "video_pll1_out", }; -static const char * const imx8mp_hdmi_27m_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", +static const char * const imx8mp_hdmi_24m_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", "sys_pll3_out", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", "sys_pll1_133m", }; @@ -632,7 +632,7 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) hws[IMX8MP_CLK_IPP_DO_CLKO1] = imx8m_clk_hw_composite("ipp_do_clko1", imx8mp_ipp_do_clko1_sels, ccm_base + 0xba00); hws[IMX8MP_CLK_IPP_DO_CLKO2] = imx8m_clk_hw_composite("ipp_do_clko2", imx8mp_ipp_do_clko2_sels, ccm_base + 0xba80); hws[IMX8MP_CLK_HDMI_FDCC_TST] = imx8m_clk_hw_composite("hdmi_fdcc_tst", imx8mp_hdmi_fdcc_tst_sels, ccm_base + 0xbb00); - hws[IMX8MP_CLK_HDMI_27M] = imx8m_clk_hw_composite("hdmi_27m", imx8mp_hdmi_27m_sels, ccm_base + 0xbb80); + hws[IMX8MP_CLK_HDMI_24M] = imx8m_clk_hw_composite("hdmi_24m", imx8mp_hdmi_24m_sels, ccm_base + 0xbb80); hws[IMX8MP_CLK_HDMI_REF_266M] = imx8m_clk_hw_composite("hdmi_ref_266m", imx8mp_hdmi_ref_266m_sels, ccm_base + 0xbc00); hws[IMX8MP_CLK_USDHC3] = imx8m_clk_hw_composite("usdhc3", imx8mp_usdhc3_sels, ccm_base + 0xbc80); hws[IMX8MP_CLK_MEDIA_CAM1_PIX] = imx8m_clk_hw_composite("media_cam1_pix", imx8mp_media_cam1_pix_sels, ccm_base + 0xbd00); diff --git a/include/dt-bindings/clock/imx8mp-clock.h b/include/dt-bindings/clock/imx8mp-clock.h index 2fab63186bca..00d4d2288990 100644 --- a/include/dt-bindings/clock/imx8mp-clock.h +++ b/include/dt-bindings/clock/imx8mp-clock.h @@ -173,7 +173,7 @@ #define IMX8MP_CLK_IPP_DO_CLKO1 164 #define IMX8MP_CLK_IPP_DO_CLKO2 165 #define IMX8MP_CLK_HDMI_FDCC_TST 166 -#define IMX8MP_CLK_HDMI_27M 167 +#define IMX8MP_CLK_HDMI_24M 167 #define IMX8MP_CLK_HDMI_REF_266M 168 #define IMX8MP_CLK_USDHC3 169 #define IMX8MP_CLK_MEDIA_CAM1_PIX 170 -- cgit v1.2.3 From 4f929d0877543df8a834afa5b8732d469c05cd84 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 20 Feb 2020 17:56:49 +0200 Subject: firmware: imx: Remove IMX_SC_RPC_SVC_ABORT This is not used by linux and not supported as part of imx SCU api, it was added by mistake. The constant value "9" has since been reassigned in firmware to a different service. Signed-off-by: Leonard Crestez Signed-off-by: Shawn Guo --- include/linux/firmware/imx/ipc.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/firmware/imx/ipc.h b/include/linux/firmware/imx/ipc.h index 6312c8cb084a..891057434858 100644 --- a/include/linux/firmware/imx/ipc.h +++ b/include/linux/firmware/imx/ipc.h @@ -25,7 +25,6 @@ enum imx_sc_rpc_svc { IMX_SC_RPC_SVC_PAD = 6, IMX_SC_RPC_SVC_MISC = 7, IMX_SC_RPC_SVC_IRQ = 8, - IMX_SC_RPC_SVC_ABORT = 9 }; struct imx_sc_rpc_msg { -- cgit v1.2.3 From d6fb02f054127c23b048f4753176c57f3e440e79 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 19 Feb 2020 18:17:06 +0800 Subject: clk: imx: imx8mq: fix a53 cpu clock The A53 CCM clk root only accepts input up to 1GHz, CCM A53 root signoff timing is 1Ghz, however the A53 core which sources from CCM root could run above 1GHz which violates the CCM. There is a CORE_SEL slice before A53 core, we need to configure the CORE_SEL slice source from ARM PLL, not A53 CCM clk root. The A53 CCM clk root should only be used when need to change ARM PLL frequency. Add arm_a53_core clk that could source from arm_a53_div and arm_pll_out. Configure a53 ccm root sources from 800MHz sys pll Configure a53 core sources from arm_pll_out Mark arm_a53_core as critical clock Fixes: db27e40b27f1 ("clk: imx8mq: Add the missing ARM clock") Reviewed-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mq.c | 16 ++++++++++++---- include/dt-bindings/clock/imx8mq-clock.h | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 1f5ea1eaad65..b81f02ab7eb1 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -41,6 +41,8 @@ static const char * const video2_pll_out_sels[] = {"video2_pll1_ref_sel", }; static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m", "sys1_pll_800m", "sys1_pll_400m", "audio_pll1_out", "sys3_pll_out", }; +static const char * const imx8mq_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + static const char * const imx8mq_arm_m4_sels[] = {"osc_25m", "sys2_pll_200m", "sys2_pll_250m", "sys1_pll_266m", "sys1_pll_800m", "audio_pll1_out", "video_pll1_out", "sys3_pll_out", }; @@ -425,6 +427,9 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) hws[IMX8MQ_CLK_GPU_SHADER_CG] = hws[IMX8MQ_CLK_GPU_SHADER]; hws[IMX8MQ_CLK_GPU_SHADER_DIV] = hws[IMX8MQ_CLK_GPU_SHADER]; + /* CORE SEL */ + hws[IMX8MQ_CLK_A53_CORE] = imx_clk_hw_mux2_flags("arm_a53_core", base + 0x9880, 24, 1, imx8mq_a53_core_sels, ARRAY_SIZE(imx8mq_a53_core_sels), CLK_IS_CRITICAL); + /* BUS */ hws[IMX8MQ_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mq_main_axi_sels, base + 0x8800); hws[IMX8MQ_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mq_enet_axi_sels, base + 0x8880); @@ -588,11 +593,14 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) hws[IMX8MQ_GPT_3M_CLK] = imx_clk_hw_fixed_factor("gpt_3m", "osc_25m", 1, 8); hws[IMX8MQ_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); - hws[IMX8MQ_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_div", - hws[IMX8MQ_CLK_A53_DIV]->clk, - hws[IMX8MQ_CLK_A53_SRC]->clk, + clk_hw_set_parent(hws[IMX8MQ_CLK_A53_SRC], hws[IMX8MQ_SYS1_PLL_800M]); + clk_hw_set_parent(hws[IMX8MQ_CLK_A53_CORE], hws[IMX8MQ_ARM_PLL_OUT]); + + hws[IMX8MQ_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MQ_CLK_A53_CORE]->clk, + hws[IMX8MQ_CLK_A53_CORE]->clk, hws[IMX8MQ_ARM_PLL_OUT]->clk, - hws[IMX8MQ_SYS1_PLL_800M]->clk); + hws[IMX8MQ_CLK_A53_DIV]->clk); imx_check_clk_hws(hws, IMX8MQ_CLK_END); diff --git a/include/dt-bindings/clock/imx8mq-clock.h b/include/dt-bindings/clock/imx8mq-clock.h index 2b88723310bd..9b8045d75b8b 100644 --- a/include/dt-bindings/clock/imx8mq-clock.h +++ b/include/dt-bindings/clock/imx8mq-clock.h @@ -429,6 +429,8 @@ #define IMX8MQ_CLK_M4_CORE 287 #define IMX8MQ_CLK_VPU_CORE 288 -#define IMX8MQ_CLK_END 289 +#define IMX8MQ_CLK_A53_CORE 289 + +#define IMX8MQ_CLK_END 290 #endif /* __DT_BINDINGS_CLOCK_IMX8MQ_H */ -- cgit v1.2.3 From d3b70cd87e773bb298eb78fbafed68afaf06b1a2 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 19 Feb 2020 18:17:07 +0800 Subject: clk: imx: imx8mm: fix a53 cpu clock The A53 CCM clk root only accepts input up to 1GHz, CCM A53 root signoff timing is 1Ghz, however the A53 core which sources from CCM root could run above 1GHz which voilates the CCM. There is a CORE_SEL slice before A53 core, we need configure the CORE_SEL slice source from ARM PLL, not A53 CCM clk root. The A53 CCM clk root should only be used when need to change ARM PLL frequency. Add arm_a53_core clk that could source from arm_a53_div and arm_pll_out. Configure a53 ccm root sources from 800MHz sys pll Configure a53 core sources from arm_pll_out Mark arm_a53_core as critical clock Fixes: ba5625c3e272 ("clk: imx: Add clock driver support for imx8mm") Reviewed-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mm.c | 16 ++++++++++++---- include/dt-bindings/clock/imx8mm-clock.h | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 481d20ae4e0b..9feda4f5b3d6 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -39,6 +39,8 @@ static const char *sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; static const char *imx8mm_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; +static const char * const imx8mm_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + static const char *imx8mm_m4_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "sys_pll1_266m", "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; @@ -437,6 +439,9 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) hws[IMX8MM_CLK_GPU2D_CG] = hws[IMX8MM_CLK_GPU2D_CORE]; hws[IMX8MM_CLK_GPU2D_DIV] = hws[IMX8MM_CLK_GPU2D_CORE]; + /* CORE SEL */ + hws[IMX8MM_CLK_A53_CORE] = imx_clk_hw_mux2_flags("arm_a53_core", base + 0x9880, 24, 1, imx8mm_a53_core_sels, ARRAY_SIZE(imx8mm_a53_core_sels), CLK_IS_CRITICAL); + /* BUS */ hws[IMX8MM_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mm_main_axi_sels, base + 0x8800); hws[IMX8MM_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mm_enet_axi_sels, base + 0x8880); @@ -603,11 +608,14 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) hws[IMX8MM_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); hws[IMX8MM_CLK_DRAM_CORE] = imx_clk_hw_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mm_dram_core_sels, ARRAY_SIZE(imx8mm_dram_core_sels), CLK_IS_CRITICAL); - hws[IMX8MM_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_div", - hws[IMX8MM_CLK_A53_DIV]->clk, - hws[IMX8MM_CLK_A53_SRC]->clk, + clk_hw_set_parent(hws[IMX8MM_CLK_A53_SRC], hws[IMX8MM_SYS_PLL1_800M]); + clk_hw_set_parent(hws[IMX8MM_CLK_A53_CORE], hws[IMX8MM_ARM_PLL_OUT]); + + hws[IMX8MM_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MM_CLK_A53_CORE]->clk, + hws[IMX8MM_CLK_A53_CORE]->clk, hws[IMX8MM_ARM_PLL_OUT]->clk, - hws[IMX8MM_SYS_PLL1_800M]->clk); + hws[IMX8MM_CLK_A53_DIV]->clk); imx_check_clk_hws(hws, IMX8MM_CLK_END); diff --git a/include/dt-bindings/clock/imx8mm-clock.h b/include/dt-bindings/clock/imx8mm-clock.h index dbfee6579d6c..e63a5530aed7 100644 --- a/include/dt-bindings/clock/imx8mm-clock.h +++ b/include/dt-bindings/clock/imx8mm-clock.h @@ -272,6 +272,8 @@ #define IMX8MM_CLK_CLKO2 250 -#define IMX8MM_CLK_END 251 +#define IMX8MM_CLK_A53_CORE 251 + +#define IMX8MM_CLK_END 252 #endif -- cgit v1.2.3 From c69def88987995f5d3ea28f12febd1b2c567f511 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 19 Feb 2020 18:17:08 +0800 Subject: clk: imx: imx8mn: fix a53 cpu clock The A53 CCM clk root only accepts input up to 1GHz, CCM A53 root signoff timing is 1Ghz, however the A53 core which sources from CCM root could run above 1GHz which voilates the CCM. There is a CORE_SEL slice before A53 core, we need configure the CORE_SEL slice source from ARM PLL, not A53 CCM clk root. The A53 CCM clk root should only be used when need to change ARM PLL frequency. Add arm_a53_core clk that could source from arm_a53_div and arm_pll_out. Configure a53 ccm root sources from 800MHz sys pll Configure a53 core sources from arm_pll_out Mark arm_a53_core as critical clk. Fixes: 96d6392b54db ("clk: imx: Add support for i.MX8MN clock driver") Reviewed-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mn.c | 16 ++++++++++++---- include/dt-bindings/clock/imx8mn-clock.h | 4 +++- 2 files changed, 15 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index fb47f86e35e8..83618affca8b 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -38,6 +38,8 @@ static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pl "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; +static const char * const imx8mn_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + static const char * const imx8mn_gpu_core_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", "video_pll1_out", "audio_pll2_out", }; @@ -425,6 +427,9 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) hws[IMX8MN_CLK_GPU_SHADER_CG] = hws[IMX8MN_CLK_GPU_SHADER]; hws[IMX8MN_CLK_GPU_SHADER_DIV] = hws[IMX8MN_CLK_GPU_SHADER]; + /* CORE SEL */ + hws[IMX8MN_CLK_A53_CORE] = imx_clk_hw_mux2_flags("arm_a53_core", base + 0x9880, 24, 1, imx8mn_a53_core_sels, ARRAY_SIZE(imx8mn_a53_core_sels), CLK_IS_CRITICAL); + /* BUS */ hws[IMX8MN_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mn_main_axi_sels, base + 0x8800); hws[IMX8MN_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mn_enet_axi_sels, base + 0x8880); @@ -554,11 +559,14 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) hws[IMX8MN_CLK_DRAM_ALT_ROOT] = imx_clk_hw_fixed_factor("dram_alt_root", "dram_alt", 1, 4); - hws[IMX8MN_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_div", - hws[IMX8MN_CLK_A53_DIV]->clk, - hws[IMX8MN_CLK_A53_SRC]->clk, + clk_hw_set_parent(hws[IMX8MN_CLK_A53_SRC], hws[IMX8MN_SYS_PLL1_800M]); + clk_hw_set_parent(hws[IMX8MN_CLK_A53_CORE], hws[IMX8MN_ARM_PLL_OUT]); + + hws[IMX8MN_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MN_CLK_A53_CORE]->clk, + hws[IMX8MN_CLK_A53_CORE]->clk, hws[IMX8MN_ARM_PLL_OUT]->clk, - hws[IMX8MN_SYS_PLL1_800M]->clk); + hws[IMX8MN_CLK_A53_DIV]->clk); imx_check_clk_hws(hws, IMX8MN_CLK_END); diff --git a/include/dt-bindings/clock/imx8mn-clock.h b/include/dt-bindings/clock/imx8mn-clock.h index c42a22d3cf7c..6c4364c01c50 100644 --- a/include/dt-bindings/clock/imx8mn-clock.h +++ b/include/dt-bindings/clock/imx8mn-clock.h @@ -232,6 +232,8 @@ #define IMX8MN_CLK_GPU_CORE 212 #define IMX8MN_CLK_GPU_SHADER 213 -#define IMX8MN_CLK_END 214 +#define IMX8MN_CLK_A53_CORE 214 + +#define IMX8MN_CLK_END 215 #endif -- cgit v1.2.3 From 7ab227210110a4137b005b7be3df1ec2d668ac96 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 19 Feb 2020 18:17:09 +0800 Subject: clk: imx: imx8mp: fix a53 cpu clock The A53 CCM clk root only accepts input up to 1GHz, CCM A53 root signoff timing is 1Ghz, however the A53 core which sources from CCM root could run above 1GHz which voilates the CCM. There is a CORE_SEL slice before A53 core, we need configure the CORE_SEL slice source from ARM PLL, not A53 CCM clk root. The A53 CCM clk root should only be used when need to change ARM PLL frequency. Add arm_a53_core clk that could source from arm_a53_div and arm_pll_out. Configure a53 ccm root sources from 800MHz sys pll Configure a53 core sources from arm_pll_out Mark arm_a53_core as critical clk Reviewed-by: Jacky Bai Signed-off-by: Peng Fan Signed-off-by: Shawn Guo --- drivers/clk/imx/clk-imx8mp.c | 16 ++++++++++++---- include/dt-bindings/clock/imx8mp-clock.h | 3 ++- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx8mp.c b/drivers/clk/imx/clk-imx8mp.c index a6313cf4a30c..c6161a4af201 100644 --- a/drivers/clk/imx/clk-imx8mp.c +++ b/drivers/clk/imx/clk-imx8mp.c @@ -34,6 +34,8 @@ static const char * const imx8mp_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pl "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", "audio_pll1_out", "sys_pll3_out", }; +static const char * const imx8mp_a53_core_sels[] = {"arm_a53_div", "arm_pll_out", }; + static const char * const imx8mp_m7_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll2_250m", "vpu_pll_out", "sys_pll1_800m", "audio_pll1_out", "video_pll1_out", "sys_pll3_out", }; @@ -554,6 +556,9 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) hws[IMX8MP_CLK_HSIO_AXI_DIV] = imx_clk_hw_divider2("hsio_axi_div", "hsio_axi_cg", ccm_base + 0x8380, 0, 3); hws[IMX8MP_CLK_MEDIA_ISP_DIV] = imx_clk_hw_divider2("media_isp_div", "media_isp_cg", ccm_base + 0x8400, 0, 3); + /* CORE SEL */ + hws[IMX8MP_CLK_A53_CORE] = imx_clk_hw_mux2_flags("arm_a53_core", ccm_base + 0x9880, 24, 1, imx8mp_a53_core_sels, ARRAY_SIZE(imx8mp_a53_core_sels), CLK_IS_CRITICAL); + hws[IMX8MP_CLK_MAIN_AXI] = imx8m_clk_hw_composite_critical("main_axi", imx8mp_main_axi_sels, ccm_base + 0x8800); hws[IMX8MP_CLK_ENET_AXI] = imx8m_clk_hw_composite("enet_axi", imx8mp_enet_axi_sels, ccm_base + 0x8880); hws[IMX8MP_CLK_NAND_USDHC_BUS] = imx8m_clk_hw_composite_critical("nand_usdhc_bus", imx8mp_nand_usdhc_sels, ccm_base + 0x8900); @@ -724,11 +729,14 @@ static int imx8mp_clocks_probe(struct platform_device *pdev) hws[IMX8MP_CLK_VPU_ROOT] = imx_clk_hw_gate4("vpu_root_clk", "vpu_bus", ccm_base + 0x4630, 0); hws[IMX8MP_CLK_AUDIO_ROOT] = imx_clk_hw_gate4("audio_root_clk", "ipg_root", ccm_base + 0x4650, 0); - hws[IMX8MP_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_div", - hws[IMX8MP_CLK_A53_DIV]->clk, - hws[IMX8MP_CLK_A53_SRC]->clk, + clk_hw_set_parent(hws[IMX8MP_CLK_A53_SRC], hws[IMX8MP_SYS_PLL1_800M]); + clk_hw_set_parent(hws[IMX8MP_CLK_A53_CORE], hws[IMX8MP_ARM_PLL_OUT]); + + hws[IMX8MP_CLK_ARM] = imx_clk_hw_cpu("arm", "arm_a53_core", + hws[IMX8MP_CLK_A53_CORE]->clk, + hws[IMX8MP_CLK_A53_CORE]->clk, hws[IMX8MP_ARM_PLL_OUT]->clk, - hws[IMX8MP_SYS_PLL1_800M]->clk); + hws[IMX8MP_CLK_A53_DIV]->clk); imx_check_clk_hws(hws, IMX8MP_CLK_END); diff --git a/include/dt-bindings/clock/imx8mp-clock.h b/include/dt-bindings/clock/imx8mp-clock.h index 00d4d2288990..47ab082238b4 100644 --- a/include/dt-bindings/clock/imx8mp-clock.h +++ b/include/dt-bindings/clock/imx8mp-clock.h @@ -294,7 +294,8 @@ #define IMX8MP_CLK_DRAM_ALT_ROOT 285 #define IMX8MP_CLK_DRAM_CORE 286 #define IMX8MP_CLK_ARM 287 +#define IMX8MP_CLK_A53_CORE 288 -#define IMX8MP_CLK_END 288 +#define IMX8MP_CLK_END 289 #endif -- cgit v1.2.3 From 8d74a623cc3cecda89da628b8f3d115d8cf1ee8f Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Feb 2020 10:19:12 +0100 Subject: Revert "nl80211: add src and dst addr attributes for control port tx/rx" This reverts commit 8c3ed7aa2b9ef666195b789e9b02e28383243fa8. As Jouni points out, there's really no need for this, since the RSN pre-authentication frames are normal data frames, not port control frames (locally). We can still revert this now since it hasn't actually gone beyond -next. Fixes: 8c3ed7aa2b9e ("nl80211: add src and dst addr attributes for control port tx/rx") Signed-off-by: Johannes Berg Link: https://lore.kernel.org/r/20200224101910.b746e263287a.I9eb15d6895515179d50964dec3550c9dc784bb93@changeid Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 3 +-- include/uapi/linux/nl80211.h | 16 +--------------- net/mac80211/ieee80211_i.h | 3 +-- net/mac80211/tx.c | 3 +-- net/wireless/nl80211.c | 18 +++--------------- net/wireless/rdev-ops.h | 8 ++++---- net/wireless/trace.h | 27 ++++++++++----------------- 7 files changed, 21 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 7ea23caa7301..089ffdd6dba5 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3974,8 +3974,7 @@ struct cfg80211_ops { int (*tx_control_port)(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, - const u8 *dest, const u8 *src, - const __be16 proto, + const u8 *dest, const __be16 proto, const bool noencrypt); int (*get_ftm_responder_stats)(struct wiphy *wiphy, diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 158bccb4a47b..350ab86fe20e 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1039,14 +1039,11 @@ * a control port frame and as a notification that a control port frame * has been received. %NL80211_ATTR_FRAME is used to specify the * frame contents. The frame is the raw EAPoL data, without ethernet or - * 802.11 headers. An optional %NL80211_ATTR_SRC_MAC can be used to send - * pre-auth frames to STAs on behalf of other APs. + * 802.11 headers. * When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE, * %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added * indicating the protocol type of the received frame; whether the frame * was received unencrypted and the MAC address of the peer respectively. - * %NL80211_ATTR_DST_MAC can be used to forward pre-auth frames in - * userspace while using AP mode. * * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. * @@ -2412,9 +2409,6 @@ enum nl80211_commands { * %NL80211_ATTR_AKM_SUITES are default capabilities if AKM suites not * advertised for a specific interface type. * - * @NL80211_ATTR_SRC_MAC: MAC address used in control port over nl80211 transmit - * @NL80211_ATTR_DST_MAC: MAC address used in control port over nl80211 receive - * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2883,9 +2877,6 @@ enum nl80211_attrs { NL80211_ATTR_IFTYPE_AKM_SUITES, - NL80211_ATTR_SRC_MAC, - NL80211_ATTR_DST_MAC, - /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -5548,10 +5539,6 @@ enum nl80211_feature_flags { * feature, which prevents bufferbloat by using the expected transmission * time to limit the amount of data buffered in the hardware. * - * @NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_MAC_ADDRS: The driver - * can use src and dst MAC addresses with control port over nl80211 rx - * and tx operations. - * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -5599,7 +5586,6 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SAE_OFFLOAD, NL80211_EXT_FEATURE_VLAN_OFFLOAD, NL80211_EXT_FEATURE_AQL, - NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211_MAC_ADDRS, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index da9eaa9ee37e..8a49d78ad7c9 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1792,8 +1792,7 @@ void ieee80211_check_fast_xmit_iface(struct ieee80211_sub_if_data *sdata); void ieee80211_clear_fast_xmit(struct sta_info *sta); int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, - const u8 *dest, const u8 *src, __be16 proto, - bool unencrypted); + const u8 *dest, __be16 proto, bool unencrypted); int ieee80211_probe_mesh_link(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 2645a39cce88..cddaacaa31a3 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -5283,8 +5283,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len, - const u8 *dest, const u8 *src, __be16 proto, - bool unencrypted) + const u8 *dest, __be16 proto, bool unencrypted) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index f0112dabe21e..d795552f97b2 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -634,8 +634,6 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), [NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), [NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy), - [NL80211_ATTR_SRC_MAC] = NLA_POLICY_ETH_ADDR, - [NL80211_ATTR_DST_MAC] = NLA_POLICY_ETH_ADDR, }; /* policy for the key attributes */ @@ -13698,7 +13696,6 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) const u8 *buf; size_t len; u8 *dest; - u8 src[ETH_ALEN]; u16 proto; bool noencrypt; int err; @@ -13736,13 +13733,6 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) goto out; } - /* copy src address under wdev_lock, as we may copy wdev_address */ - if (info->attrs[NL80211_ATTR_SRC_MAC]) - ether_addr_copy(src, - nla_data(info->attrs[NL80211_ATTR_SRC_MAC])); - else - ether_addr_copy(src, wdev_address(wdev)); - wdev_unlock(wdev); buf = nla_data(info->attrs[NL80211_ATTR_FRAME]); @@ -13753,7 +13743,7 @@ static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]); return rdev_tx_control_port(rdev, dev, buf, len, - dest, src, cpu_to_be16(proto), noencrypt); + dest, cpu_to_be16(proto), noencrypt); out: wdev_unlock(wdev); @@ -16010,8 +16000,7 @@ static int __nl80211_rx_control_port(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct ethhdr *ehdr = eth_hdr(skb); - const u8 *daddr = ehdr->h_dest; - const u8 *saddr = ehdr->h_source; + const u8 *addr = ehdr->h_source; u16 proto = be16_to_cpu(skb->protocol); struct sk_buff *msg; void *hdr; @@ -16036,8 +16025,7 @@ static int __nl80211_rx_control_port(struct net_device *dev, nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev), NL80211_ATTR_PAD) || - nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, saddr) || - nla_put(msg, NL80211_ATTR_DST_MAC, ETH_ALEN, daddr) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) || nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, proto) || (unencrypted && nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index 5ea34c1b60fe..e0d34f796d0b 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -734,14 +734,14 @@ static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, static inline int rdev_tx_control_port(struct cfg80211_registered_device *rdev, struct net_device *dev, const void *buf, size_t len, - const u8 *dest, const u8 *src, - __be16 proto, const bool noencrypt) + const u8 *dest, __be16 proto, + const bool noencrypt) { int ret; trace_rdev_tx_control_port(&rdev->wiphy, dev, buf, len, - dest, src, proto, noencrypt); + dest, proto, noencrypt); ret = rdev->ops->tx_control_port(&rdev->wiphy, dev, buf, len, - dest, src, proto, noencrypt); + dest, proto, noencrypt); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index b6b60e3aea41..3ef1679b0e66 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -1928,31 +1928,27 @@ TRACE_EVENT(rdev_mgmt_tx, TRACE_EVENT(rdev_tx_control_port, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - const u8 *buf, size_t len, - const u8 *dest, const u8 *src, __be16 proto, + const u8 *buf, size_t len, const u8 *dest, __be16 proto, bool unencrypted), - TP_ARGS(wiphy, netdev, buf, len, dest, src, proto, unencrypted), + TP_ARGS(wiphy, netdev, buf, len, dest, proto, unencrypted), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY MAC_ENTRY(dest) - MAC_ENTRY(src) - __field(u16, proto) + __field(__be16, proto) __field(bool, unencrypted) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; MAC_ASSIGN(dest, dest); - MAC_ASSIGN(src, src); - __entry->proto = be16_to_cpu(proto); + __entry->proto = proto; __entry->unencrypted = unencrypted; ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", dest: " MAC_PR_FMT - ", src: " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s", - WIPHY_PR_ARG, NETDEV_PR_ARG, - MAC_PR_ARG(dest), MAC_PR_ARG(src), - __entry->proto, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT "," + " proto: 0x%x, unencrypted: %s", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dest), + be16_to_cpu(__entry->proto), BOOL_TO_STR(__entry->unencrypted)) ); @@ -2844,7 +2840,6 @@ TRACE_EVENT(cfg80211_rx_control_port, TP_STRUCT__entry( NETDEV_ENTRY __field(int, len) - MAC_ENTRY(to) MAC_ENTRY(from) __field(u16, proto) __field(bool, unencrypted) @@ -2852,14 +2847,12 @@ TRACE_EVENT(cfg80211_rx_control_port, TP_fast_assign( NETDEV_ASSIGN; __entry->len = skb->len; - MAC_ASSIGN(to, eth_hdr(skb)->h_dest); MAC_ASSIGN(from, eth_hdr(skb)->h_source); __entry->proto = be16_to_cpu(skb->protocol); __entry->unencrypted = unencrypted; ), - TP_printk(NETDEV_PR_FMT ", len=%d, dest: " MAC_PR_FMT - ", src: " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s", - NETDEV_PR_ARG, __entry->len, MAC_PR_ARG(to), MAC_PR_ARG(from), + TP_printk(NETDEV_PR_FMT ", len=%d, " MAC_PR_FMT ", proto: 0x%x, unencrypted: %s", + NETDEV_PR_ARG, __entry->len, MAC_PR_ARG(from), __entry->proto, BOOL_TO_STR(__entry->unencrypted)) ); -- cgit v1.2.3 From febc7ec6cb704c619e159b3c3ff84cd57e12815f Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:55:55 +0100 Subject: cfg80211: drop duplicated documentation of field "probe_resp_offload" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "probe_resp_offload" was already documented above the definition of struct wiphy. Both comments were identical. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-1-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 089ffdd6dba5..e410e6a8d0fe 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4666,11 +4666,6 @@ struct wiphy { u32 available_antennas_tx; u32 available_antennas_rx; - /* - * Bitmap of supported protocols for probe response offloading - * see &enum nl80211_probe_resp_offload_support_attr. Only valid - * when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set. - */ u32 probe_resp_offload; const u8 *extended_capabilities, *extended_capabilities_mask; -- cgit v1.2.3 From cfb99437e2856665de196dfaf2f327736a717463 Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:55:56 +0100 Subject: cfg80211: drop duplicated documentation of field "privid" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "privid" was already documented above the definition of struct wiphy. Comments were not identical, but they said more or less the same thing. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-2-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e410e6a8d0fe..eaf654779b8c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4674,11 +4674,6 @@ struct wiphy { const struct wiphy_iftype_ext_capab *iftype_ext_capab; unsigned int num_iftype_ext_capab; - /* If multiple wiphys are registered and you're handed e.g. - * a regular netdev with assigned ieee80211_ptr, you won't - * know whether it points to a wiphy your driver has registered - * or not. Assign this to something global to your driver to - * help determine whether you own this wiphy or not. */ const void *privid; struct ieee80211_supported_band *bands[NUM_NL80211_BANDS]; -- cgit v1.2.3 From 4308d955a561f5e332952dce83ef42fca5914f5e Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:55:57 +0100 Subject: cfg80211: drop duplicated documentation of field "registered" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Field "registered" was documented three times: twice in the documentation block of struct wiphy and once inside the struct definition. This patch keep only one comment. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-3-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index eaf654779b8c..e849f4e2ae85 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4465,7 +4465,6 @@ struct wiphy_iftype_akm_suites { * @debugfsdir: debugfs directory used for this wiphy, will be renamed * automatically on wiphy renames * @dev: (virtual) struct device for this wiphy - * @registered: helps synchronize suspend/resume with wiphy unregister * @wext: wireless extension handlers * @priv: driver private data (sized according to wiphy_new() parameter) * @interface_modes: bitmask of interfaces types valid for this wiphy, @@ -4690,7 +4689,6 @@ struct wiphy { * you need use set_wiphy_dev() (see below) */ struct device dev; - /* protects ->resume, ->suspend sysfs callbacks against unregister hw */ bool registered; /* dir in debugfs: ieee80211/ */ -- cgit v1.2.3 From af18d341fbdfd36a37eda1f0c26ff96a4ece9a36 Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:55:58 +0100 Subject: cfg80211: drop duplicated documentation of field "_net" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "_net" was already documented above the definition of struct wiphy. Both comments were identical. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-4-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e849f4e2ae85..23a7f689bbb3 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4699,7 +4699,6 @@ struct wiphy { struct list_head wdev_list; - /* the network namespace this phy lives in currently */ possible_net_t _net; #ifdef CONFIG_CFG80211_WEXT -- cgit v1.2.3 From 78fb55939765668810aea12832648539b39bb2ac Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:55:59 +0100 Subject: cfg80211: drop duplicated documentation of field "perm_addr" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "perm_addr" was already documented above the definition of struct wiphy. Comments were almost identical. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-5-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 23a7f689bbb3..bd9cc9877280 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4598,7 +4598,6 @@ struct wiphy_iftype_akm_suites { struct wiphy { /* assign these fields before you register the wiphy */ - /* permanent MAC address(es) */ u8 perm_addr[ETH_ALEN]; u8 addr_mask[ETH_ALEN]; -- cgit v1.2.3 From 6046fdc963b2a94068195e2fd83965e6d52326a0 Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:56:00 +0100 Subject: cfg80211: drop duplicated documentation of field "reg_notifier" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "reg_notifier" was already documented above the definition of struct wiphy. The comment inside the definition of the struct did not bring more information. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-6-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bd9cc9877280..90ec1abea960 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4676,7 +4676,6 @@ struct wiphy { struct ieee80211_supported_band *bands[NUM_NL80211_BANDS]; - /* Lets us get back the wiphy on the callback */ void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request); -- cgit v1.2.3 From edf77192f8076922d423e2cab61e0e782a4e4da5 Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:56:01 +0100 Subject: cfg80211: merge documentations of field "debugfsdir" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "privid" is documented twice. Comments were more or less the same. The patch merge them. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-7-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 90ec1abea960..3a9ee56a0165 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4462,8 +4462,8 @@ struct wiphy_iftype_akm_suites { * the same number of arbitrary MAC addresses. * @registered: protects ->resume and ->suspend sysfs callbacks against * unregister hardware - * @debugfsdir: debugfs directory used for this wiphy, will be renamed - * automatically on wiphy renames + * @debugfsdir: debugfs directory used for this wiphy (ieee80211/). + * It will be renamed automatically on wiphy renames * @dev: (virtual) struct device for this wiphy * @wext: wireless extension handlers * @priv: driver private data (sized according to wiphy_new() parameter) @@ -4689,7 +4689,6 @@ struct wiphy { bool registered; - /* dir in debugfs: ieee80211/ */ struct dentry *debugfsdir; const struct ieee80211_ht_cap *ht_capa_mod_mask; -- cgit v1.2.3 From 15bc6dfbe493f91f3daa2bfee82e5dbe1b476eed Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:56:02 +0100 Subject: cfg80211: merge documentations of field "dev" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The field "dev" was documented on two places. This patch merges the comments. Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-8-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3a9ee56a0165..9daecfe3ba70 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4464,7 +4464,9 @@ struct wiphy_iftype_akm_suites { * unregister hardware * @debugfsdir: debugfs directory used for this wiphy (ieee80211/). * It will be renamed automatically on wiphy renames - * @dev: (virtual) struct device for this wiphy + * @dev: (virtual) struct device for this wiphy. The item in + * /sys/class/ieee80211/ points to this. You need use set_wiphy_dev() + * (see below). * @wext: wireless extension handlers * @priv: driver private data (sized according to wiphy_new() parameter) * @interface_modes: bitmask of interfaces types valid for this wiphy, @@ -4683,8 +4685,6 @@ struct wiphy { const struct ieee80211_regdomain __rcu *regd; - /* the item in /sys/class/ieee80211/ points to this, - * you need use set_wiphy_dev() (see below) */ struct device dev; bool registered; -- cgit v1.2.3 From cd9b52bf75be2da3482b1e675eccb6c556d4a214 Mon Sep 17 00:00:00 2001 From: Jérôme Pouiller Date: Fri, 21 Feb 2020 12:56:04 +0100 Subject: cfg80211: fix indentation errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Pouiller Link: https://lore.kernel.org/r/20200221115604.594035-10-Jerome.Pouiller@silabs.com Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 9daecfe3ba70..682ee4dd6963 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -72,12 +72,12 @@ struct wiphy; * * @IEEE80211_CHAN_DISABLED: This channel is disabled. * @IEEE80211_CHAN_NO_IR: do not initiate radiation, this includes - * sending probe requests or beaconing. + * sending probe requests or beaconing. * @IEEE80211_CHAN_RADAR: Radar detection is required on this channel. * @IEEE80211_CHAN_NO_HT40PLUS: extension channel above this channel - * is not permitted. + * is not permitted. * @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel - * is not permitted. + * is not permitted. * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel. * @IEEE80211_CHAN_NO_80MHZ: If the driver supports 80 MHz on the band, * this flag indicates that an 80 MHz channel cannot use this @@ -1686,7 +1686,7 @@ struct mpath_info { * @basic_rates_len: number of basic rates * @ap_isolate: do not forward packets between connected stations * @ht_opmode: HT Operation mode - * (u16 = opmode, -1 = do not change) + * (u16 = opmode, -1 = do not change) * @p2p_ctwindow: P2P CT Window (-1 = no change) * @p2p_opp_ps: P2P opportunistic PS (-1 = no change) */ @@ -2062,8 +2062,8 @@ struct cfg80211_bss_select_adjust { * @ie_len: length of ie in octets * @flags: bit field of flags controlling operation * @match_sets: sets of parameters to be matched for a scan result - * entry to be considered valid and to be passed to the host - * (others are filtered out). + * entry to be considered valid and to be passed to the host + * (others are filtered out). * If ommited, all results are passed. * @n_match_sets: number of match sets * @report_results: indicates that results were reported for this request @@ -2456,7 +2456,7 @@ struct cfg80211_disassoc_request { * will be used in ht_capa. Un-supported values will be ignored. * @ht_capa_mask: The bits of ht_capa which are to be used. * @wep_keys: static WEP keys, if not NULL points to an array of - * CFG80211_MAX_WEP_KEYS WEP keys + * CFG80211_MAX_WEP_KEYS WEP keys * @wep_tx_key: key index (0..3) of the default TX static WEP key */ struct cfg80211_ibss_params { @@ -4426,7 +4426,7 @@ struct wiphy_iftype_akm_suites { * note that if your driver uses wiphy_apply_custom_regulatory() * the reg_notifier's request can be passed as NULL * @regd: the driver's regulatory domain, if one was requested via - * the regulatory_hint() API. This can be used by the driver + * the regulatory_hint() API. This can be used by the driver * on the reg_notifier() if it chooses to ignore future * regulatory domain changes caused by other drivers. * @signal_type: signal type reported in &struct cfg80211_bss. @@ -5502,9 +5502,9 @@ void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr); * @wiphy: the wireless device giving the hint (used only for reporting * conflicts) * @alpha2: the ISO/IEC 3166 alpha2 the driver claims its regulatory domain - * should be in. If @rd is set this should be NULL. Note that if you - * set this to NULL you should still set rd->alpha2 to some accepted - * alpha2. + * should be in. If @rd is set this should be NULL. Note that if you + * set this to NULL you should still set rd->alpha2 to some accepted + * alpha2. * * Wireless drivers can use this function to hint to the wireless core * what it believes should be the current regulatory domain by -- cgit v1.2.3 From 56be393fa8b40db2d4f54f97614f645eb8d3c32e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 22 Feb 2020 15:25:43 +0200 Subject: cfg80211: Support key configuration for Beacon protection (BIGTK) IEEE P802.11-REVmd/D3.0 adds support for protecting Beacon frames using a new set of keys (BIGTK; key index 6..7) similarly to the way group-addressed Robust Management frames are protected (IGTK; key index 4..5). Extend cfg80211 and nl80211 to allow the new BIGTK to be configured. Add an extended feature flag to indicate driver support for the new key index values to avoid array overflows in driver implementations and also to indicate to user space when this functionality is available. Signed-off-by: Jouni Malinen Link: https://lore.kernel.org/r/20200222132548.20835-2-jouni@codeaurora.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 5 ++++ include/uapi/linux/nl80211.h | 6 +++++ net/wireless/nl80211.c | 56 +++++++++++++++++++++++++++++++++++--------- net/wireless/rdev-ops.h | 13 ++++++++++ net/wireless/sme.c | 11 +++++++-- net/wireless/trace.h | 17 ++++++++++++++ net/wireless/util.c | 7 +++++- 7 files changed, 101 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 682ee4dd6963..04b6df15706c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -3369,6 +3369,8 @@ struct cfg80211_update_owe_info { * @set_default_key: set the default key on an interface * * @set_default_mgmt_key: set the default management frame key on an interface + + * @set_default_beacon_key: set the default Beacon frame key on an interface * * @set_rekey_data: give the data necessary for GTK rekeying to the driver * @@ -3702,6 +3704,9 @@ struct cfg80211_ops { int (*set_default_mgmt_key)(struct wiphy *wiphy, struct net_device *netdev, u8 key_index); + int (*set_default_beacon_key)(struct wiphy *wiphy, + struct net_device *netdev, + u8 key_index); int (*start_ap)(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *settings); diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 350ab86fe20e..934e62fe8705 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4550,6 +4550,7 @@ enum nl80211_key_default_types { * See &enum nl80211_key_default_types. * @NL80211_KEY_MODE: the mode from enum nl80211_key_mode. * Defaults to @NL80211_KEY_RX_TX. + * @NL80211_KEY_DEFAULT_BEACON: flag indicating default Beacon frame key * * @__NL80211_KEY_AFTER_LAST: internal * @NL80211_KEY_MAX: highest key attribute @@ -4565,6 +4566,7 @@ enum nl80211_key_attributes { NL80211_KEY_TYPE, NL80211_KEY_DEFAULT_TYPES, NL80211_KEY_MODE, + NL80211_KEY_DEFAULT_BEACON, /* keep last */ __NL80211_KEY_AFTER_LAST, @@ -5539,6 +5541,9 @@ enum nl80211_feature_flags { * feature, which prevents bufferbloat by using the expected transmission * time to limit the amount of data buffered in the hardware. * + * @NL80211_EXT_FEATURE_BEACON_PROTECTION: The driver supports Beacon protection + * and can receive key configuration for BIGTK using key indexes 6 and 7. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -5586,6 +5591,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SAE_OFFLOAD, NL80211_EXT_FEATURE_VLAN_OFFLOAD, NL80211_EXT_FEATURE_AQL, + NL80211_EXT_FEATURE_BEACON_PROTECTION, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ce55a2c05fe9..a75f72288139 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -368,7 +368,7 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, - [NL80211_ATTR_KEY_IDX] = NLA_POLICY_MAX(NLA_U8, 5), + [NL80211_ATTR_KEY_IDX] = NLA_POLICY_MAX(NLA_U8, 7), [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, @@ -1037,7 +1037,7 @@ struct key_parse { struct key_params p; int idx; int type; - bool def, defmgmt; + bool def, defmgmt, defbeacon; bool def_uni, def_multi; }; @@ -1053,12 +1053,13 @@ static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key, k->def = !!tb[NL80211_KEY_DEFAULT]; k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; + k->defbeacon = !!tb[NL80211_KEY_DEFAULT_BEACON]; if (k->def) { k->def_uni = true; k->def_multi = true; } - if (k->defmgmt) + if (k->defmgmt || k->defbeacon) k->def_multi = true; if (tb[NL80211_KEY_IDX]) @@ -1165,14 +1166,17 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) if (err) return err; - if (k->def && k->defmgmt) { - GENL_SET_ERR_MSG(info, "key with def && defmgmt is invalid"); + if ((k->def ? 1 : 0) + (k->defmgmt ? 1 : 0) + + (k->defbeacon ? 1 : 0) > 1) { + GENL_SET_ERR_MSG(info, + "key with multiple default flags is invalid"); return -EINVAL; } - if (k->defmgmt) { + if (k->defmgmt || k->defbeacon) { if (k->def_uni || !k->def_multi) { - GENL_SET_ERR_MSG(info, "defmgmt key must be mcast"); + GENL_SET_ERR_MSG(info, + "defmgmt/defbeacon key must be mcast"); return -EINVAL; } } @@ -1184,14 +1188,20 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) "defmgmt key idx not 4 or 5"); return -EINVAL; } + } else if (k->defbeacon) { + if (k->idx < 6 || k->idx > 7) { + GENL_SET_ERR_MSG(info, + "defbeacon key idx not 6 or 7"); + return -EINVAL; + } } else if (k->def) { if (k->idx < 0 || k->idx > 3) { GENL_SET_ERR_MSG(info, "def key idx not 0-3"); return -EINVAL; } } else { - if (k->idx < 0 || k->idx > 5) { - GENL_SET_ERR_MSG(info, "key idx not 0-5"); + if (k->idx < 0 || k->idx > 7) { + GENL_SET_ERR_MSG(info, "key idx not 0-7"); return -EINVAL; } } @@ -3817,8 +3827,14 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) void *hdr; struct sk_buff *msg; - if (info->attrs[NL80211_ATTR_KEY_IDX]) + if (info->attrs[NL80211_ATTR_KEY_IDX]) { key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); + if (key_idx > 5 && + !wiphy_ext_feature_isset( + &rdev->wiphy, + NL80211_EXT_FEATURE_BEACON_PROTECTION)) + return -EINVAL; + } if (info->attrs[NL80211_ATTR_MAC]) mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); @@ -3894,7 +3910,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) /* Only support setting default key and * Extended Key ID action NL80211_KEY_SET_TX. */ - if (!key.def && !key.defmgmt && + if (!key.def && !key.defmgmt && !key.defbeacon && !(key.p.mode == NL80211_KEY_SET_TX)) return -EINVAL; @@ -3941,6 +3957,24 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) #ifdef CONFIG_CFG80211_WEXT dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; #endif + } else if (key.defbeacon) { + if (key.def_uni || !key.def_multi) { + err = -EINVAL; + goto out; + } + + if (!rdev->ops->set_default_beacon_key) { + err = -EOPNOTSUPP; + goto out; + } + + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (err) + goto out; + + err = rdev_set_default_beacon_key(rdev, dev, key.idx); + if (err) + goto out; } else if (key.p.mode == NL80211_KEY_SET_TX && wiphy_ext_feature_isset(&rdev->wiphy, NL80211_EXT_FEATURE_EXT_KEY_ID)) { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index e0d34f796d0b..af7fcf2a3b4a 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -136,6 +136,19 @@ rdev_set_default_mgmt_key(struct cfg80211_registered_device *rdev, return ret; } +static inline int +rdev_set_default_beacon_key(struct cfg80211_registered_device *rdev, + struct net_device *netdev, u8 key_index) +{ + int ret; + + trace_rdev_set_default_beacon_key(&rdev->wiphy, netdev, key_index); + ret = rdev->ops->set_default_beacon_key(&rdev->wiphy, netdev, + key_index); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + static inline int rdev_start_ap(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_ap_settings *settings) diff --git a/net/wireless/sme.c b/net/wireless/sme.c index d32a2ec4d96a..ac3e60aa1fc8 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -1111,9 +1111,16 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, * Delete all the keys ... pairwise keys can't really * exist any more anyway, but default keys might. */ - if (rdev->ops->del_key) - for (i = 0; i < 6; i++) + if (rdev->ops->del_key) { + int max_key_idx = 5; + + if (wiphy_ext_feature_isset( + wdev->wiphy, + NL80211_EXT_FEATURE_BEACON_PROTECTION)) + max_key_idx = 7; + for (i = 0; i <= max_key_idx; i++) rdev_del_key(rdev, dev, i, false, NULL); + } rdev_set_qos_map(rdev, dev, NULL); diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 3ef1679b0e66..56b78222746c 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -510,6 +510,23 @@ TRACE_EVENT(rdev_set_default_mgmt_key, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index) ); +TRACE_EVENT(rdev_set_default_beacon_key, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u8 key_index), + TP_ARGS(wiphy, netdev, key_index), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + __field(u8, key_index) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + __entry->key_index = key_index; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index) +); + TRACE_EVENT(rdev_start_ap, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, struct cfg80211_ap_settings *settings), diff --git a/net/wireless/util.c b/net/wireless/util.c index 8481e9ac33da..72926f87c913 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -231,7 +231,12 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr) { - if (key_idx < 0 || key_idx > 5) + int max_key_idx = 5; + + if (wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_BEACON_PROTECTION)) + max_key_idx = 7; + if (key_idx < 0 || key_idx > max_key_idx) return -EINVAL; if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) -- cgit v1.2.3 From 5779dd0a7dbd71e82478fb0bf125cc6cd3c43266 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 24 Feb 2020 15:23:34 +0530 Subject: PCI: endpoint: Use notification chain mechanism to notify EPC events to EPF Use atomic_notifier_call_chain() to notify EPC events like linkup to EPF driver instead of using linkup ops in EPF driver. This is in preparation for adding proper locking mechanism to EPF ops. This will also enable to add more events (in addition to linkup) in the future. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Tested-by: Vidya Sagar --- drivers/pci/endpoint/functions/pci-epf-test.c | 13 ++++++++++--- drivers/pci/endpoint/pci-epc-core.c | 9 ++------- drivers/pci/endpoint/pci-epf-core.c | 22 +--------------------- include/linux/pci-epc.h | 8 ++++++++ include/linux/pci-epf.h | 6 ++---- 5 files changed, 23 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 5d74f81ddfe4..bddff15052cc 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -360,12 +360,16 @@ reset_handler: msecs_to_jiffies(1)); } -static void pci_epf_test_linkup(struct pci_epf *epf) +static int pci_epf_test_notifier(struct notifier_block *nb, unsigned long val, + void *data) { + struct pci_epf *epf = container_of(nb, struct pci_epf, nb); struct pci_epf_test *epf_test = epf_get_drvdata(epf); queue_delayed_work(kpcitest_workqueue, &epf_test->cmd_handler, msecs_to_jiffies(1)); + + return NOTIFY_OK; } static void pci_epf_test_unbind(struct pci_epf *epf) @@ -546,8 +550,12 @@ static int pci_epf_test_bind(struct pci_epf *epf) } } - if (!linkup_notifier) + if (linkup_notifier) { + epf->nb.notifier_call = pci_epf_test_notifier; + pci_epc_register_notifier(epc, &epf->nb); + } else { queue_work(kpcitest_workqueue, &epf_test->cmd_handler.work); + } return 0; } @@ -580,7 +588,6 @@ static int pci_epf_test_probe(struct pci_epf *epf) static struct pci_epf_ops ops = { .unbind = pci_epf_test_unbind, .bind = pci_epf_test_bind, - .linkup = pci_epf_test_linkup, }; static struct pci_epf_driver test_driver = { diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 2091508c1620..2f6436599fcb 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -539,16 +539,10 @@ EXPORT_SYMBOL_GPL(pci_epc_remove_epf); */ void pci_epc_linkup(struct pci_epc *epc) { - unsigned long flags; - struct pci_epf *epf; - if (!epc || IS_ERR(epc)) return; - spin_lock_irqsave(&epc->lock, flags); - list_for_each_entry(epf, &epc->pci_epf, list) - pci_epf_linkup(epf); - spin_unlock_irqrestore(&epc->lock, flags); + atomic_notifier_call_chain(&epc->notifier, 0, NULL); } EXPORT_SYMBOL_GPL(pci_epc_linkup); @@ -612,6 +606,7 @@ __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops, spin_lock_init(&epc->lock); INIT_LIST_HEAD(&epc->pci_epf); + ATOMIC_INIT_NOTIFIER_HEAD(&epc->notifier); device_initialize(&epc->dev); epc->dev.class = pci_epc_class; diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index fb1306de8f40..93f28c65ace0 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -20,26 +20,6 @@ static DEFINE_MUTEX(pci_epf_mutex); static struct bus_type pci_epf_bus_type; static const struct device_type pci_epf_type; -/** - * pci_epf_linkup() - Notify the function driver that EPC device has - * established a connection with the Root Complex. - * @epf: the EPF device bound to the EPC device which has established - * the connection with the host - * - * Invoke to notify the function driver that EPC device has established - * a connection with the Root Complex. - */ -void pci_epf_linkup(struct pci_epf *epf) -{ - if (!epf->driver) { - dev_WARN(&epf->dev, "epf device not bound to driver\n"); - return; - } - - epf->driver->ops->linkup(epf); -} -EXPORT_SYMBOL_GPL(pci_epf_linkup); - /** * pci_epf_unbind() - Notify the function driver that the binding between the * EPF device and EPC device has been lost @@ -214,7 +194,7 @@ int __pci_epf_register_driver(struct pci_epf_driver *driver, if (!driver->ops) return -EINVAL; - if (!driver->ops->bind || !driver->ops->unbind || !driver->ops->linkup) + if (!driver->ops->bind || !driver->ops->unbind) return -EINVAL; driver->driver.bus = &pci_epf_bus_type; diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 56f1846b9d39..36644ccd32ac 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -89,6 +89,7 @@ struct pci_epc_mem { * @max_functions: max number of functions that can be configured in this EPC * @group: configfs group representing the PCI EPC device * @lock: spinlock to protect pci_epc ops + * @notifier: used to notify EPF of any EPC events (like linkup) */ struct pci_epc { struct device dev; @@ -99,6 +100,7 @@ struct pci_epc { struct config_group *group; /* spinlock to protect against concurrent access of EP controller */ spinlock_t lock; + struct atomic_notifier_head notifier; }; /** @@ -141,6 +143,12 @@ static inline void *epc_get_drvdata(struct pci_epc *epc) return dev_get_drvdata(&epc->dev); } +static inline int +pci_epc_register_notifier(struct pci_epc *epc, struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&epc->notifier, nb); +} + struct pci_epc * __devm_pci_epc_create(struct device *dev, const struct pci_epc_ops *ops, struct module *owner); diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 2d6f07556682..4993f7f6439b 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -55,13 +55,10 @@ struct pci_epf_header { * @bind: ops to perform when a EPC device has been bound to EPF device * @unbind: ops to perform when a binding has been lost between a EPC device * and EPF device - * @linkup: ops to perform when the EPC device has established a connection with - * a host system */ struct pci_epf_ops { int (*bind)(struct pci_epf *epf); void (*unbind)(struct pci_epf *epf); - void (*linkup)(struct pci_epf *epf); }; /** @@ -112,6 +109,7 @@ struct pci_epf_bar { * @epc: the EPC device to which this EPF device is bound * @driver: the EPF driver to which this EPF device is bound * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc + * @nb: notifier block to notify EPF of any EPC events (like linkup) */ struct pci_epf { struct device dev; @@ -125,6 +123,7 @@ struct pci_epf { struct pci_epc *epc; struct pci_epf_driver *driver; struct list_head list; + struct notifier_block nb; }; #define to_pci_epf(epf_dev) container_of((epf_dev), struct pci_epf, dev) @@ -154,5 +153,4 @@ void *pci_epf_alloc_space(struct pci_epf *epf, size_t size, enum pci_barno bar, void pci_epf_free_space(struct pci_epf *epf, void *addr, enum pci_barno bar); int pci_epf_bind(struct pci_epf *epf); void pci_epf_unbind(struct pci_epf *epf); -void pci_epf_linkup(struct pci_epf *epf); #endif /* __LINUX_PCI_EPF_H */ -- cgit v1.2.3 From 3d3248dbd018502f654064c78efcd2e165ab3486 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 24 Feb 2020 15:23:35 +0530 Subject: PCI: endpoint: Replace spinlock with mutex The pci_epc_ops is not intended to be invoked from interrupt context. Hence replace spin_lock_irqsave and spin_unlock_irqrestore with mutex_lock and mutex_unlock respectively. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epc-core.c | 82 ++++++++++++++----------------------- include/linux/pci-epc.h | 6 +-- 2 files changed, 34 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 2f6436599fcb..e51a12ed85bb 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -120,7 +120,6 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, u8 func_no) { const struct pci_epc_features *epc_features; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return NULL; @@ -128,9 +127,9 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc, if (!epc->ops->get_features) return NULL; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); epc_features = epc->ops->get_features(epc, func_no); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return epc_features; } @@ -144,14 +143,12 @@ EXPORT_SYMBOL_GPL(pci_epc_get_features); */ void pci_epc_stop(struct pci_epc *epc) { - unsigned long flags; - if (IS_ERR(epc) || !epc->ops->stop) return; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); epc->ops->stop(epc); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); } EXPORT_SYMBOL_GPL(pci_epc_stop); @@ -164,7 +161,6 @@ EXPORT_SYMBOL_GPL(pci_epc_stop); int pci_epc_start(struct pci_epc *epc) { int ret; - unsigned long flags; if (IS_ERR(epc)) return -EINVAL; @@ -172,9 +168,9 @@ int pci_epc_start(struct pci_epc *epc) if (!epc->ops->start) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); ret = epc->ops->start(epc); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return ret; } @@ -193,7 +189,6 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, enum pci_epc_irq_type type, u16 interrupt_num) { int ret; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; @@ -201,9 +196,9 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, if (!epc->ops->raise_irq) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); ret = epc->ops->raise_irq(epc, func_no, type, interrupt_num); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return ret; } @@ -219,7 +214,6 @@ EXPORT_SYMBOL_GPL(pci_epc_raise_irq); int pci_epc_get_msi(struct pci_epc *epc, u8 func_no) { int interrupt; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return 0; @@ -227,9 +221,9 @@ int pci_epc_get_msi(struct pci_epc *epc, u8 func_no) if (!epc->ops->get_msi) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); interrupt = epc->ops->get_msi(epc, func_no); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); if (interrupt < 0) return 0; @@ -252,7 +246,6 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) { int ret; u8 encode_int; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || interrupts > 32) @@ -263,9 +256,9 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 interrupts) encode_int = order_base_2(interrupts); - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); ret = epc->ops->set_msi(epc, func_no, encode_int); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return ret; } @@ -281,7 +274,6 @@ EXPORT_SYMBOL_GPL(pci_epc_set_msi); int pci_epc_get_msix(struct pci_epc *epc, u8 func_no) { int interrupt; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return 0; @@ -289,9 +281,9 @@ int pci_epc_get_msix(struct pci_epc *epc, u8 func_no) if (!epc->ops->get_msix) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); interrupt = epc->ops->get_msix(epc, func_no); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); if (interrupt < 0) return 0; @@ -311,7 +303,6 @@ EXPORT_SYMBOL_GPL(pci_epc_get_msix); int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts) { int ret; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || interrupts < 1 || interrupts > 2048) @@ -320,9 +311,9 @@ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u16 interrupts) if (!epc->ops->set_msix) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); ret = epc->ops->set_msix(epc, func_no, interrupts - 1); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return ret; } @@ -339,17 +330,15 @@ EXPORT_SYMBOL_GPL(pci_epc_set_msix); void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr) { - unsigned long flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return; if (!epc->ops->unmap_addr) return; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); epc->ops->unmap_addr(epc, func_no, phys_addr); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); } EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); @@ -367,7 +356,6 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, phys_addr_t phys_addr, u64 pci_addr, size_t size) { int ret; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; @@ -375,9 +363,9 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, if (!epc->ops->map_addr) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); ret = epc->ops->map_addr(epc, func_no, phys_addr, pci_addr, size); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return ret; } @@ -394,8 +382,6 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr); void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar) { - unsigned long flags; - if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || (epf_bar->barno == BAR_5 && epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)) @@ -404,9 +390,9 @@ void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, if (!epc->ops->clear_bar) return; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); epc->ops->clear_bar(epc, func_no, epf_bar); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); } EXPORT_SYMBOL_GPL(pci_epc_clear_bar); @@ -422,7 +408,6 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, struct pci_epf_bar *epf_bar) { int ret; - unsigned long irq_flags; int flags = epf_bar->flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions || @@ -437,9 +422,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, if (!epc->ops->set_bar) return 0; - spin_lock_irqsave(&epc->lock, irq_flags); + mutex_lock(&epc->lock); ret = epc->ops->set_bar(epc, func_no, epf_bar); - spin_unlock_irqrestore(&epc->lock, irq_flags); + mutex_unlock(&epc->lock); return ret; } @@ -460,7 +445,6 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *header) { int ret; - unsigned long flags; if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions) return -EINVAL; @@ -468,9 +452,9 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, if (!epc->ops->write_header) return 0; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); ret = epc->ops->write_header(epc, func_no, header); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return ret; } @@ -487,8 +471,6 @@ EXPORT_SYMBOL_GPL(pci_epc_write_header); */ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf) { - unsigned long flags; - if (epf->epc) return -EBUSY; @@ -500,9 +482,9 @@ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf) epf->epc = epc; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); list_add_tail(&epf->list, &epc->pci_epf); - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); return 0; } @@ -517,15 +499,13 @@ EXPORT_SYMBOL_GPL(pci_epc_add_epf); */ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf) { - unsigned long flags; - if (!epc || IS_ERR(epc) || !epf) return; - spin_lock_irqsave(&epc->lock, flags); + mutex_lock(&epc->lock); list_del(&epf->list); epf->epc = NULL; - spin_unlock_irqrestore(&epc->lock, flags); + mutex_unlock(&epc->lock); } EXPORT_SYMBOL_GPL(pci_epc_remove_epf); @@ -604,7 +584,7 @@ __pci_epc_create(struct device *dev, const struct pci_epc_ops *ops, goto err_ret; } - spin_lock_init(&epc->lock); + mutex_init(&epc->lock); INIT_LIST_HEAD(&epc->pci_epf); ATOMIC_INIT_NOTIFIER_HEAD(&epc->notifier); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 36644ccd32ac..9dd60f2e9705 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -88,7 +88,7 @@ struct pci_epc_mem { * @mem: address space of the endpoint controller * @max_functions: max number of functions that can be configured in this EPC * @group: configfs group representing the PCI EPC device - * @lock: spinlock to protect pci_epc ops + * @lock: mutex to protect pci_epc ops * @notifier: used to notify EPF of any EPC events (like linkup) */ struct pci_epc { @@ -98,8 +98,8 @@ struct pci_epc { struct pci_epc_mem *mem; u8 max_functions; struct config_group *group; - /* spinlock to protect against concurrent access of EP controller */ - spinlock_t lock; + /* mutex to protect against concurrent access of EP controller */ + struct mutex lock; struct atomic_notifier_head notifier; }; -- cgit v1.2.3 From 04e046ca57ebed3943422dee10eec9e73aec081e Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 24 Feb 2020 15:23:36 +0530 Subject: PCI: endpoint: Fix for concurrent memory allocation in OB address region pci-epc-mem uses a bitmap to manage the Endpoint outbound (OB) address region. This address region will be shared by multiple endpoint functions (in the case of multi function endpoint) and it has to be protected from concurrent access to avoid updating an inconsistent state. Use a mutex to protect bitmap updates to prevent the memory allocation API from returning incorrect addresses. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi Cc: stable@vger.kernel.org # v4.14+ --- drivers/pci/endpoint/pci-epc-mem.c | 10 ++++++++-- include/linux/pci-epc.h | 3 +++ 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c index d2b174ce15de..abfac1109a13 100644 --- a/drivers/pci/endpoint/pci-epc-mem.c +++ b/drivers/pci/endpoint/pci-epc-mem.c @@ -79,6 +79,7 @@ int __pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_base, size_t size, mem->page_size = page_size; mem->pages = pages; mem->size = size; + mutex_init(&mem->lock); epc->mem = mem; @@ -122,7 +123,7 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, phys_addr_t *phys_addr, size_t size) { int pageno; - void __iomem *virt_addr; + void __iomem *virt_addr = NULL; struct pci_epc_mem *mem = epc->mem; unsigned int page_shift = ilog2(mem->page_size); int order; @@ -130,15 +131,18 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, size = ALIGN(size, mem->page_size); order = pci_epc_mem_get_order(mem, size); + mutex_lock(&mem->lock); pageno = bitmap_find_free_region(mem->bitmap, mem->pages, order); if (pageno < 0) - return NULL; + goto ret; *phys_addr = mem->phys_base + ((phys_addr_t)pageno << page_shift); virt_addr = ioremap(*phys_addr, size); if (!virt_addr) bitmap_release_region(mem->bitmap, pageno, order); +ret: + mutex_unlock(&mem->lock); return virt_addr; } EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr); @@ -164,7 +168,9 @@ void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr, pageno = (phys_addr - mem->phys_base) >> page_shift; size = ALIGN(size, mem->page_size); order = pci_epc_mem_get_order(mem, size); + mutex_lock(&mem->lock); bitmap_release_region(mem->bitmap, pageno, order); + mutex_unlock(&mem->lock); } EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 9dd60f2e9705..4e3e527c49d1 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -71,6 +71,7 @@ struct pci_epc_ops { * @bitmap: bitmap to manage the PCI address space * @pages: number of bits representing the address region * @page_size: size of each page + * @lock: mutex to protect bitmap */ struct pci_epc_mem { phys_addr_t phys_base; @@ -78,6 +79,8 @@ struct pci_epc_mem { unsigned long *bitmap; size_t page_size; int pages; + /* mutex to protect against concurrent access for memory allocation*/ + struct mutex lock; }; /** -- cgit v1.2.3 From 77f576deaa393b54a0f2ca8ab1ab5b2d3c6b971b Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 20 Jan 2020 13:21:22 +0530 Subject: nl80211: Add NL command to support TID speicific configurations Add the new NL80211_CMD_SET_TID_CONFIG command to support data TID specific configuration. Per TID configuration is passed in the nested NL80211_ATTR_TID_CONFIG attribute. This patch adds support to configure per TID noack policy through the NL80211_TID_CONFIG_ATTR_NOACK attribute. Signed-off-by: Tamizh chelvam Link: https://lore.kernel.org/r/1579506687-18296-2-git-send-email-tamizhr@codeaurora.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 40 ++++++++++++ include/uapi/linux/nl80211.h | 71 +++++++++++++++++++++ net/wireless/nl80211.c | 148 +++++++++++++++++++++++++++++++++++++++++++ net/wireless/rdev-ops.h | 24 +++++++ net/wireless/trace.h | 37 +++++++++++ 5 files changed, 320 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 04b6df15706c..65a8bf73d21e 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -626,6 +626,38 @@ struct cfg80211_chan_def { struct ieee80211_edmg edmg; }; +enum ieee80211_tid_conf_mask { + IEEE80211_TID_CONF_NOACK = BIT(NL80211_TID_CONFIG_ATTR_NOACK), +}; + +/** + * struct ieee80211_tid_cfg - TID specific configuration + * @config_override: Flag to notify driver to reset TID configuration + * of the peer. + * @tid: TID number + * @tid_conf_mask: bitmap indicating which parameter changed + * see &enum ieee80211_tid_conf_mask + * @noack: noack configuration value for the TID + */ +struct ieee80211_tid_cfg { + bool config_override; + u8 tid; + u32 tid_conf_mask; + enum nl80211_tid_config noack; +}; + +/** + * struct ieee80211_tid_config - TID configuration + * @peer: Station's MAC address + * @n_tid_conf: Number of TID specific configurations to be applied + * @tid_conf: Configuration change info + */ +struct ieee80211_tid_config { + const u8 *peer; + u32 n_tid_conf; + struct ieee80211_tid_cfg tid_conf[]; +}; + /** * cfg80211_get_chandef_type - return old channel type from chandef * @chandef: the channel definition @@ -3671,6 +3703,10 @@ struct cfg80211_update_owe_info { * * @probe_mesh_link: Probe direct Mesh peer's link quality by sending data frame * and overrule HWMP path selection algorithm. + * @set_tid_config: TID specific configuration, this can be peer or BSS specific + * This callback may sleep. + * @reset_tid_config: Reset TID specific configuration for the peer. + * This callback may sleep. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -3994,6 +4030,10 @@ struct cfg80211_ops { struct cfg80211_update_owe_info *owe_info); int (*probe_mesh_link)(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len); + int (*set_tid_config)(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_tid_config *tid_conf); + int (*reset_tid_config)(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 tid); }; /* diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 934e62fe8705..0b12fcffb518 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -264,6 +264,27 @@ * %NL80211_ATTR_VLAN_ID. */ +/** + * DOC: TID configuration + * + * TID configuration support can be advertised by drivers by setting + * @NL80211_EXT_FEATURE_PER_TID_* and/or @NL80211_EXT_FEATURE_PER_STA_* config + * mentioned in &enum nl80211_tid_config_attr. + * Needed configuration parameters are mentioned in + * &enum nl80211_tid_config_attr and it will be passed using + * %NL80211_CMD_SET_TID_CONFIG through %NL80211_ATTR_TID_CONFIG. + * If the configuration needs to be applied for specific peer then MAC address + * of the peer needs to be passed in %NL80211_ATT_MAC, otherwise the + * configuration will be applied for all the connected peers in the vif except + * the peer which has peer specific configuration for the TID. + * And the peer specific configuration will be overridden if + * %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set. + * All this configurations are valid only for STA's current connection + * i.e. the configurations will be reset to default when the STA connects back + * after disconnection/roaming, and this configuration will be cleared when + * the interface goes down. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -1125,6 +1146,9 @@ * peer MAC address and %NL80211_ATTR_FRAME is used to specify the frame * content. The frame is ethernet data. * + * @NL80211_CMD_SET_TID_CONFIG: Data frame TID specific configuration + * is passed using %NL80211_ATTR_TID_CONFIG attribute. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1349,6 +1373,8 @@ enum nl80211_commands { NL80211_CMD_PROBE_MESH_LINK, + NL80211_CMD_SET_TID_CONFIG, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2409,6 +2435,9 @@ enum nl80211_commands { * %NL80211_ATTR_AKM_SUITES are default capabilities if AKM suites not * advertised for a specific interface type. * + * @NL80211_ATTR_TID_CONFIG: TID specific configuration in a + * nested attribute with &enum nl80211_tid_config_attr sub-attributes. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -2877,6 +2906,8 @@ enum nl80211_attrs { NL80211_ATTR_IFTYPE_AKM_SUITES, + NL80211_ATTR_TID_CONFIG, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4722,6 +4753,40 @@ enum nl80211_tx_power_setting { NL80211_TX_POWER_FIXED, }; +/** + * enum nl80211_tid_config - TID config state + * @NL80211_TID_CONFIG_ENABLE: Enable config for the TID + * @NL80211_TID_CONFIG_DISABLE: Disable config for the TID + */ +enum nl80211_tid_config { + NL80211_TID_CONFIG_ENABLE, + NL80211_TID_CONFIG_DISABLE, +}; + +/* enum nl80211_tid_config_attr - TID specific configuration. + * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if no peer + * is selected, if set indicates that the new configuration overrides + * all previous peer configurations, otherwise previous peer specific + * configurations should be left untouched. If peer is selected then + * it will reset particular TID configuration of that peer and it will + * not accept other TID config attributes along with peer. + * @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs(bit 0 to 7) + * Its type is u8. + * @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID. + * specified in %NL80211_TID_CONFIG_ATTR_TID. see %enum nl80211_tid_config. + * Its type is u8. + */ +enum nl80211_tid_config_attr { + __NL80211_TID_CONFIG_ATTR_INVALID, + NL80211_TID_CONFIG_ATTR_OVERRIDE, + NL80211_TID_CONFIG_ATTR_TIDS, + NL80211_TID_CONFIG_ATTR_NOACK, + + /* keep last */ + __NL80211_TID_CONFIG_ATTR_AFTER_LAST, + NL80211_TID_CONFIG_ATTR_MAX = __NL80211_TID_CONFIG_ATTR_AFTER_LAST - 1 +}; + /** * enum nl80211_packet_pattern_attr - packet pattern attribute * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute @@ -5540,6 +5605,10 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL) * feature, which prevents bufferbloat by using the expected transmission * time to limit the amount of data buffered in the hardware. + * @NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG: Driver supports per TID NoAck + * policy functionality. + * @NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG: Driver supports STA specific NoAck + * policy functionality. * * @NL80211_EXT_FEATURE_BEACON_PROTECTION: The driver supports Beacon protection * and can receive key configuration for BIGTK using key indexes 6 and 7. @@ -5592,6 +5661,8 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VLAN_OFFLOAD, NL80211_EXT_FEATURE_AQL, NL80211_EXT_FEATURE_BEACON_PROTECTION, + NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG, + NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a75f72288139..a0839fae6555 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -328,6 +328,14 @@ he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = { [NL80211_HE_BSS_COLOR_ATTR_PARTIAL] = { .type = NLA_FLAG }, }; +static const struct nla_policy +nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { + [NL80211_TID_CONFIG_ATTR_OVERRIDE] = { .type = NLA_FLAG }, + [NL80211_TID_CONFIG_ATTR_TIDS] = { .type = NLA_U8 }, + [NL80211_TID_CONFIG_ATTR_NOACK] = + NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), +}; + const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD }, [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, @@ -634,6 +642,8 @@ const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), [NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), [NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy), + [NL80211_ATTR_TID_CONFIG] = + NLA_POLICY_NESTED_ARRAY(nl80211_tid_config_attr_policy), }; /* policy for the key attributes */ @@ -13934,6 +13944,137 @@ static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info) return rdev_probe_mesh_link(rdev, dev, dest, buf, len); } +static int +__nl80211_check_tid_conf_support(struct cfg80211_registered_device *rdev, + struct netlink_ext_ack *extack, + const u8 *peer, struct nlattr *attrs[], + struct ieee80211_tid_cfg *tid_conf, + enum nl80211_tid_config_attr attr, + enum nl80211_ext_feature_index per_tid_config, + enum nl80211_ext_feature_index per_sta_config) +{ + if (!wiphy_ext_feature_isset(&rdev->wiphy, per_tid_config)) { + NL_SET_ERR_MSG_ATTR(extack, attrs[attr], + "TID specific configuration not supported"); + return -ENOTSUPP; + } + + if (peer && !wiphy_ext_feature_isset(&rdev->wiphy, per_sta_config)) { + NL_SET_ERR_MSG_ATTR(extack, attrs[attr], + "peer specific TID configuration not supported"); + return -ENOTSUPP; + } + + tid_conf->tid_conf_mask |= BIT(attr); + return 0; +} + +#define nl80211_check_tid_config_support(rdev, extack, peer, attrs, tid_conf, \ + conf) \ + __nl80211_check_tid_conf_support(rdev, extack, peer, attrs, tid_conf, \ + NL80211_TID_CONFIG_ATTR_##conf, \ + NL80211_EXT_FEATURE_PER_TID_##conf##_CONFIG, \ + NL80211_EXT_FEATURE_PER_STA_##conf##_CONFIG) + +static int parse_tid_conf(struct cfg80211_registered_device *rdev, + struct nlattr *attrs[], struct net_device *dev, + struct ieee80211_tid_cfg *tid_conf, + struct genl_info *info, const u8 *peer) +{ + struct netlink_ext_ack *extack = info->extack; + int err; + + if (!attrs[NL80211_TID_CONFIG_ATTR_TIDS]) + return -EINVAL; + + tid_conf->config_override = + nla_get_flag(attrs[NL80211_TID_CONFIG_ATTR_OVERRIDE]); + tid_conf->tid = nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_TIDS]); + + if (tid_conf->config_override) { + if (rdev->ops->reset_tid_config) { + err = rdev_reset_tid_config(rdev, dev, peer, + tid_conf->tid); + /* If peer is there no other configuration will be + * allowed + */ + if (err || peer) + return err; + } else { + return -EINVAL; + } + } + + if (attrs[NL80211_TID_CONFIG_ATTR_NOACK]) { + err = nl80211_check_tid_config_support(rdev, extack, peer, + attrs, tid_conf, + NOACK); + if (err) + return err; + + tid_conf->noack = + nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_NOACK]); + } + + return 0; +} + +static int nl80211_set_tid_config(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct nlattr *attrs[NL80211_TID_CONFIG_ATTR_MAX + 1]; + struct net_device *dev = info->user_ptr[1]; + struct ieee80211_tid_config *tid_config; + struct nlattr *tid; + int conf_idx = 0, rem_conf; + int ret = -EINVAL; + u32 num_conf = 0; + + if (!info->attrs[NL80211_ATTR_TID_CONFIG]) + return -EINVAL; + + if (!rdev->ops->set_tid_config) + return -EOPNOTSUPP; + + nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], + rem_conf) + num_conf++; + + tid_config = kzalloc(struct_size(tid_config, tid_conf, num_conf), + GFP_KERNEL); + if (!tid_config) + return -ENOMEM; + + tid_config->n_tid_conf = num_conf; + + if (info->attrs[NL80211_ATTR_MAC]) + tid_config->peer = nla_data(info->attrs[NL80211_ATTR_MAC]); + + nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], + rem_conf) { + ret = nla_parse_nested(attrs, NL80211_TID_CONFIG_ATTR_MAX, + tid, NULL, NULL); + + if (ret) + goto bad_tid_conf; + + ret = parse_tid_conf(rdev, attrs, dev, + &tid_config->tid_conf[conf_idx], + info, tid_config->peer); + if (ret) + goto bad_tid_conf; + + conf_idx++; + } + + ret = rdev_set_tid_config(rdev, dev, tid_config); + +bad_tid_conf: + kfree(tid_config); + return ret; +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -14888,6 +15029,13 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_TID_CONFIG, + .doit = nl80211_set_tid_config, + .flags = GENL_UNS_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_family nl80211_fam __ro_after_init = { diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index af7fcf2a3b4a..a754e0496b6c 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1326,4 +1326,28 @@ rdev_probe_mesh_link(struct cfg80211_registered_device *rdev, return ret; } +static inline int rdev_set_tid_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, + struct ieee80211_tid_config *tid_conf) +{ + int ret; + + trace_rdev_set_tid_config(&rdev->wiphy, dev, tid_conf); + ret = rdev->ops->set_tid_config(&rdev->wiphy, dev, tid_conf); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + +static inline int rdev_reset_tid_config(struct cfg80211_registered_device *rdev, + struct net_device *dev, const u8 *peer, + u8 tid) +{ + int ret; + + trace_rdev_reset_tid_config(&rdev->wiphy, dev, peer, tid); + ret = rdev->ops->reset_tid_config(&rdev->wiphy, dev, peer, tid); + trace_rdev_return_int(&rdev->wiphy, ret); + return ret; +} + #endif /* __CFG80211_RDEV_OPS */ diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 56b78222746c..167b18896cd6 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3480,6 +3480,43 @@ TRACE_EVENT(rdev_probe_mesh_link, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dest)) ); +TRACE_EVENT(rdev_set_tid_config, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + struct ieee80211_tid_config *tid_conf), + TP_ARGS(wiphy, netdev, tid_conf), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, tid_conf->peer); + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT, + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) +); + +TRACE_EVENT(rdev_reset_tid_config, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, + const u8 *peer, u8 tid), + TP_ARGS(wiphy, netdev, peer, tid), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(u8, tid) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->tid = tid; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", tid: %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tid) +); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #undef TRACE_INCLUDE_PATH -- cgit v1.2.3 From 07301c982643a432212840a4b648b5d3f5a061fa Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 24 Feb 2020 15:23:37 +0530 Subject: PCI: endpoint: Protect concurrent access to pci_epf_ops with mutex Protect concurrent access to pci_epf_ops with a mutex. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-epf-core.c | 11 ++++++++++- include/linux/pci-epf.h | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pci/endpoint/pci-epf-core.c b/drivers/pci/endpoint/pci-epf-core.c index 93f28c65ace0..6e0648991b5c 100644 --- a/drivers/pci/endpoint/pci-epf-core.c +++ b/drivers/pci/endpoint/pci-epf-core.c @@ -35,7 +35,9 @@ void pci_epf_unbind(struct pci_epf *epf) return; } + mutex_lock(&epf->lock); epf->driver->ops->unbind(epf); + mutex_unlock(&epf->lock); module_put(epf->driver->owner); } EXPORT_SYMBOL_GPL(pci_epf_unbind); @@ -49,6 +51,8 @@ EXPORT_SYMBOL_GPL(pci_epf_unbind); */ int pci_epf_bind(struct pci_epf *epf) { + int ret; + if (!epf->driver) { dev_WARN(&epf->dev, "epf device not bound to driver\n"); return -EINVAL; @@ -57,7 +61,11 @@ int pci_epf_bind(struct pci_epf *epf) if (!try_module_get(epf->driver->owner)) return -EAGAIN; - return epf->driver->ops->bind(epf); + mutex_lock(&epf->lock); + ret = epf->driver->ops->bind(epf); + mutex_unlock(&epf->lock); + + return ret; } EXPORT_SYMBOL_GPL(pci_epf_bind); @@ -252,6 +260,7 @@ struct pci_epf *pci_epf_create(const char *name) device_initialize(dev); dev->bus = &pci_epf_bus_type; dev->type = &pci_epf_type; + mutex_init(&epf->lock); ret = dev_set_name(dev, "%s", name); if (ret) { diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index 4993f7f6439b..bcdf4f07bde7 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -110,6 +110,7 @@ struct pci_epf_bar { * @driver: the EPF driver to which this EPF device is bound * @list: to add pci_epf as a list of PCI endpoint functions to pci_epc * @nb: notifier block to notify EPF of any EPC events (like linkup) + * @lock: mutex to protect pci_epf_ops */ struct pci_epf { struct device dev; @@ -124,6 +125,8 @@ struct pci_epf { struct pci_epf_driver *driver; struct list_head list; struct notifier_block nb; + /* mutex to protect against concurrent access of pci_epf_ops */ + struct mutex lock; }; #define to_pci_epf(epf_dev) container_of((epf_dev), struct pci_epf, dev) -- cgit v1.2.3 From 2499ee84e02774a8573b7b4c76c8f2ea38669313 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Mon, 24 Feb 2020 15:23:38 +0530 Subject: PCI: endpoint: Assign function number for each PF in EPC core The PCIe endpoint core relies on the drivers that invoke the pci_epc_add_epf() API to allocate and assign a function number to each physical function (PF). Since endpoint function device can be created by multiple mechanisms (configfs, devicetree, etc..), allowing each of these mechanisms to assign a function number would result in mutliple endpoint function devices having the same function number. In order to avoid this, let EPC core assign a function number to the endpoint device. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Lorenzo Pieralisi --- drivers/pci/endpoint/pci-ep-cfs.c | 27 +++++---------------------- drivers/pci/endpoint/pci-epc-core.c | 26 ++++++++++++++++++++++---- include/linux/pci-epc.h | 2 ++ 3 files changed, 29 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index d1288a0bd530..e7e8367eead1 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -29,7 +29,6 @@ struct pci_epc_group { struct config_group group; struct pci_epc *epc; bool start; - unsigned long function_num_map; }; static inline struct pci_epf_group *to_pci_epf_group(struct config_item *item) @@ -89,37 +88,22 @@ static int pci_epc_epf_link(struct config_item *epc_item, struct config_item *epf_item) { int ret; - u32 func_no = 0; struct pci_epf_group *epf_group = to_pci_epf_group(epf_item); struct pci_epc_group *epc_group = to_pci_epc_group(epc_item); struct pci_epc *epc = epc_group->epc; struct pci_epf *epf = epf_group->epf; - func_no = find_first_zero_bit(&epc_group->function_num_map, - BITS_PER_LONG); - if (func_no >= BITS_PER_LONG) - return -EINVAL; - - set_bit(func_no, &epc_group->function_num_map); - epf->func_no = func_no; - ret = pci_epc_add_epf(epc, epf); if (ret) - goto err_add_epf; + return ret; ret = pci_epf_bind(epf); - if (ret) - goto err_epf_bind; + if (ret) { + pci_epc_remove_epf(epc, epf); + return ret; + } return 0; - -err_epf_bind: - pci_epc_remove_epf(epc, epf); - -err_add_epf: - clear_bit(func_no, &epc_group->function_num_map); - - return ret; } static void pci_epc_epf_unlink(struct config_item *epc_item, @@ -134,7 +118,6 @@ static void pci_epc_epf_unlink(struct config_item *epc_item, epc = epc_group->epc; epf = epf_group->epf; - clear_bit(epf->func_no, &epc_group->function_num_map); pci_epf_unbind(epf); pci_epc_remove_epf(epc, epf); } diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index e51a12ed85bb..dc1c673534e0 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -471,22 +471,39 @@ EXPORT_SYMBOL_GPL(pci_epc_write_header); */ int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf) { + u32 func_no; + int ret = 0; + if (epf->epc) return -EBUSY; if (IS_ERR(epc)) return -EINVAL; - if (epf->func_no > epc->max_functions - 1) - return -EINVAL; + mutex_lock(&epc->lock); + func_no = find_first_zero_bit(&epc->function_num_map, + BITS_PER_LONG); + if (func_no >= BITS_PER_LONG) { + ret = -EINVAL; + goto ret; + } + + if (func_no > epc->max_functions - 1) { + dev_err(&epc->dev, "Exceeding max supported Function Number\n"); + ret = -EINVAL; + goto ret; + } + set_bit(func_no, &epc->function_num_map); + epf->func_no = func_no; epf->epc = epc; - mutex_lock(&epc->lock); list_add_tail(&epf->list, &epc->pci_epf); + +ret: mutex_unlock(&epc->lock); - return 0; + return ret; } EXPORT_SYMBOL_GPL(pci_epc_add_epf); @@ -503,6 +520,7 @@ void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf) return; mutex_lock(&epc->lock); + clear_bit(epf->func_no, &epc->function_num_map); list_del(&epf->list); epf->epc = NULL; mutex_unlock(&epc->lock); diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 4e3e527c49d1..ccaf6e3fa931 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -92,6 +92,7 @@ struct pci_epc_mem { * @max_functions: max number of functions that can be configured in this EPC * @group: configfs group representing the PCI EPC device * @lock: mutex to protect pci_epc ops + * @function_num_map: bitmap to manage physical function number * @notifier: used to notify EPF of any EPC events (like linkup) */ struct pci_epc { @@ -103,6 +104,7 @@ struct pci_epc { struct config_group *group; /* mutex to protect against concurrent access of EP controller */ struct mutex lock; + unsigned long function_num_map; struct atomic_notifier_head notifier; }; -- cgit v1.2.3 From b2b2042b204796190af7c20069ab790a614c36d0 Mon Sep 17 00:00:00 2001 From: Mel Gorman Date: Mon, 24 Feb 2020 09:52:13 +0000 Subject: sched/numa: Distinguish between the different task_numa_migrate() failure cases sched:sched_stick_numa is meant to fire when a task is unable to migrate to the preferred node but from the trace, it's possibile to tell the difference between "no CPU found", "migration to idle CPU failed" and "tasks could not be swapped". Extend the tracepoint accordingly. Signed-off-by: Mel Gorman [ Minor edits. ] Signed-off-by: Ingo Molnar Acked-by: Peter Zijlstra Cc: Steven Rostedt Cc: Vincent Guittot Cc: Juri Lelli Cc: Dietmar Eggemann Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-4-mgorman@techsingularity.net --- include/trace/events/sched.h | 49 ++++++++++++++++++++++++-------------------- kernel/sched/fair.c | 6 +++--- 2 files changed, 30 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 420e80e56e55..9c3ebb7c83a5 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -487,7 +487,11 @@ TRACE_EVENT(sched_process_hang, ); #endif /* CONFIG_DETECT_HUNG_TASK */ -DECLARE_EVENT_CLASS(sched_move_task_template, +/* + * Tracks migration of tasks from one runqueue to another. Can be used to + * detect if automatic NUMA balancing is bouncing between nodes. + */ +TRACE_EVENT(sched_move_numa, TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), @@ -519,23 +523,7 @@ DECLARE_EVENT_CLASS(sched_move_task_template, __entry->dst_cpu, __entry->dst_nid) ); -/* - * Tracks migration of tasks from one runqueue to another. Can be used to - * detect if automatic NUMA balancing is bouncing between nodes - */ -DEFINE_EVENT(sched_move_task_template, sched_move_numa, - TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), - - TP_ARGS(tsk, src_cpu, dst_cpu) -); - -DEFINE_EVENT(sched_move_task_template, sched_stick_numa, - TP_PROTO(struct task_struct *tsk, int src_cpu, int dst_cpu), - - TP_ARGS(tsk, src_cpu, dst_cpu) -); - -TRACE_EVENT(sched_swap_numa, +DECLARE_EVENT_CLASS(sched_numa_pair_template, TP_PROTO(struct task_struct *src_tsk, int src_cpu, struct task_struct *dst_tsk, int dst_cpu), @@ -561,11 +549,11 @@ TRACE_EVENT(sched_swap_numa, __entry->src_ngid = task_numa_group_id(src_tsk); __entry->src_cpu = src_cpu; __entry->src_nid = cpu_to_node(src_cpu); - __entry->dst_pid = task_pid_nr(dst_tsk); - __entry->dst_tgid = task_tgid_nr(dst_tsk); - __entry->dst_ngid = task_numa_group_id(dst_tsk); + __entry->dst_pid = dst_tsk ? task_pid_nr(dst_tsk) : 0; + __entry->dst_tgid = dst_tsk ? task_tgid_nr(dst_tsk) : 0; + __entry->dst_ngid = dst_tsk ? task_numa_group_id(dst_tsk) : 0; __entry->dst_cpu = dst_cpu; - __entry->dst_nid = cpu_to_node(dst_cpu); + __entry->dst_nid = dst_cpu >= 0 ? cpu_to_node(dst_cpu) : -1; ), TP_printk("src_pid=%d src_tgid=%d src_ngid=%d src_cpu=%d src_nid=%d dst_pid=%d dst_tgid=%d dst_ngid=%d dst_cpu=%d dst_nid=%d", @@ -575,6 +563,23 @@ TRACE_EVENT(sched_swap_numa, __entry->dst_cpu, __entry->dst_nid) ); +DEFINE_EVENT(sched_numa_pair_template, sched_stick_numa, + + TP_PROTO(struct task_struct *src_tsk, int src_cpu, + struct task_struct *dst_tsk, int dst_cpu), + + TP_ARGS(src_tsk, src_cpu, dst_tsk, dst_cpu) +); + +DEFINE_EVENT(sched_numa_pair_template, sched_swap_numa, + + TP_PROTO(struct task_struct *src_tsk, int src_cpu, + struct task_struct *dst_tsk, int dst_cpu), + + TP_ARGS(src_tsk, src_cpu, dst_tsk, dst_cpu) +); + + /* * Tracepoint for waking a polling cpu without an IPI. */ diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index f524ce3cea82..5d9c23c134af 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -1849,7 +1849,7 @@ static int task_numa_migrate(struct task_struct *p) /* No better CPU than the current one was found. */ if (env.best_cpu == -1) { - trace_sched_stick_numa(p, env.src_cpu, -1); + trace_sched_stick_numa(p, env.src_cpu, NULL, -1); return -EAGAIN; } @@ -1858,7 +1858,7 @@ static int task_numa_migrate(struct task_struct *p) ret = migrate_task_to(p, env.best_cpu); WRITE_ONCE(best_rq->numa_migrate_on, 0); if (ret != 0) - trace_sched_stick_numa(p, env.src_cpu, env.best_cpu); + trace_sched_stick_numa(p, env.src_cpu, NULL, env.best_cpu); return ret; } @@ -1866,7 +1866,7 @@ static int task_numa_migrate(struct task_struct *p) WRITE_ONCE(best_rq->numa_migrate_on, 0); if (ret != 0) - trace_sched_stick_numa(p, env.src_cpu, task_cpu(env.best_task)); + trace_sched_stick_numa(p, env.src_cpu, env.best_task, env.best_cpu); put_task_struct(env.best_task); return ret; } -- cgit v1.2.3 From 0dacee1bfa70e171be3a12a30414c228453048d2 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:17 +0000 Subject: sched/pelt: Remove unused runnable load average Now that runnable_load_avg is no more used, we can remove it to make space for a new signal. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-8-mgorman@techsingularity.net --- include/linux/sched.h | 5 +- kernel/sched/core.c | 2 - kernel/sched/debug.c | 8 ---- kernel/sched/fair.c | 130 +++++++------------------------------------------- kernel/sched/pelt.c | 62 ++++++++++-------------- kernel/sched/sched.h | 7 +-- 6 files changed, 43 insertions(+), 171 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 04278493bf15..037eaffabc24 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -357,7 +357,7 @@ struct util_est { /* * The load_avg/util_avg accumulates an infinite geometric series - * (see __update_load_avg() in kernel/sched/fair.c). + * (see __update_load_avg_cfs_rq() in kernel/sched/pelt.c). * * [load_avg definition] * @@ -401,11 +401,9 @@ struct util_est { struct sched_avg { u64 last_update_time; u64 load_sum; - u64 runnable_load_sum; u32 util_sum; u32 period_contrib; unsigned long load_avg; - unsigned long runnable_load_avg; unsigned long util_avg; struct util_est util_est; } ____cacheline_aligned; @@ -449,7 +447,6 @@ struct sched_statistics { struct sched_entity { /* For load-balancing: */ struct load_weight load; - unsigned long runnable_weight; struct rb_node run_node; struct list_head group_node; unsigned int on_rq; diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e94819d573be..8e6f38073ab3 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -761,7 +761,6 @@ static void set_load_weight(struct task_struct *p, bool update_load) if (task_has_idle_policy(p)) { load->weight = scale_load(WEIGHT_IDLEPRIO); load->inv_weight = WMULT_IDLEPRIO; - p->se.runnable_weight = load->weight; return; } @@ -774,7 +773,6 @@ static void set_load_weight(struct task_struct *p, bool update_load) } else { load->weight = scale_load(sched_prio_to_weight[prio]); load->inv_weight = sched_prio_to_wmult[prio]; - p->se.runnable_weight = load->weight; } } diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 879d3ccf3806..cfecaad387c0 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -402,11 +402,9 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group } P(se->load.weight); - P(se->runnable_weight); #ifdef CONFIG_SMP P(se->avg.load_avg); P(se->avg.util_avg); - P(se->avg.runnable_load_avg); #endif #undef PN_SCHEDSTAT @@ -524,11 +522,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) SEQ_printf(m, " .%-30s: %d\n", "nr_running", cfs_rq->nr_running); SEQ_printf(m, " .%-30s: %ld\n", "load", cfs_rq->load.weight); #ifdef CONFIG_SMP - SEQ_printf(m, " .%-30s: %ld\n", "runnable_weight", cfs_rq->runnable_weight); SEQ_printf(m, " .%-30s: %lu\n", "load_avg", cfs_rq->avg.load_avg); - SEQ_printf(m, " .%-30s: %lu\n", "runnable_load_avg", - cfs_rq->avg.runnable_load_avg); SEQ_printf(m, " .%-30s: %lu\n", "util_avg", cfs_rq->avg.util_avg); SEQ_printf(m, " .%-30s: %u\n", "util_est_enqueued", @@ -947,13 +942,10 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns, "nr_involuntary_switches", (long long)p->nivcsw); P(se.load.weight); - P(se.runnable_weight); #ifdef CONFIG_SMP P(se.avg.load_sum); - P(se.avg.runnable_load_sum); P(se.avg.util_sum); P(se.avg.load_avg); - P(se.avg.runnable_load_avg); P(se.avg.util_avg); P(se.avg.last_update_time); P(se.avg.util_est.ewma); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 7a3c66f1762f..b0fb3d6a6185 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -741,9 +741,7 @@ void init_entity_runnable_average(struct sched_entity *se) * nothing has been attached to the task group yet. */ if (entity_is_task(se)) - sa->runnable_load_avg = sa->load_avg = scale_load_down(se->load.weight); - - se->runnable_weight = se->load.weight; + sa->load_avg = scale_load_down(se->load.weight); /* when this task enqueue'ed, it will contribute to its cfs_rq's load_avg */ } @@ -2898,25 +2896,6 @@ account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) } while (0) #ifdef CONFIG_SMP -static inline void -enqueue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) -{ - cfs_rq->runnable_weight += se->runnable_weight; - - cfs_rq->avg.runnable_load_avg += se->avg.runnable_load_avg; - cfs_rq->avg.runnable_load_sum += se_runnable(se) * se->avg.runnable_load_sum; -} - -static inline void -dequeue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) -{ - cfs_rq->runnable_weight -= se->runnable_weight; - - sub_positive(&cfs_rq->avg.runnable_load_avg, se->avg.runnable_load_avg); - sub_positive(&cfs_rq->avg.runnable_load_sum, - se_runnable(se) * se->avg.runnable_load_sum); -} - static inline void enqueue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { @@ -2932,28 +2911,22 @@ dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) } #else static inline void -enqueue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } -static inline void -dequeue_runnable_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } -static inline void enqueue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } static inline void dequeue_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *se) { } #endif static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, - unsigned long weight, unsigned long runnable) + unsigned long weight) { if (se->on_rq) { /* commit outstanding execution time */ if (cfs_rq->curr == se) update_curr(cfs_rq); account_entity_dequeue(cfs_rq, se); - dequeue_runnable_load_avg(cfs_rq, se); } dequeue_load_avg(cfs_rq, se); - se->runnable_weight = runnable; update_load_set(&se->load, weight); #ifdef CONFIG_SMP @@ -2961,16 +2934,13 @@ static void reweight_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, u32 divider = LOAD_AVG_MAX - 1024 + se->avg.period_contrib; se->avg.load_avg = div_u64(se_weight(se) * se->avg.load_sum, divider); - se->avg.runnable_load_avg = - div_u64(se_runnable(se) * se->avg.runnable_load_sum, divider); } while (0); #endif enqueue_load_avg(cfs_rq, se); - if (se->on_rq) { + if (se->on_rq) account_entity_enqueue(cfs_rq, se); - enqueue_runnable_load_avg(cfs_rq, se); - } + } void reweight_task(struct task_struct *p, int prio) @@ -2980,7 +2950,7 @@ void reweight_task(struct task_struct *p, int prio) struct load_weight *load = &se->load; unsigned long weight = scale_load(sched_prio_to_weight[prio]); - reweight_entity(cfs_rq, se, weight, weight); + reweight_entity(cfs_rq, se, weight); load->inv_weight = sched_prio_to_wmult[prio]; } @@ -3092,50 +3062,6 @@ static long calc_group_shares(struct cfs_rq *cfs_rq) */ return clamp_t(long, shares, MIN_SHARES, tg_shares); } - -/* - * This calculates the effective runnable weight for a group entity based on - * the group entity weight calculated above. - * - * Because of the above approximation (2), our group entity weight is - * an load_avg based ratio (3). This means that it includes blocked load and - * does not represent the runnable weight. - * - * Approximate the group entity's runnable weight per ratio from the group - * runqueue: - * - * grq->avg.runnable_load_avg - * ge->runnable_weight = ge->load.weight * -------------------------- (7) - * grq->avg.load_avg - * - * However, analogous to above, since the avg numbers are slow, this leads to - * transients in the from-idle case. Instead we use: - * - * ge->runnable_weight = ge->load.weight * - * - * max(grq->avg.runnable_load_avg, grq->runnable_weight) - * ----------------------------------------------------- (8) - * max(grq->avg.load_avg, grq->load.weight) - * - * Where these max() serve both to use the 'instant' values to fix the slow - * from-idle and avoid the /0 on to-idle, similar to (6). - */ -static long calc_group_runnable(struct cfs_rq *cfs_rq, long shares) -{ - long runnable, load_avg; - - load_avg = max(cfs_rq->avg.load_avg, - scale_load_down(cfs_rq->load.weight)); - - runnable = max(cfs_rq->avg.runnable_load_avg, - scale_load_down(cfs_rq->runnable_weight)); - - runnable *= shares; - if (load_avg) - runnable /= load_avg; - - return clamp_t(long, runnable, MIN_SHARES, shares); -} #endif /* CONFIG_SMP */ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); @@ -3147,7 +3073,7 @@ static inline int throttled_hierarchy(struct cfs_rq *cfs_rq); static void update_cfs_group(struct sched_entity *se) { struct cfs_rq *gcfs_rq = group_cfs_rq(se); - long shares, runnable; + long shares; if (!gcfs_rq) return; @@ -3156,16 +3082,15 @@ static void update_cfs_group(struct sched_entity *se) return; #ifndef CONFIG_SMP - runnable = shares = READ_ONCE(gcfs_rq->tg->shares); + shares = READ_ONCE(gcfs_rq->tg->shares); if (likely(se->load.weight == shares)) return; #else shares = calc_group_shares(gcfs_rq); - runnable = calc_group_runnable(gcfs_rq, shares); #endif - reweight_entity(cfs_rq_of(se), se, shares, runnable); + reweight_entity(cfs_rq_of(se), se, shares); } #else /* CONFIG_FAIR_GROUP_SCHED */ @@ -3290,11 +3215,11 @@ void set_task_rq_fair(struct sched_entity *se, * _IFF_ we look at the pure running and runnable sums. Because they * represent the very same entity, just at different points in the hierarchy. * - * Per the above update_tg_cfs_util() is trivial and simply copies the running - * sum over (but still wrong, because the group entity and group rq do not have - * their PELT windows aligned). + * Per the above update_tg_cfs_util() is trivial * and simply copies the + * running sum over (but still wrong, because the group entity and group rq do + * not have their PELT windows aligned). * - * However, update_tg_cfs_runnable() is more complex. So we have: + * However, update_tg_cfs_load() is more complex. So we have: * * ge->avg.load_avg = ge->load.weight * ge->avg.runnable_avg (2) * @@ -3375,11 +3300,11 @@ update_tg_cfs_util(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq } static inline void -update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) +update_tg_cfs_load(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) { long delta_avg, running_sum, runnable_sum = gcfs_rq->prop_runnable_sum; - unsigned long runnable_load_avg, load_avg; - u64 runnable_load_sum, load_sum = 0; + unsigned long load_avg; + u64 load_sum = 0; s64 delta_sum; if (!runnable_sum) @@ -3427,20 +3352,6 @@ update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cf se->avg.load_avg = load_avg; add_positive(&cfs_rq->avg.load_avg, delta_avg); add_positive(&cfs_rq->avg.load_sum, delta_sum); - - runnable_load_sum = (s64)se_runnable(se) * runnable_sum; - runnable_load_avg = div_s64(runnable_load_sum, LOAD_AVG_MAX); - - if (se->on_rq) { - delta_sum = runnable_load_sum - - se_weight(se) * se->avg.runnable_load_sum; - delta_avg = runnable_load_avg - se->avg.runnable_load_avg; - add_positive(&cfs_rq->avg.runnable_load_avg, delta_avg); - add_positive(&cfs_rq->avg.runnable_load_sum, delta_sum); - } - - se->avg.runnable_load_sum = runnable_sum; - se->avg.runnable_load_avg = runnable_load_avg; } static inline void add_tg_cfs_propagate(struct cfs_rq *cfs_rq, long runnable_sum) @@ -3468,7 +3379,7 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) add_tg_cfs_propagate(cfs_rq, gcfs_rq->prop_runnable_sum); update_tg_cfs_util(cfs_rq, se, gcfs_rq); - update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); + update_tg_cfs_load(cfs_rq, se, gcfs_rq); trace_pelt_cfs_tp(cfs_rq); trace_pelt_se_tp(se); @@ -3612,8 +3523,6 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s div_u64(se->avg.load_avg * se->avg.load_sum, se_weight(se)); } - se->avg.runnable_load_sum = se->avg.load_sum; - enqueue_load_avg(cfs_rq, se); cfs_rq->avg.util_avg += se->avg.util_avg; cfs_rq->avg.util_sum += se->avg.util_sum; @@ -4074,14 +3983,12 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When enqueuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. - * - Add its load to cfs_rq->runnable_avg * - For group_entity, update its weight to reflect the new share of * its group cfs_rq * - Add its new weight to cfs_rq->load.weight */ update_load_avg(cfs_rq, se, UPDATE_TG | DO_ATTACH); update_cfs_group(se); - enqueue_runnable_load_avg(cfs_rq, se); account_entity_enqueue(cfs_rq, se); if (flags & ENQUEUE_WAKEUP) @@ -4158,13 +4065,11 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When dequeuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. - * - Subtract its load from the cfs_rq->runnable_avg. * - Subtract its previous weight from cfs_rq->load.weight. * - For group entity, update its weight to reflect the new share * of its group cfs_rq. */ update_load_avg(cfs_rq, se, UPDATE_TG); - dequeue_runnable_load_avg(cfs_rq, se); update_stats_dequeue(cfs_rq, se, flags); @@ -7649,9 +7554,6 @@ static inline bool cfs_rq_is_decayed(struct cfs_rq *cfs_rq) if (cfs_rq->avg.util_sum) return false; - if (cfs_rq->avg.runnable_load_sum) - return false; - return true; } diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index bd006b79b360..3eb0ed333dcb 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -108,7 +108,7 @@ static u32 __accumulate_pelt_segments(u64 periods, u32 d1, u32 d3) */ static __always_inline u32 accumulate_sum(u64 delta, struct sched_avg *sa, - unsigned long load, unsigned long runnable, int running) + unsigned long load, int running) { u32 contrib = (u32)delta; /* p == 0 -> delta < 1024 */ u64 periods; @@ -121,8 +121,6 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ if (periods) { sa->load_sum = decay_load(sa->load_sum, periods); - sa->runnable_load_sum = - decay_load(sa->runnable_load_sum, periods); sa->util_sum = decay_load((u64)(sa->util_sum), periods); /* @@ -148,8 +146,6 @@ accumulate_sum(u64 delta, struct sched_avg *sa, if (load) sa->load_sum += load * contrib; - if (runnable) - sa->runnable_load_sum += runnable * contrib; if (running) sa->util_sum += contrib << SCHED_CAPACITY_SHIFT; @@ -186,7 +182,7 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ static __always_inline int ___update_load_sum(u64 now, struct sched_avg *sa, - unsigned long load, unsigned long runnable, int running) + unsigned long load, int running) { u64 delta; @@ -222,7 +218,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Also see the comment in accumulate_sum(). */ if (!load) - runnable = running = 0; + running = 0; /* * Now we know we crossed measurement unit boundaries. The *_avg @@ -231,14 +227,14 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Step 1: accumulate *_sum since last_update_time. If we haven't * crossed period boundaries, finish. */ - if (!accumulate_sum(delta, sa, load, runnable, running)) + if (!accumulate_sum(delta, sa, load, running)) return 0; return 1; } static __always_inline void -___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runnable) +___update_load_avg(struct sched_avg *sa, unsigned long load) { u32 divider = LOAD_AVG_MAX - 1024 + sa->period_contrib; @@ -246,7 +242,6 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runna * Step 2: update *_avg. */ sa->load_avg = div_u64(load * sa->load_sum, divider); - sa->runnable_load_avg = div_u64(runnable * sa->runnable_load_sum, divider); WRITE_ONCE(sa->util_avg, sa->util_sum / divider); } @@ -254,17 +249,13 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runna * sched_entity: * * task: - * se_runnable() == se_weight() + * se_weight() = se->load.weight * * group: [ see update_cfs_group() ] * se_weight() = tg->weight * grq->load_avg / tg->load_avg - * se_runnable() = se_weight(se) * grq->runnable_load_avg / grq->load_avg * - * load_sum := runnable_sum - * load_avg = se_weight(se) * runnable_avg - * - * runnable_load_sum := runnable_sum - * runnable_load_avg = se_runnable(se) * runnable_avg + * load_sum := runnable + * load_avg = se_weight(se) * load_sum * * XXX collapse load_sum and runnable_load_sum * @@ -272,15 +263,12 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load, unsigned long runna * * load_sum = \Sum se_weight(se) * se->avg.load_sum * load_avg = \Sum se->avg.load_avg - * - * runnable_load_sum = \Sum se_runnable(se) * se->avg.runnable_load_sum - * runnable_load_avg = \Sum se->avg.runable_load_avg */ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, 0, 0, 0)) { - ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); + if (___update_load_sum(now, &se->avg, 0, 0)) { + ___update_load_avg(&se->avg, se_weight(se)); trace_pelt_se_tp(se); return 1; } @@ -290,10 +278,9 @@ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, !!se->on_rq, !!se->on_rq, - cfs_rq->curr == se)) { + if (___update_load_sum(now, &se->avg, !!se->on_rq, cfs_rq->curr == se)) { - ___update_load_avg(&se->avg, se_weight(se), se_runnable(se)); + ___update_load_avg(&se->avg, se_weight(se)); cfs_se_util_change(&se->avg); trace_pelt_se_tp(se); return 1; @@ -306,10 +293,9 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) { if (___update_load_sum(now, &cfs_rq->avg, scale_load_down(cfs_rq->load.weight), - scale_load_down(cfs_rq->runnable_weight), cfs_rq->curr != NULL)) { - ___update_load_avg(&cfs_rq->avg, 1, 1); + ___update_load_avg(&cfs_rq->avg, 1); trace_pelt_cfs_tp(cfs_rq); return 1; } @@ -322,20 +308,19 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) * * util_sum = \Sum se->avg.util_sum but se->avg.util_sum is not tracked * util_sum = cpu_scale * load_sum - * runnable_load_sum = load_sum + * runnable_sum = util_sum * - * load_avg and runnable_load_avg are not supported and meaningless. + * load_avg is not supported and meaningless. * */ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_rt, - running, running, running)) { - ___update_load_avg(&rq->avg_rt, 1, 1); + ___update_load_avg(&rq->avg_rt, 1); trace_pelt_rt_tp(rq); return 1; } @@ -348,18 +333,19 @@ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) * * util_sum = \Sum se->avg.util_sum but se->avg.util_sum is not tracked * util_sum = cpu_scale * load_sum - * runnable_load_sum = load_sum + * runnable_sum = util_sum + * + * load_avg is not supported and meaningless. * */ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_dl, - running, running, running)) { - ___update_load_avg(&rq->avg_dl, 1, 1); + ___update_load_avg(&rq->avg_dl, 1); trace_pelt_dl_tp(rq); return 1; } @@ -373,7 +359,9 @@ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) * * util_sum = \Sum se->avg.util_sum but se->avg.util_sum is not tracked * util_sum = cpu_scale * load_sum - * runnable_load_sum = load_sum + * runnable_sum = util_sum + * + * load_avg is not supported and meaningless. * */ @@ -401,16 +389,14 @@ int update_irq_load_avg(struct rq *rq, u64 running) * rq->clock += delta with delta >= running */ ret = ___update_load_sum(rq->clock - running, &rq->avg_irq, - 0, 0, 0); ret += ___update_load_sum(rq->clock, &rq->avg_irq, - 1, 1, 1); if (ret) { - ___update_load_avg(&rq->avg_irq, 1, 1); + ___update_load_avg(&rq->avg_irq, 1); trace_pelt_irq_tp(rq); } diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 12bf82d86156..ce27e588fa7c 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -489,7 +489,6 @@ struct cfs_bandwidth { }; /* CFS-related fields in a runqueue */ struct cfs_rq { struct load_weight load; - unsigned long runnable_weight; unsigned int nr_running; unsigned int h_nr_running; /* SCHED_{NORMAL,BATCH,IDLE} */ unsigned int idle_h_nr_running; /* SCHED_IDLE */ @@ -688,8 +687,10 @@ struct dl_rq { #ifdef CONFIG_FAIR_GROUP_SCHED /* An entity is a task if it doesn't "own" a runqueue */ #define entity_is_task(se) (!se->my_q) + #else #define entity_is_task(se) 1 + #endif #ifdef CONFIG_SMP @@ -701,10 +702,6 @@ static inline long se_weight(struct sched_entity *se) return scale_load_down(se->load.weight); } -static inline long se_runnable(struct sched_entity *se) -{ - return scale_load_down(se->runnable_weight); -} static inline bool sched_asym_prefer(int a, int b) { -- cgit v1.2.3 From 9f68395333ad7f5bfe2f83473fed363d4229f11c Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 24 Feb 2020 09:52:18 +0000 Subject: sched/pelt: Add a new runnable average signal Now that runnable_load_avg has been removed, we can replace it by a new signal that will highlight the runnable pressure on a cfs_rq. This signal track the waiting time of tasks on rq and can help to better define the state of rqs. At now, only util_avg is used to define the state of a rq: A rq with more that around 80% of utilization and more than 1 tasks is considered as overloaded. But the util_avg signal of a rq can become temporaly low after that a task migrated onto another rq which can bias the classification of the rq. When tasks compete for the same rq, their runnable average signal will be higher than util_avg as it will include the waiting time and we can use this signal to better classify cfs_rqs. The new runnable_avg will track the runnable time of a task which simply adds the waiting time to the running time. The runnable _avg of cfs_rq will be the /Sum of se's runnable_avg and the runnable_avg of group entity will follow the one of the rq similarly to util_avg. Signed-off-by: Vincent Guittot Signed-off-by: Mel Gorman Signed-off-by: Ingo Molnar Reviewed-by: "Dietmar Eggemann " Acked-by: Peter Zijlstra Cc: Juri Lelli Cc: Valentin Schneider Cc: Phil Auld Cc: Hillf Danton Link: https://lore.kernel.org/r/20200224095223.13361-9-mgorman@techsingularity.net --- include/linux/sched.h | 26 ++++++++++------- kernel/sched/debug.c | 9 ++++-- kernel/sched/fair.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++----- kernel/sched/pelt.c | 39 ++++++++++++++++++-------- kernel/sched/sched.h | 22 ++++++++++++++- 5 files changed, 142 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/sched.h b/include/linux/sched.h index 037eaffabc24..2e9199bf947b 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -356,28 +356,30 @@ struct util_est { } __attribute__((__aligned__(sizeof(u64)))); /* - * The load_avg/util_avg accumulates an infinite geometric series + * The load/runnable/util_avg accumulates an infinite geometric series * (see __update_load_avg_cfs_rq() in kernel/sched/pelt.c). * * [load_avg definition] * * load_avg = runnable% * scale_load_down(load) * - * where runnable% is the time ratio that a sched_entity is runnable. - * For cfs_rq, it is the aggregated load_avg of all runnable and - * blocked sched_entities. + * [runnable_avg definition] + * + * runnable_avg = runnable% * SCHED_CAPACITY_SCALE * * [util_avg definition] * * util_avg = running% * SCHED_CAPACITY_SCALE * - * where running% is the time ratio that a sched_entity is running on - * a CPU. For cfs_rq, it is the aggregated util_avg of all runnable - * and blocked sched_entities. + * where runnable% is the time ratio that a sched_entity is runnable and + * running% the time ratio that a sched_entity is running. + * + * For cfs_rq, they are the aggregated values of all runnable and blocked + * sched_entities. * - * load_avg and util_avg don't direcly factor frequency scaling and CPU - * capacity scaling. The scaling is done through the rq_clock_pelt that - * is used for computing those signals (see update_rq_clock_pelt()) + * The load/runnable/util_avg doesn't direcly factor frequency scaling and CPU + * capacity scaling. The scaling is done through the rq_clock_pelt that is used + * for computing those signals (see update_rq_clock_pelt()) * * N.B., the above ratios (runnable% and running%) themselves are in the * range of [0, 1]. To do fixed point arithmetics, we therefore scale them @@ -401,9 +403,11 @@ struct util_est { struct sched_avg { u64 last_update_time; u64 load_sum; + u64 runnable_sum; u32 util_sum; u32 period_contrib; unsigned long load_avg; + unsigned long runnable_avg; unsigned long util_avg; struct util_est util_est; } ____cacheline_aligned; @@ -467,6 +471,8 @@ struct sched_entity { struct cfs_rq *cfs_rq; /* rq "owned" by this entity/group: */ struct cfs_rq *my_q; + /* cached value of my_q->h_nr_running */ + unsigned long runnable_weight; #endif #ifdef CONFIG_SMP diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index cfecaad387c0..8331bc04aea2 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -405,6 +405,7 @@ static void print_cfs_group_stats(struct seq_file *m, int cpu, struct task_group #ifdef CONFIG_SMP P(se->avg.load_avg); P(se->avg.util_avg); + P(se->avg.runnable_avg); #endif #undef PN_SCHEDSTAT @@ -524,6 +525,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) #ifdef CONFIG_SMP SEQ_printf(m, " .%-30s: %lu\n", "load_avg", cfs_rq->avg.load_avg); + SEQ_printf(m, " .%-30s: %lu\n", "runnable_avg", + cfs_rq->avg.runnable_avg); SEQ_printf(m, " .%-30s: %lu\n", "util_avg", cfs_rq->avg.util_avg); SEQ_printf(m, " .%-30s: %u\n", "util_est_enqueued", @@ -532,8 +535,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) cfs_rq->removed.load_avg); SEQ_printf(m, " .%-30s: %ld\n", "removed.util_avg", cfs_rq->removed.util_avg); - SEQ_printf(m, " .%-30s: %ld\n", "removed.runnable_sum", - cfs_rq->removed.runnable_sum); + SEQ_printf(m, " .%-30s: %ld\n", "removed.runnable_avg", + cfs_rq->removed.runnable_avg); #ifdef CONFIG_FAIR_GROUP_SCHED SEQ_printf(m, " .%-30s: %lu\n", "tg_load_avg_contrib", cfs_rq->tg_load_avg_contrib); @@ -944,8 +947,10 @@ void proc_sched_show_task(struct task_struct *p, struct pid_namespace *ns, P(se.load.weight); #ifdef CONFIG_SMP P(se.avg.load_sum); + P(se.avg.runnable_sum); P(se.avg.util_sum); P(se.avg.load_avg); + P(se.avg.runnable_avg); P(se.avg.util_avg); P(se.avg.last_update_time); P(se.avg.util_est.ewma); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index b0fb3d6a6185..49b36d62cc35 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -794,6 +794,8 @@ void post_init_entity_util_avg(struct task_struct *p) } } + sa->runnable_avg = cpu_scale; + if (p->sched_class != &fair_sched_class) { /* * For !fair tasks do: @@ -3215,9 +3217,9 @@ void set_task_rq_fair(struct sched_entity *se, * _IFF_ we look at the pure running and runnable sums. Because they * represent the very same entity, just at different points in the hierarchy. * - * Per the above update_tg_cfs_util() is trivial * and simply copies the - * running sum over (but still wrong, because the group entity and group rq do - * not have their PELT windows aligned). + * Per the above update_tg_cfs_util() and update_tg_cfs_runnable() are trivial + * and simply copies the running/runnable sum over (but still wrong, because + * the group entity and group rq do not have their PELT windows aligned). * * However, update_tg_cfs_load() is more complex. So we have: * @@ -3299,6 +3301,32 @@ update_tg_cfs_util(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq cfs_rq->avg.util_sum = cfs_rq->avg.util_avg * LOAD_AVG_MAX; } +static inline void +update_tg_cfs_runnable(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) +{ + long delta = gcfs_rq->avg.runnable_avg - se->avg.runnable_avg; + + /* Nothing to update */ + if (!delta) + return; + + /* + * The relation between sum and avg is: + * + * LOAD_AVG_MAX - 1024 + sa->period_contrib + * + * however, the PELT windows are not aligned between grq and gse. + */ + + /* Set new sched_entity's runnable */ + se->avg.runnable_avg = gcfs_rq->avg.runnable_avg; + se->avg.runnable_sum = se->avg.runnable_avg * LOAD_AVG_MAX; + + /* Update parent cfs_rq runnable */ + add_positive(&cfs_rq->avg.runnable_avg, delta); + cfs_rq->avg.runnable_sum = cfs_rq->avg.runnable_avg * LOAD_AVG_MAX; +} + static inline void update_tg_cfs_load(struct cfs_rq *cfs_rq, struct sched_entity *se, struct cfs_rq *gcfs_rq) { @@ -3379,6 +3407,7 @@ static inline int propagate_entity_load_avg(struct sched_entity *se) add_tg_cfs_propagate(cfs_rq, gcfs_rq->prop_runnable_sum); update_tg_cfs_util(cfs_rq, se, gcfs_rq); + update_tg_cfs_runnable(cfs_rq, se, gcfs_rq); update_tg_cfs_load(cfs_rq, se, gcfs_rq); trace_pelt_cfs_tp(cfs_rq); @@ -3449,7 +3478,7 @@ static inline void add_tg_cfs_propagate(struct cfs_rq *cfs_rq, long runnable_sum static inline int update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) { - unsigned long removed_load = 0, removed_util = 0, removed_runnable_sum = 0; + unsigned long removed_load = 0, removed_util = 0, removed_runnable = 0; struct sched_avg *sa = &cfs_rq->avg; int decayed = 0; @@ -3460,7 +3489,7 @@ update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) raw_spin_lock(&cfs_rq->removed.lock); swap(cfs_rq->removed.util_avg, removed_util); swap(cfs_rq->removed.load_avg, removed_load); - swap(cfs_rq->removed.runnable_sum, removed_runnable_sum); + swap(cfs_rq->removed.runnable_avg, removed_runnable); cfs_rq->removed.nr = 0; raw_spin_unlock(&cfs_rq->removed.lock); @@ -3472,7 +3501,16 @@ update_cfs_rq_load_avg(u64 now, struct cfs_rq *cfs_rq) sub_positive(&sa->util_avg, r); sub_positive(&sa->util_sum, r * divider); - add_tg_cfs_propagate(cfs_rq, -(long)removed_runnable_sum); + r = removed_runnable; + sub_positive(&sa->runnable_avg, r); + sub_positive(&sa->runnable_sum, r * divider); + + /* + * removed_runnable is the unweighted version of removed_load so we + * can use it to estimate removed_load_sum. + */ + add_tg_cfs_propagate(cfs_rq, + -(long)(removed_runnable * divider) >> SCHED_CAPACITY_SHIFT); decayed = 1; } @@ -3517,6 +3555,8 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s */ se->avg.util_sum = se->avg.util_avg * divider; + se->avg.runnable_sum = se->avg.runnable_avg * divider; + se->avg.load_sum = divider; if (se_weight(se)) { se->avg.load_sum = @@ -3526,6 +3566,8 @@ static void attach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s enqueue_load_avg(cfs_rq, se); cfs_rq->avg.util_avg += se->avg.util_avg; cfs_rq->avg.util_sum += se->avg.util_sum; + cfs_rq->avg.runnable_avg += se->avg.runnable_avg; + cfs_rq->avg.runnable_sum += se->avg.runnable_sum; add_tg_cfs_propagate(cfs_rq, se->avg.load_sum); @@ -3547,6 +3589,8 @@ static void detach_entity_load_avg(struct cfs_rq *cfs_rq, struct sched_entity *s dequeue_load_avg(cfs_rq, se); sub_positive(&cfs_rq->avg.util_avg, se->avg.util_avg); sub_positive(&cfs_rq->avg.util_sum, se->avg.util_sum); + sub_positive(&cfs_rq->avg.runnable_avg, se->avg.runnable_avg); + sub_positive(&cfs_rq->avg.runnable_sum, se->avg.runnable_sum); add_tg_cfs_propagate(cfs_rq, -se->avg.load_sum); @@ -3653,10 +3697,15 @@ static void remove_entity_load_avg(struct sched_entity *se) ++cfs_rq->removed.nr; cfs_rq->removed.util_avg += se->avg.util_avg; cfs_rq->removed.load_avg += se->avg.load_avg; - cfs_rq->removed.runnable_sum += se->avg.load_sum; /* == runnable_sum */ + cfs_rq->removed.runnable_avg += se->avg.runnable_avg; raw_spin_unlock_irqrestore(&cfs_rq->removed.lock, flags); } +static inline unsigned long cfs_rq_runnable_avg(struct cfs_rq *cfs_rq) +{ + return cfs_rq->avg.runnable_avg; +} + static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq) { return cfs_rq->avg.load_avg; @@ -3983,11 +4032,13 @@ enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When enqueuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. + * - Add its load to cfs_rq->runnable_avg * - For group_entity, update its weight to reflect the new share of * its group cfs_rq * - Add its new weight to cfs_rq->load.weight */ update_load_avg(cfs_rq, se, UPDATE_TG | DO_ATTACH); + se_update_runnable(se); update_cfs_group(se); account_entity_enqueue(cfs_rq, se); @@ -4065,11 +4116,13 @@ dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int flags) /* * When dequeuing a sched_entity, we must: * - Update loads to have both entity and cfs_rq synced with now. + * - Subtract its load from the cfs_rq->runnable_avg. * - Subtract its previous weight from cfs_rq->load.weight. * - For group entity, update its weight to reflect the new share * of its group cfs_rq. */ update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); update_stats_dequeue(cfs_rq, se, flags); @@ -5240,6 +5293,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags) goto enqueue_throttle; update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); update_cfs_group(se); cfs_rq->h_nr_running++; @@ -5337,6 +5391,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags) goto dequeue_throttle; update_load_avg(cfs_rq, se, UPDATE_TG); + se_update_runnable(se); update_cfs_group(se); cfs_rq->h_nr_running--; @@ -5409,6 +5464,11 @@ static unsigned long cpu_load_without(struct rq *rq, struct task_struct *p) return load; } +static unsigned long cpu_runnable(struct rq *rq) +{ + return cfs_rq_runnable_avg(&rq->cfs); +} + static unsigned long capacity_of(int cpu) { return cpu_rq(cpu)->cpu_capacity; @@ -7554,6 +7614,9 @@ static inline bool cfs_rq_is_decayed(struct cfs_rq *cfs_rq) if (cfs_rq->avg.util_sum) return false; + if (cfs_rq->avg.runnable_sum) + return false; + return true; } diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index 3eb0ed333dcb..c40d57a2a248 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -108,7 +108,7 @@ static u32 __accumulate_pelt_segments(u64 periods, u32 d1, u32 d3) */ static __always_inline u32 accumulate_sum(u64 delta, struct sched_avg *sa, - unsigned long load, int running) + unsigned long load, unsigned long runnable, int running) { u32 contrib = (u32)delta; /* p == 0 -> delta < 1024 */ u64 periods; @@ -121,6 +121,8 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ if (periods) { sa->load_sum = decay_load(sa->load_sum, periods); + sa->runnable_sum = + decay_load(sa->runnable_sum, periods); sa->util_sum = decay_load((u64)(sa->util_sum), periods); /* @@ -146,6 +148,8 @@ accumulate_sum(u64 delta, struct sched_avg *sa, if (load) sa->load_sum += load * contrib; + if (runnable) + sa->runnable_sum += runnable * contrib << SCHED_CAPACITY_SHIFT; if (running) sa->util_sum += contrib << SCHED_CAPACITY_SHIFT; @@ -182,7 +186,7 @@ accumulate_sum(u64 delta, struct sched_avg *sa, */ static __always_inline int ___update_load_sum(u64 now, struct sched_avg *sa, - unsigned long load, int running) + unsigned long load, unsigned long runnable, int running) { u64 delta; @@ -218,7 +222,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Also see the comment in accumulate_sum(). */ if (!load) - running = 0; + runnable = running = 0; /* * Now we know we crossed measurement unit boundaries. The *_avg @@ -227,7 +231,7 @@ ___update_load_sum(u64 now, struct sched_avg *sa, * Step 1: accumulate *_sum since last_update_time. If we haven't * crossed period boundaries, finish. */ - if (!accumulate_sum(delta, sa, load, running)) + if (!accumulate_sum(delta, sa, load, runnable, running)) return 0; return 1; @@ -242,6 +246,7 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load) * Step 2: update *_avg. */ sa->load_avg = div_u64(load * sa->load_sum, divider); + sa->runnable_avg = div_u64(sa->runnable_sum, divider); WRITE_ONCE(sa->util_avg, sa->util_sum / divider); } @@ -250,24 +255,30 @@ ___update_load_avg(struct sched_avg *sa, unsigned long load) * * task: * se_weight() = se->load.weight + * se_runnable() = !!on_rq * * group: [ see update_cfs_group() ] * se_weight() = tg->weight * grq->load_avg / tg->load_avg + * se_runnable() = grq->h_nr_running + * + * runnable_sum = se_runnable() * runnable = grq->runnable_sum + * runnable_avg = runnable_sum * * load_sum := runnable * load_avg = se_weight(se) * load_sum * - * XXX collapse load_sum and runnable_load_sum - * * cfq_rq: * + * runnable_sum = \Sum se->avg.runnable_sum + * runnable_avg = \Sum se->avg.runnable_avg + * * load_sum = \Sum se_weight(se) * se->avg.load_sum * load_avg = \Sum se->avg.load_avg */ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, 0, 0)) { + if (___update_load_sum(now, &se->avg, 0, 0, 0)) { ___update_load_avg(&se->avg, se_weight(se)); trace_pelt_se_tp(se); return 1; @@ -278,7 +289,8 @@ int __update_load_avg_blocked_se(u64 now, struct sched_entity *se) int __update_load_avg_se(u64 now, struct cfs_rq *cfs_rq, struct sched_entity *se) { - if (___update_load_sum(now, &se->avg, !!se->on_rq, cfs_rq->curr == se)) { + if (___update_load_sum(now, &se->avg, !!se->on_rq, se_runnable(se), + cfs_rq->curr == se)) { ___update_load_avg(&se->avg, se_weight(se)); cfs_se_util_change(&se->avg); @@ -293,6 +305,7 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) { if (___update_load_sum(now, &cfs_rq->avg, scale_load_down(cfs_rq->load.weight), + cfs_rq->h_nr_running, cfs_rq->curr != NULL)) { ___update_load_avg(&cfs_rq->avg, 1); @@ -310,13 +323,14 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq) * util_sum = cpu_scale * load_sum * runnable_sum = util_sum * - * load_avg is not supported and meaningless. + * load_avg and runnable_avg are not supported and meaningless. * */ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_rt, + running, running, running)) { @@ -335,13 +349,14 @@ int update_rt_rq_load_avg(u64 now, struct rq *rq, int running) * util_sum = cpu_scale * load_sum * runnable_sum = util_sum * - * load_avg is not supported and meaningless. + * load_avg and runnable_avg are not supported and meaningless. * */ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) { if (___update_load_sum(now, &rq->avg_dl, + running, running, running)) { @@ -361,7 +376,7 @@ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) * util_sum = cpu_scale * load_sum * runnable_sum = util_sum * - * load_avg is not supported and meaningless. + * load_avg and runnable_avg are not supported and meaningless. * */ @@ -389,9 +404,11 @@ int update_irq_load_avg(struct rq *rq, u64 running) * rq->clock += delta with delta >= running */ ret = ___update_load_sum(rq->clock - running, &rq->avg_irq, + 0, 0, 0); ret += ___update_load_sum(rq->clock, &rq->avg_irq, + 1, 1, 1); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index ce27e588fa7c..2a0caf394dd4 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -527,7 +527,7 @@ struct cfs_rq { int nr; unsigned long load_avg; unsigned long util_avg; - unsigned long runnable_sum; + unsigned long runnable_avg; } removed; #ifdef CONFIG_FAIR_GROUP_SCHED @@ -688,9 +688,29 @@ struct dl_rq { /* An entity is a task if it doesn't "own" a runqueue */ #define entity_is_task(se) (!se->my_q) +static inline void se_update_runnable(struct sched_entity *se) +{ + if (!entity_is_task(se)) + se->runnable_weight = se->my_q->h_nr_running; +} + +static inline long se_runnable(struct sched_entity *se) +{ + if (entity_is_task(se)) + return !!se->on_rq; + else + return se->runnable_weight; +} + #else #define entity_is_task(se) 1 +static inline void se_update_runnable(struct sched_entity *se) {} + +static inline long se_runnable(struct sched_entity *se) +{ + return !!se->on_rq; +} #endif #ifdef CONFIG_SMP -- cgit v1.2.3 From 3710a8a6284f58a78ba4fe9c4b6672207636a223 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 24 Feb 2020 11:34:25 +0100 Subject: nl80211: modify TID-config API Make some changes to the TID-config API: * use u16 in nl80211 (only, and restrict to using 8 bits for now), to avoid issues in the future if we ever want to use higher TIDs. * reject empty TIDs mask (via netlink policy) * change feature advertising to not use extended feature flags but have own mechanism for this, which simplifies the code * fix all variable names from 'tid' to 'tids' since it's a mask * change to cfg80211_ name prefixes, not ieee80211_ * fix some minor docs/spelling things. Change-Id: Ia234d464b3f914cdeab82f540e018855be580dce Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 43 +++++++++++-------- include/uapi/linux/nl80211.h | 51 +++++++++++++---------- net/wireless/nl80211.c | 99 +++++++++++++++++++++++++------------------- net/wireless/rdev-ops.h | 8 ++-- net/wireless/trace.h | 14 +++---- 5 files changed, 121 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 65a8bf73d21e..bbe4acef729d 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -626,36 +626,32 @@ struct cfg80211_chan_def { struct ieee80211_edmg edmg; }; -enum ieee80211_tid_conf_mask { - IEEE80211_TID_CONF_NOACK = BIT(NL80211_TID_CONFIG_ATTR_NOACK), -}; - /** - * struct ieee80211_tid_cfg - TID specific configuration + * struct cfg80211_tid_cfg - TID specific configuration * @config_override: Flag to notify driver to reset TID configuration * of the peer. - * @tid: TID number - * @tid_conf_mask: bitmap indicating which parameter changed - * see &enum ieee80211_tid_conf_mask + * @tids: bitmap of TIDs to modify + * @mask: bitmap of attributes indicating which parameter changed, + * similar to &nl80211_tid_config_supp. * @noack: noack configuration value for the TID */ -struct ieee80211_tid_cfg { +struct cfg80211_tid_cfg { bool config_override; - u8 tid; - u32 tid_conf_mask; + u8 tids; + u32 mask; enum nl80211_tid_config noack; }; /** - * struct ieee80211_tid_config - TID configuration + * struct cfg80211_tid_config - TID configuration * @peer: Station's MAC address * @n_tid_conf: Number of TID specific configurations to be applied * @tid_conf: Configuration change info */ -struct ieee80211_tid_config { +struct cfg80211_tid_config { const u8 *peer; u32 n_tid_conf; - struct ieee80211_tid_cfg tid_conf[]; + struct cfg80211_tid_cfg tid_conf[]; }; /** @@ -3705,8 +3701,8 @@ struct cfg80211_update_owe_info { * and overrule HWMP path selection algorithm. * @set_tid_config: TID specific configuration, this can be peer or BSS specific * This callback may sleep. - * @reset_tid_config: Reset TID specific configuration for the peer. - * This callback may sleep. + * @reset_tid_config: Reset TID specific configuration for the peer, for the + * given TIDs. This callback may sleep. */ struct cfg80211_ops { int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow); @@ -4031,9 +4027,9 @@ struct cfg80211_ops { int (*probe_mesh_link)(struct wiphy *wiphy, struct net_device *dev, const u8 *buf, size_t len); int (*set_tid_config)(struct wiphy *wiphy, struct net_device *dev, - struct ieee80211_tid_config *tid_conf); + struct cfg80211_tid_config *tid_conf); int (*reset_tid_config)(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u8 tid); + const u8 *peer, u8 tids); }; /* @@ -4641,6 +4637,13 @@ struct wiphy_iftype_akm_suites { * @support_mbssid must be set for this to have any effect. * * @pmsr_capa: peer measurement capabilities + * + * @tid_config_support: describes the per-TID config support that the + * device has + * @tid_config_support.vif: bitmap of attributes (configurations) + * supported by the driver for each vif + * @tid_config_support.peer: bitmap of attributes (configurations) + * supported by the driver for each peer */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -4772,6 +4775,10 @@ struct wiphy { const struct cfg80211_pmsr_capabilities *pmsr_capa; + struct { + u64 peer, vif; + } tid_config_support; + char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 0b12fcffb518..591d843eda72 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -267,20 +267,22 @@ /** * DOC: TID configuration * - * TID configuration support can be advertised by drivers by setting - * @NL80211_EXT_FEATURE_PER_TID_* and/or @NL80211_EXT_FEATURE_PER_STA_* config - * mentioned in &enum nl80211_tid_config_attr. - * Needed configuration parameters are mentioned in - * &enum nl80211_tid_config_attr and it will be passed using - * %NL80211_CMD_SET_TID_CONFIG through %NL80211_ATTR_TID_CONFIG. - * If the configuration needs to be applied for specific peer then MAC address - * of the peer needs to be passed in %NL80211_ATT_MAC, otherwise the + * TID config support can be checked in the %NL80211_ATTR_TID_CONFIG + * attribute given in wiphy capabilities. + * + * The necessary configuration parameters are mentioned in + * &enum nl80211_tid_config_attr and it will be passed to the + * %NL80211_CMD_SET_TID_CONFIG command in %NL80211_ATTR_TID_CONFIG. + * + * If the configuration needs to be applied for specific peer then the MAC + * address of the peer needs to be passed in %NL80211_ATTR_MAC, otherwise the * configuration will be applied for all the connected peers in the vif except - * the peer which has peer specific configuration for the TID. - * And the peer specific configuration will be overridden if - * %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set. - * All this configurations are valid only for STA's current connection - * i.e. the configurations will be reset to default when the STA connects back + * any peers that have peer specific configuration for the TID by default; if + * the %NL80211_TID_CONFIG_ATTR_OVERRIDE flag is set, peer specific values + * will be overwritten. + * + * All this configuration is valid only for STA's current connection + * i.e. the configuration will be reset to default when the STA connects back * after disconnection/roaming, and this configuration will be cleared when * the interface goes down. */ @@ -2436,7 +2438,9 @@ enum nl80211_commands { * advertised for a specific interface type. * * @NL80211_ATTR_TID_CONFIG: TID specific configuration in a - * nested attribute with &enum nl80211_tid_config_attr sub-attributes. + * nested attribute with &enum nl80211_tid_config_attr sub-attributes; + * on output (in wiphy attributes) it contains only the feature sub- + * attributes. * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined @@ -4764,20 +4768,29 @@ enum nl80211_tid_config { }; /* enum nl80211_tid_config_attr - TID specific configuration. + * @NL80211_TID_CONFIG_ATTR_PAD: pad attribute for 64-bit values + * @NL80211_TID_CONFIG_ATTR_VIF_SUPP: a bitmap (u64) of attributes supported + * for per-vif configuration; doesn't list the ones that are generic + * (%NL80211_TID_CONFIG_ATTR_TIDS, %NL80211_TID_CONFIG_ATTR_OVERRIDE). + * @NL80211_TID_CONFIG_ATTR_PEER_SUPP: same as the previous per-vif one, but + * per peer instead. * @NL80211_TID_CONFIG_ATTR_OVERRIDE: flag attribue, if no peer * is selected, if set indicates that the new configuration overrides * all previous peer configurations, otherwise previous peer specific * configurations should be left untouched. If peer is selected then * it will reset particular TID configuration of that peer and it will * not accept other TID config attributes along with peer. - * @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs(bit 0 to 7) - * Its type is u8. + * @NL80211_TID_CONFIG_ATTR_TIDS: a bitmask value of TIDs (bit 0 to 7) + * Its type is u16. * @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID. * specified in %NL80211_TID_CONFIG_ATTR_TID. see %enum nl80211_tid_config. * Its type is u8. */ enum nl80211_tid_config_attr { __NL80211_TID_CONFIG_ATTR_INVALID, + NL80211_TID_CONFIG_ATTR_PAD, + NL80211_TID_CONFIG_ATTR_VIF_SUPP, + NL80211_TID_CONFIG_ATTR_PEER_SUPP, NL80211_TID_CONFIG_ATTR_OVERRIDE, NL80211_TID_CONFIG_ATTR_TIDS, NL80211_TID_CONFIG_ATTR_NOACK, @@ -5605,10 +5618,6 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_AQL: The driver supports the Airtime Queue Limit (AQL) * feature, which prevents bufferbloat by using the expected transmission * time to limit the amount of data buffered in the hardware. - * @NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG: Driver supports per TID NoAck - * policy functionality. - * @NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG: Driver supports STA specific NoAck - * policy functionality. * * @NL80211_EXT_FEATURE_BEACON_PROTECTION: The driver supports Beacon protection * and can receive key configuration for BIGTK using key indexes 6 and 7. @@ -5661,8 +5670,6 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_VLAN_OFFLOAD, NL80211_EXT_FEATURE_AQL, NL80211_EXT_FEATURE_BEACON_PROTECTION, - NL80211_EXT_FEATURE_PER_TID_NOACK_CONFIG, - NL80211_EXT_FEATURE_PER_STA_NOACK_CONFIG, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a0839fae6555..56ac851ccee1 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -330,8 +330,10 @@ he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = { static const struct nla_policy nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { + [NL80211_TID_CONFIG_ATTR_VIF_SUPP] = { .type = NLA_U64 }, + [NL80211_TID_CONFIG_ATTR_PEER_SUPP] = { .type = NLA_U64 }, [NL80211_TID_CONFIG_ATTR_OVERRIDE] = { .type = NLA_FLAG }, - [NL80211_TID_CONFIG_ATTR_TIDS] = { .type = NLA_U8 }, + [NL80211_TID_CONFIG_ATTR_TIDS] = NLA_POLICY_RANGE(NLA_U16, 1, 0xff), [NL80211_TID_CONFIG_ATTR_NOACK] = NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), }; @@ -1957,6 +1959,40 @@ nl80211_put_iftype_akm_suites(struct cfg80211_registered_device *rdev, return 0; } +static int +nl80211_put_tid_config_support(struct cfg80211_registered_device *rdev, + struct sk_buff *msg) +{ + struct nlattr *supp; + + if (!rdev->wiphy.tid_config_support.vif && + !rdev->wiphy.tid_config_support.peer) + return 0; + + supp = nla_nest_start(msg, NL80211_ATTR_TID_CONFIG); + if (!supp) + return -ENOSPC; + + if (rdev->wiphy.tid_config_support.vif && + nla_put_u64_64bit(msg, NL80211_TID_CONFIG_ATTR_VIF_SUPP, + rdev->wiphy.tid_config_support.vif, + NL80211_TID_CONFIG_ATTR_PAD)) + goto fail; + + if (rdev->wiphy.tid_config_support.peer && + nla_put_u64_64bit(msg, NL80211_TID_CONFIG_ATTR_PEER_SUPP, + rdev->wiphy.tid_config_support.peer, + NL80211_TID_CONFIG_ATTR_PAD)) + goto fail; + + nla_nest_end(msg, supp); + + return 0; +fail: + nla_nest_cancel(msg, supp); + return -ENOBUFS; +} + struct nl80211_dump_wiphy_state { s64 filter_wiphy; long start; @@ -2518,6 +2554,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, if (nl80211_put_iftype_akm_suites(rdev, msg)) goto nla_put_failure; + if (nl80211_put_tid_config_support(rdev, msg)) + goto nla_put_failure; + /* done */ state->split_start = 0; break; @@ -13944,44 +13983,13 @@ static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info) return rdev_probe_mesh_link(rdev, dev, dest, buf, len); } -static int -__nl80211_check_tid_conf_support(struct cfg80211_registered_device *rdev, - struct netlink_ext_ack *extack, - const u8 *peer, struct nlattr *attrs[], - struct ieee80211_tid_cfg *tid_conf, - enum nl80211_tid_config_attr attr, - enum nl80211_ext_feature_index per_tid_config, - enum nl80211_ext_feature_index per_sta_config) -{ - if (!wiphy_ext_feature_isset(&rdev->wiphy, per_tid_config)) { - NL_SET_ERR_MSG_ATTR(extack, attrs[attr], - "TID specific configuration not supported"); - return -ENOTSUPP; - } - - if (peer && !wiphy_ext_feature_isset(&rdev->wiphy, per_sta_config)) { - NL_SET_ERR_MSG_ATTR(extack, attrs[attr], - "peer specific TID configuration not supported"); - return -ENOTSUPP; - } - - tid_conf->tid_conf_mask |= BIT(attr); - return 0; -} - -#define nl80211_check_tid_config_support(rdev, extack, peer, attrs, tid_conf, \ - conf) \ - __nl80211_check_tid_conf_support(rdev, extack, peer, attrs, tid_conf, \ - NL80211_TID_CONFIG_ATTR_##conf, \ - NL80211_EXT_FEATURE_PER_TID_##conf##_CONFIG, \ - NL80211_EXT_FEATURE_PER_STA_##conf##_CONFIG) - static int parse_tid_conf(struct cfg80211_registered_device *rdev, struct nlattr *attrs[], struct net_device *dev, - struct ieee80211_tid_cfg *tid_conf, + struct cfg80211_tid_cfg *tid_conf, struct genl_info *info, const u8 *peer) { struct netlink_ext_ack *extack = info->extack; + u64 mask; int err; if (!attrs[NL80211_TID_CONFIG_ATTR_TIDS]) @@ -13989,12 +13997,12 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev, tid_conf->config_override = nla_get_flag(attrs[NL80211_TID_CONFIG_ATTR_OVERRIDE]); - tid_conf->tid = nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_TIDS]); + tid_conf->tids = nla_get_u16(attrs[NL80211_TID_CONFIG_ATTR_TIDS]); if (tid_conf->config_override) { if (rdev->ops->reset_tid_config) { err = rdev_reset_tid_config(rdev, dev, peer, - tid_conf->tid); + tid_conf->tids); /* If peer is there no other configuration will be * allowed */ @@ -14006,16 +14014,21 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev, } if (attrs[NL80211_TID_CONFIG_ATTR_NOACK]) { - err = nl80211_check_tid_config_support(rdev, extack, peer, - attrs, tid_conf, - NOACK); - if (err) - return err; - + tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_NOACK); tid_conf->noack = nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_NOACK]); } + if (peer) + mask = rdev->wiphy.tid_config_support.peer; + else + mask = rdev->wiphy.tid_config_support.vif; + + if (tid_conf->mask & ~mask) { + NL_SET_ERR_MSG(extack, "unsupported TID configuration"); + return -ENOTSUPP; + } + return 0; } @@ -14025,7 +14038,7 @@ static int nl80211_set_tid_config(struct sk_buff *skb, struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct nlattr *attrs[NL80211_TID_CONFIG_ATTR_MAX + 1]; struct net_device *dev = info->user_ptr[1]; - struct ieee80211_tid_config *tid_config; + struct cfg80211_tid_config *tid_config; struct nlattr *tid; int conf_idx = 0, rem_conf; int ret = -EINVAL; diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index a754e0496b6c..99462f0c4e08 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -1328,7 +1328,7 @@ rdev_probe_mesh_link(struct cfg80211_registered_device *rdev, static inline int rdev_set_tid_config(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct ieee80211_tid_config *tid_conf) + struct cfg80211_tid_config *tid_conf) { int ret; @@ -1340,12 +1340,12 @@ static inline int rdev_set_tid_config(struct cfg80211_registered_device *rdev, static inline int rdev_reset_tid_config(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *peer, - u8 tid) + u8 tids) { int ret; - trace_rdev_reset_tid_config(&rdev->wiphy, dev, peer, tid); - ret = rdev->ops->reset_tid_config(&rdev->wiphy, dev, peer, tid); + trace_rdev_reset_tid_config(&rdev->wiphy, dev, peer, tids); + ret = rdev->ops->reset_tid_config(&rdev->wiphy, dev, peer, tids); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 167b18896cd6..839df54cee21 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -3482,7 +3482,7 @@ TRACE_EVENT(rdev_probe_mesh_link, TRACE_EVENT(rdev_set_tid_config, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - struct ieee80211_tid_config *tid_conf), + struct cfg80211_tid_config *tid_conf), TP_ARGS(wiphy, netdev, tid_conf), TP_STRUCT__entry( WIPHY_ENTRY @@ -3500,22 +3500,22 @@ TRACE_EVENT(rdev_set_tid_config, TRACE_EVENT(rdev_reset_tid_config, TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, - const u8 *peer, u8 tid), - TP_ARGS(wiphy, netdev, peer, tid), + const u8 *peer, u8 tids), + TP_ARGS(wiphy, netdev, peer, tids), TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY MAC_ENTRY(peer) - __field(u8, tid) + __field(u8, tids) ), TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; MAC_ASSIGN(peer, peer); - __entry->tid = tid; + __entry->tids = tids; ), - TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", tid: %u", - WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tid) + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", tids: 0x%x", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tids) ); #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ -- cgit v1.2.3 From 6a21d16c4db08398c737e0ffd03e4eca7131ac78 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 20 Jan 2020 13:21:23 +0530 Subject: nl80211: Add support to configure TID specific retry configuration This patch adds support to configure per TID retry configuration through the NL80211_TID_CONFIG_ATTR_RETRY_SHORT and NL80211_TID_CONFIG_ATTR_RETRY_LONG attributes. This TID specific retry configuration will have more precedence than phy level configuration. Signed-off-by: Tamizh chelvam Link: https://lore.kernel.org/r/1579506687-18296-3-git-send-email-tamizhr@codeaurora.org [rebase completely on top of my previous API changes] Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 8 ++++++++ include/uapi/linux/nl80211.h | 12 ++++++++++++ net/wireless/nl80211.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index bbe4acef729d..98981d1a026b 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -634,12 +634,15 @@ struct cfg80211_chan_def { * @mask: bitmap of attributes indicating which parameter changed, * similar to &nl80211_tid_config_supp. * @noack: noack configuration value for the TID + * @retry_long: retry count value + * @retry_short: retry count value */ struct cfg80211_tid_cfg { bool config_override; u8 tids; u32 mask; enum nl80211_tid_config noack; + u8 retry_long, retry_short; }; /** @@ -4644,6 +4647,8 @@ struct wiphy_iftype_akm_suites { * supported by the driver for each vif * @tid_config_support.peer: bitmap of attributes (configurations) * supported by the driver for each peer + * @tid_config_support.max_retry: maximum supported retry count for + * long/short retry configuration */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -4777,8 +4782,11 @@ struct wiphy { struct { u64 peer, vif; + u8 max_retry; } tid_config_support; + u8 max_data_retry_count; + char priv[0] __aligned(NETDEV_ALIGN); }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 591d843eda72..c3481e1feebe 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4785,6 +4785,16 @@ enum nl80211_tid_config { * @NL80211_TID_CONFIG_ATTR_NOACK: Configure ack policy for the TID. * specified in %NL80211_TID_CONFIG_ATTR_TID. see %enum nl80211_tid_config. * Its type is u8. + * @NL80211_TID_CONFIG_ATTR_RETRY_SHORT: Number of retries used with data frame + * transmission, user-space sets this configuration in + * &NL80211_CMD_SET_TID_CONFIG. It is u8 type, min value is 1 and + * the max value is advertised by the driver in this attribute on + * output in wiphy capabilities. + * @NL80211_TID_CONFIG_ATTR_RETRY_LONG: Number of retries used with data frame + * transmission, user-space sets this configuration in + * &NL80211_CMD_SET_TID_CONFIG. Its type is u8, min value is 1 and + * the max value is advertised by the driver in this attribute on + * output in wiphy capabilities. */ enum nl80211_tid_config_attr { __NL80211_TID_CONFIG_ATTR_INVALID, @@ -4794,6 +4804,8 @@ enum nl80211_tid_config_attr { NL80211_TID_CONFIG_ATTR_OVERRIDE, NL80211_TID_CONFIG_ATTR_TIDS, NL80211_TID_CONFIG_ATTR_NOACK, + NL80211_TID_CONFIG_ATTR_RETRY_SHORT, + NL80211_TID_CONFIG_ATTR_RETRY_LONG, /* keep last */ __NL80211_TID_CONFIG_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 56ac851ccee1..4c79ba685992 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -336,6 +336,8 @@ nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { [NL80211_TID_CONFIG_ATTR_TIDS] = NLA_POLICY_RANGE(NLA_U16, 1, 0xff), [NL80211_TID_CONFIG_ATTR_NOACK] = NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), + [NL80211_TID_CONFIG_ATTR_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1), + [NL80211_TID_CONFIG_ATTR_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), }; const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { @@ -1985,6 +1987,14 @@ nl80211_put_tid_config_support(struct cfg80211_registered_device *rdev, NL80211_TID_CONFIG_ATTR_PAD)) goto fail; + /* for now we just use the same value ... makes more sense */ + if (nla_put_u8(msg, NL80211_TID_CONFIG_ATTR_RETRY_SHORT, + rdev->wiphy.tid_config_support.max_retry)) + goto fail; + if (nla_put_u8(msg, NL80211_TID_CONFIG_ATTR_RETRY_LONG, + rdev->wiphy.tid_config_support.max_retry)) + goto fail; + nla_nest_end(msg, supp); return 0; @@ -14019,6 +14029,24 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev, nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_NOACK]); } + if (attrs[NL80211_TID_CONFIG_ATTR_RETRY_SHORT]) { + tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RETRY_SHORT); + tid_conf->retry_short = + nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_RETRY_SHORT]); + + if (tid_conf->retry_short > rdev->wiphy.max_data_retry_count) + return -EINVAL; + } + + if (attrs[NL80211_TID_CONFIG_ATTR_RETRY_LONG]) { + tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG); + tid_conf->retry_long = + nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_RETRY_LONG]); + + if (tid_conf->retry_long > rdev->wiphy.max_data_retry_count) + return -EINVAL; + } + if (peer) mask = rdev->wiphy.tid_config_support.peer; else -- cgit v1.2.3 From ade274b23e41886091a7e105ab3de71baef112e7 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 20 Jan 2020 13:21:24 +0530 Subject: nl80211: Add support to configure TID specific AMPDU configuration This patch adds support to configure per TID AMPDU control configuration to enable/disable aggregation through the NL80211_TID_CONFIG_ATTR_AMPDU_CTRL attribute. Signed-off-by: Tamizh chelvam Link: https://lore.kernel.org/r/1579506687-18296-4-git-send-email-tamizhr@codeaurora.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 4 ++++ net/wireless/nl80211.c | 8 ++++++++ 3 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 98981d1a026b..78171ae01406 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -636,6 +636,7 @@ struct cfg80211_chan_def { * @noack: noack configuration value for the TID * @retry_long: retry count value * @retry_short: retry count value + * @ampdu: Enable/Disable aggregation */ struct cfg80211_tid_cfg { bool config_override; @@ -643,6 +644,7 @@ struct cfg80211_tid_cfg { u32 mask; enum nl80211_tid_config noack; u8 retry_long, retry_short; + enum nl80211_tid_config ampdu; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index c3481e1feebe..71cae33e6679 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4795,6 +4795,9 @@ enum nl80211_tid_config { * &NL80211_CMD_SET_TID_CONFIG. Its type is u8, min value is 1 and * the max value is advertised by the driver in this attribute on * output in wiphy capabilities. + * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable aggregation for the TIDs + * specified in %NL80211_TID_CONFIG_ATTR_TIDS. Its type is u8, using + * the values from &nl80211_tid_config. */ enum nl80211_tid_config_attr { __NL80211_TID_CONFIG_ATTR_INVALID, @@ -4806,6 +4809,7 @@ enum nl80211_tid_config_attr { NL80211_TID_CONFIG_ATTR_NOACK, NL80211_TID_CONFIG_ATTR_RETRY_SHORT, NL80211_TID_CONFIG_ATTR_RETRY_LONG, + NL80211_TID_CONFIG_ATTR_AMPDU_CTRL, /* keep last */ __NL80211_TID_CONFIG_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4c79ba685992..078d30756b3e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -338,6 +338,8 @@ nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), [NL80211_TID_CONFIG_ATTR_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1), [NL80211_TID_CONFIG_ATTR_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), + [NL80211_TID_CONFIG_ATTR_AMPDU_CTRL] = + NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), }; const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { @@ -14047,6 +14049,12 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev, return -EINVAL; } + if (attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]) { + tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL); + tid_conf->ampdu = + nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]); + } + if (peer) mask = rdev->wiphy.tid_config_support.peer; else -- cgit v1.2.3 From 04f7d142f51c6019a695cfd70c09bb60233472c5 Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 20 Jan 2020 13:21:25 +0530 Subject: nl80211: Add support to configure TID specific RTSCTS configuration This patch adds support to configure per TID RTSCTS control configuration to enable/disable through the NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL attribute. Signed-off-by: Tamizh chelvam Link: https://lore.kernel.org/r/1579506687-18296-5-git-send-email-tamizhr@codeaurora.org Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 2 ++ include/uapi/linux/nl80211.h | 4 ++++ net/wireless/nl80211.c | 8 ++++++++ 3 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 78171ae01406..f7c84c32ba39 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -637,6 +637,7 @@ struct cfg80211_chan_def { * @retry_long: retry count value * @retry_short: retry count value * @ampdu: Enable/Disable aggregation + * @rtscts: Enable/Disable RTS/CTS */ struct cfg80211_tid_cfg { bool config_override; @@ -645,6 +646,7 @@ struct cfg80211_tid_cfg { enum nl80211_tid_config noack; u8 retry_long, retry_short; enum nl80211_tid_config ampdu; + enum nl80211_tid_config rtscts; }; /** diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 71cae33e6679..b002ef2060fa 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4798,6 +4798,9 @@ enum nl80211_tid_config { * @NL80211_TID_CONFIG_ATTR_AMPDU_CTRL: Enable/Disable aggregation for the TIDs * specified in %NL80211_TID_CONFIG_ATTR_TIDS. Its type is u8, using * the values from &nl80211_tid_config. + * @NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL: Enable/Disable RTS_CTS for the TIDs + * specified in %NL80211_TID_CONFIG_ATTR_TIDS. It is u8 type, using + * the values from &nl80211_tid_config. */ enum nl80211_tid_config_attr { __NL80211_TID_CONFIG_ATTR_INVALID, @@ -4810,6 +4813,7 @@ enum nl80211_tid_config_attr { NL80211_TID_CONFIG_ATTR_RETRY_SHORT, NL80211_TID_CONFIG_ATTR_RETRY_LONG, NL80211_TID_CONFIG_ATTR_AMPDU_CTRL, + NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL, /* keep last */ __NL80211_TID_CONFIG_ATTR_AFTER_LAST, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 078d30756b3e..ae5e10fe1196 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -340,6 +340,8 @@ nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { [NL80211_TID_CONFIG_ATTR_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), [NL80211_TID_CONFIG_ATTR_AMPDU_CTRL] = NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), + [NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL] = + NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), }; const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { @@ -14055,6 +14057,12 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev, nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]); } + if (attrs[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL]) { + tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL); + tid_conf->rtscts = + nla_get_u8(attrs[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL]); + } + if (peer) mask = rdev->wiphy.tid_config_support.peer; else -- cgit v1.2.3 From 370f51d5edac83bfdb9a078d7098f06403dfa4bc Mon Sep 17 00:00:00 2001 From: Tamizh chelvam Date: Mon, 20 Jan 2020 13:21:27 +0530 Subject: mac80211: Add api to support configuring TID specific configuration Implement drv_set_tid_config api to allow TID specific configuration and drv_reset_tid_config api to reset peer specific TID configuration. This per-TID onfiguration will be applied for all the connected stations when MAC is NULL. Signed-off-by: Tamizh chelvam Link: https://lore.kernel.org/r/1579506687-18296-7-git-send-email-tamizhr@codeaurora.org Signed-off-by: Johannes Berg --- include/net/mac80211.h | 10 +++++++++ net/mac80211/cfg.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++ net/mac80211/driver-ops.h | 27 +++++++++++++++++++++++ 3 files changed, 93 insertions(+) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index ce80773dfeb2..5ef34a2ba3c7 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -3776,6 +3776,9 @@ enum ieee80211_reconfig_type { * * @start_pmsr: start peer measurement (e.g. FTM) (this call can sleep) * @abort_pmsr: abort peer measurement (this call can sleep) + * @set_tid_config: Apply TID specific configurations. This callback may sleep. + * @reset_tid_config: Reset TID specific configuration for the peer. + * This callback may sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -4080,6 +4083,13 @@ struct ieee80211_ops { struct cfg80211_pmsr_request *request); void (*abort_pmsr)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_pmsr_request *request); + int (*set_tid_config)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct cfg80211_tid_config *tid_conf); + int (*reset_tid_config)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u8 tids); }; /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index a762ba6e4c5d..7b654d2b8bb2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3886,6 +3886,60 @@ ieee80211_abort_pmsr(struct wiphy *wiphy, struct wireless_dev *dev, return drv_abort_pmsr(local, sdata, request); } +static int ieee80211_set_tid_config(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_tid_config *tid_conf) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + int ret; + + if (!sdata->local->ops->set_tid_config) + return -EOPNOTSUPP; + + if (!tid_conf->peer) + return drv_set_tid_config(sdata->local, sdata, NULL, tid_conf); + + mutex_lock(&sdata->local->sta_mtx); + sta = sta_info_get_bss(sdata, tid_conf->peer); + if (!sta) { + mutex_unlock(&sdata->local->sta_mtx); + return -ENOENT; + } + + ret = drv_set_tid_config(sdata->local, sdata, &sta->sta, tid_conf); + mutex_unlock(&sdata->local->sta_mtx); + + return ret; +} + +static int ieee80211_reset_tid_config(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, u8 tid) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta; + int ret; + + if (!sdata->local->ops->reset_tid_config) + return -EOPNOTSUPP; + + if (!peer) + return drv_reset_tid_config(sdata->local, sdata, NULL, tid); + + mutex_lock(&sdata->local->sta_mtx); + sta = sta_info_get_bss(sdata, peer); + if (!sta) { + mutex_unlock(&sdata->local->sta_mtx); + return -ENOENT; + } + + ret = drv_reset_tid_config(sdata->local, sdata, &sta->sta, tid); + mutex_unlock(&sdata->local->sta_mtx); + + return ret; +} + const struct cfg80211_ops mac80211_config_ops = { .add_virtual_intf = ieee80211_add_iface, .del_virtual_intf = ieee80211_del_iface, @@ -3986,4 +4040,6 @@ const struct cfg80211_ops mac80211_config_ops = { .start_pmsr = ieee80211_start_pmsr, .abort_pmsr = ieee80211_abort_pmsr, .probe_mesh_link = ieee80211_probe_mesh_link, + .set_tid_config = ieee80211_set_tid_config, + .reset_tid_config = ieee80211_reset_tid_config, }; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 2c9b3eb8b652..3877710e3b48 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1358,4 +1358,31 @@ static inline void drv_del_nan_func(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline int drv_set_tid_config(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + struct cfg80211_tid_config *tid_conf) +{ + int ret; + + might_sleep(); + ret = local->ops->set_tid_config(&local->hw, &sdata->vif, sta, + tid_conf); + trace_drv_return_int(local, ret); + + return ret; +} + +static inline int drv_reset_tid_config(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, u8 tid) +{ + int ret; + + might_sleep(); + ret = local->ops->reset_tid_config(&local->hw, &sdata->vif, sta, tid); + trace_drv_return_int(local, ret); + + return ret; +} #endif /* __MAC80211_DRIVER_OPS */ -- cgit v1.2.3 From 4e52889f48fedfa5f2e443dc8490550cb49fbdc3 Mon Sep 17 00:00:00 2001 From: Eugen Hristev Date: Mon, 13 Jan 2020 10:48:53 +0100 Subject: media: atmel: atmel-isc-base: expose white balance as v4l2 controls This exposes the white balance configuration of the ISC as v4l2 controls into userspace. There are 8 controls available: 4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB. These gains are multipliers for each component, in format unsigned 0:4:9 with a default value of 512 (1.0 multiplier). 4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB. These offsets are added/substracted from each component, in format signed 1:12:0 with a default value of 0 (+/- 0) To expose this to userspace, added 8 custom controls, in an auto cluster. To summarize the functionality: The auto cluster switch is the auto white balance control, and it works like this: AWB == 1: autowhitebalance is on, the do_white_balance button is inactive, the gains/offsets are inactive, but volatile and readable. Thus, the results of the whitebalance algorithm are available to userspace to read at any time. AWB == 0: autowhitebalance is off, cluster is in manual mode, user can configure the gain/offsets directly. More than that, if the do_white_balance button is pressed, the driver will perform one-time-adjustment, (preferably with color checker card) and the userspace can read again the new values. With this feature, the userspace can save the coefficients and reinstall them for example after reboot or reprobing the driver. [hverkuil: fix checkpatch warning] [hverkuil: minor spacing adjustments in the functionality description] Signed-off-by: Eugen Hristev Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/atmel/atmel-isc-base.c | 222 ++++++++++++++++++++++---- drivers/media/platform/atmel/atmel-isc.h | 23 ++- include/linux/atmel-isc-media.h | 58 +++++++ include/uapi/linux/v4l2-controls.h | 6 + 4 files changed, 281 insertions(+), 28 deletions(-) create mode 100644 include/linux/atmel-isc-media.h (limited to 'include') diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c index d7669a03e98e..b6c9a78c9198 100644 --- a/drivers/media/platform/atmel/atmel-isc-base.c +++ b/drivers/media/platform/atmel/atmel-isc-base.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -224,10 +225,35 @@ const u32 isc_gamma_table[GAMMA_MAX + 1][GAMMA_ENTRIES] = { (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) +#define ISC_CTRL_ISC_TO_V4L2(x) ((x) == ISC_WB_O_ZERO_VAL ? 0 : (x)) +#define ISC_CTRL_V4L2_TO_ISC(x) ((x) ? (x) : ISC_WB_O_ZERO_VAL) + +static inline void isc_update_v4l2_ctrls(struct isc_device *isc) +{ + struct isc_ctrls *ctrls = &isc->ctrls; + + /* In here we set the v4l2 controls w.r.t. our pipeline config */ + v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); + v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); + v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); + v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); + + v4l2_ctrl_s_ctrl(isc->r_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R])); + v4l2_ctrl_s_ctrl(isc->b_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B])); + v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR])); + v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB])); +} + static inline void isc_update_awb_ctrls(struct isc_device *isc) { struct isc_ctrls *ctrls = &isc->ctrls; + /* In here we set our actual hw pipeline config */ + regmap_write(isc->regmap, ISC_WB_O_RGR, (ISC_WB_O_ZERO_VAL - (ctrls->offset[ISC_HIS_CFG_MODE_R])) | ((ISC_WB_O_ZERO_VAL - ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); @@ -662,11 +688,9 @@ static void isc_set_pipeline(struct isc_device *isc, u32 pipeline) bay_cfg = isc->config.sd_format->cfa_baycfg; - if (ctrls->awb == ISC_WB_NONE) - isc_reset_awb_ctrls(isc); - regmap_write(regmap, ISC_WB_CFG, bay_cfg); isc_update_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); @@ -1396,6 +1420,7 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) isc->try_config.sd_format != isc->config.sd_format) { isc->ctrls.hist_stat = HIST_INIT; isc_reset_awb_ctrls(isc); + isc_update_v4l2_ctrls(isc); } /* make the try configuration active */ isc->config = isc->try_config; @@ -1814,10 +1839,6 @@ static void isc_awb_work(struct work_struct *w) ctrls->hist_id = hist_id; baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; - /* if no more auto white balance, reset controls. */ - if (ctrls->awb == ISC_WB_NONE) - isc_reset_awb_ctrls(isc); - pm_runtime_get_sync(isc->dev); /* @@ -1842,6 +1863,8 @@ static void isc_awb_work(struct work_struct *w) if (ctrls->awb == ISC_WB_ONETIME) { v4l2_info(&isc->v4l2_dev, "Completed one time white-balance adjustment.\n"); + /* update the v4l2 controls values */ + isc_update_v4l2_ctrls(isc); ctrls->awb = ISC_WB_NONE; } } @@ -1873,6 +1896,27 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_GAMMA: ctrls->gamma_index = ctrl->val; break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct v4l2_ctrl_ops isc_ctrl_ops = { + .s_ctrl = isc_s_ctrl, +}; + +static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) + return 0; + + switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (ctrl->val == 1) ctrls->awb = ISC_WB_AUTO; @@ -1883,36 +1927,142 @@ static int isc_s_ctrl(struct v4l2_ctrl *ctrl) if (!isc->config.sd_format) break; - if (ctrls->hist_stat != HIST_ENABLED) - isc_reset_awb_ctrls(isc); + /* configure the controls with new values from v4l2 */ + if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; + if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) + ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; + + if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_R] = + ISC_CTRL_V4L2_TO_ISC(isc->r_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_B] = + ISC_CTRL_V4L2_TO_ISC(isc->b_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GR] = + ISC_CTRL_V4L2_TO_ISC(isc->gr_off_ctrl->val); + if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) + ctrls->offset[ISC_HIS_CFG_MODE_GB] = + ISC_CTRL_V4L2_TO_ISC(isc->gb_off_ctrl->val); - if (isc->ctrls.awb == ISC_WB_AUTO && + isc_update_awb_ctrls(isc); + + if (vb2_is_streaming(&isc->vb2_vidq)) { + /* + * If we are streaming, we can update profile to + * have the new settings in place. + */ + isc_update_profile(isc); + } else { + /* + * The auto cluster will activate automatically this + * control. This has to be deactivated when not + * streaming. + */ + v4l2_ctrl_activate(isc->do_wb_ctrl, false); + } + + /* if we have autowhitebalance on, start histogram procedure */ + if (ctrls->awb == ISC_WB_AUTO && vb2_is_streaming(&isc->vb2_vidq) && ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) isc_set_histogram(isc, true); - break; - case V4L2_CID_DO_WHITE_BALANCE: - /* if AWB is enabled, do nothing */ - if (ctrls->awb == ISC_WB_AUTO) - return 0; + /* + * for one time whitebalance adjustment, check the button, + * if it's pressed, perform the one time operation. + */ + if (ctrls->awb == ISC_WB_NONE && + ctrl->cluster[ISC_CTRL_DO_WB]->is_new && + !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & + V4L2_CTRL_FLAG_INACTIVE)) { + ctrls->awb = ISC_WB_ONETIME; + isc_set_histogram(isc, true); + v4l2_dbg(1, debug, &isc->v4l2_dev, + "One time white-balance started.\n"); + } + return 0; + } + return 0; +} - ctrls->awb = ISC_WB_ONETIME; - isc_set_histogram(isc, true); - v4l2_dbg(1, debug, &isc->v4l2_dev, - "One time white-balance started.\n"); +static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) +{ + struct isc_device *isc = container_of(ctrl->handler, + struct isc_device, ctrls.handler); + struct isc_ctrls *ctrls = &isc->ctrls; + + switch (ctrl->id) { + /* being a cluster, this id will be called for every control */ + case V4L2_CID_AUTO_WHITE_BALANCE: + ctrl->cluster[ISC_CTRL_R_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_R]; + ctrl->cluster[ISC_CTRL_B_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_B]; + ctrl->cluster[ISC_CTRL_GR_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GR]; + ctrl->cluster[ISC_CTRL_GB_GAIN]->val = + ctrls->gain[ISC_HIS_CFG_MODE_GB]; + + ctrl->cluster[ISC_CTRL_R_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_R]); + ctrl->cluster[ISC_CTRL_B_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_B]); + ctrl->cluster[ISC_CTRL_GR_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GR]); + ctrl->cluster[ISC_CTRL_GB_OFF]->val = + ISC_CTRL_ISC_TO_V4L2(ctrls->offset[ISC_HIS_CFG_MODE_GB]); break; - default: - return -EINVAL; } - return 0; } -static const struct v4l2_ctrl_ops isc_ctrl_ops = { - .s_ctrl = isc_s_ctrl, +static const struct v4l2_ctrl_ops isc_awb_ops = { + .s_ctrl = isc_s_awb_ctrl, + .g_volatile_ctrl = isc_g_volatile_awb_ctrl, }; +#define ISC_CTRL_OFF(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = -4095, \ + .max = 4095, \ + .step = 1, \ + .def = 0, \ + } + +ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); +ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); +ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); +ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); + +#define ISC_CTRL_GAIN(_name, _id, _name_str) \ + static const struct v4l2_ctrl_config _name = { \ + .ops = &isc_awb_ops, \ + .id = _id, \ + .name = _name_str, \ + .type = V4L2_CTRL_TYPE_INTEGER, \ + .flags = V4L2_CTRL_FLAG_SLIDER, \ + .min = 0, \ + .max = 8191, \ + .step = 1, \ + .def = 512, \ + } + +ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); +ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); +ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); +ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); + static int isc_ctrl_init(struct isc_device *isc) { const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; @@ -1923,7 +2073,7 @@ static int isc_ctrl_init(struct isc_device *isc) ctrls->hist_stat = HIST_INIT; isc_reset_awb_ctrls(isc); - ret = v4l2_ctrl_handler_init(hdl, 5); + ret = v4l2_ctrl_handler_init(hdl, 13); if (ret < 0) return ret; @@ -1933,10 +2083,13 @@ static int isc_ctrl_init(struct isc_device *isc) v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256); v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, GAMMA_MAX, 1, 2); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); + isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); /* do_white_balance is a button, so min,max,step,default are ignored */ - isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_DO_WHITE_BALANCE, + isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, + V4L2_CID_DO_WHITE_BALANCE, 0, 0, 0, 0); if (!isc->do_wb_ctrl) { @@ -1947,6 +2100,21 @@ static int isc_ctrl_init(struct isc_device *isc) v4l2_ctrl_activate(isc->do_wb_ctrl, false); + isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); + isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); + isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); + isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); + isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); + isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); + isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); + isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); + + /* + * The cluster is in auto mode with autowhitebalance enabled + * and manual mode otherwise. + */ + v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); + v4l2_ctrl_handler_setup(hdl); return 0; diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h index bfaed2fad2b5..fc56a745c7d1 100644 --- a/drivers/media/platform/atmel/atmel-isc.h +++ b/drivers/media/platform/atmel/atmel-isc.h @@ -213,7 +213,6 @@ struct isc_device { struct fmt_config try_config; struct isc_ctrls ctrls; - struct v4l2_ctrl *do_wb_ctrl; struct work_struct awb_work; struct mutex lock; /* serialize access to file operations */ @@ -223,6 +222,28 @@ struct isc_device { struct isc_subdev_entity *current_subdev; struct list_head subdev_entities; + + struct { +#define ISC_CTRL_DO_WB 1 +#define ISC_CTRL_R_GAIN 2 +#define ISC_CTRL_B_GAIN 3 +#define ISC_CTRL_GR_GAIN 4 +#define ISC_CTRL_GB_GAIN 5 +#define ISC_CTRL_R_OFF 6 +#define ISC_CTRL_B_OFF 7 +#define ISC_CTRL_GR_OFF 8 +#define ISC_CTRL_GB_OFF 9 + struct v4l2_ctrl *awb_ctrl; + struct v4l2_ctrl *do_wb_ctrl; + struct v4l2_ctrl *r_gain_ctrl; + struct v4l2_ctrl *b_gain_ctrl; + struct v4l2_ctrl *gr_gain_ctrl; + struct v4l2_ctrl *gb_gain_ctrl; + struct v4l2_ctrl *r_off_ctrl; + struct v4l2_ctrl *b_off_ctrl; + struct v4l2_ctrl *gr_off_ctrl; + struct v4l2_ctrl *gb_off_ctrl; + }; }; #define GAMMA_MAX 2 diff --git a/include/linux/atmel-isc-media.h b/include/linux/atmel-isc-media.h new file mode 100644 index 000000000000..79a320fb724e --- /dev/null +++ b/include/linux/atmel-isc-media.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019 Microchip Technology Inc. and its subsidiaries + * + * Author: Eugen Hristev + */ + +#ifndef __LINUX_ATMEL_ISC_MEDIA_H__ +#define __LINUX_ATMEL_ISC_MEDIA_H__ + +/* + * There are 8 controls available: + * 4 gain controls, sliders, for each of the BAYER components: R, B, GR, GB. + * These gains are multipliers for each component, in format unsigned 0:4:9 with + * a default value of 512 (1.0 multiplier). + * 4 offset controls, sliders, for each of the BAYER components: R, B, GR, GB. + * These offsets are added/substracted from each component, in format signed + * 1:12:0 with a default value of 0 (+/- 0) + * + * To expose this to userspace, added 8 custom controls, in an auto cluster. + * + * To summarize the functionality: + * The auto cluster switch is the auto white balance control, and it works + * like this: + * AWB == 1: autowhitebalance is on, the do_white_balance button is inactive, + * the gains/offsets are inactive, but volatile and readable. + * Thus, the results of the whitebalance algorithm are available to userspace to + * read at any time. + * AWB == 0: autowhitebalance is off, cluster is in manual mode, user can + * configure the gain/offsets directly. + * More than that, if the do_white_balance button is + * pressed, the driver will perform one-time-adjustment, (preferably with color + * checker card) and the userspace can read again the new values. + * + * With this feature, the userspace can save the coefficients and reinstall them + * for example after reboot or reprobing the driver. + */ + +enum atmel_isc_ctrl_id { + /* Red component gain control */ + ISC_CID_R_GAIN = (V4L2_CID_USER_ATMEL_ISC_BASE + 0), + /* Blue component gain control */ + ISC_CID_B_GAIN, + /* Green Red component gain control */ + ISC_CID_GR_GAIN, + /* Green Blue gain control */ + ISC_CID_GB_GAIN, + /* Red component offset control */ + ISC_CID_R_OFFSET, + /* Blue component offset control */ + ISC_CID_B_OFFSET, + /* Green Red component offset control */ + ISC_CID_GR_OFFSET, + /* Green Blue component offset control */ + ISC_CID_GB_OFFSET, +}; + +#endif diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h index 5a7bedee2b0e..1a58d7cc4ccc 100644 --- a/include/uapi/linux/v4l2-controls.h +++ b/include/uapi/linux/v4l2-controls.h @@ -192,6 +192,12 @@ enum v4l2_colorfx { * We reserve 16 controls for this driver. */ #define V4L2_CID_USER_IMX_BASE (V4L2_CID_USER_BASE + 0x10b0) +/* + * The base for the atmel isc driver controls. + * We reserve 32 controls for this driver. + */ +#define V4L2_CID_USER_ATMEL_ISC_BASE (V4L2_CID_USER_BASE + 0x10c0) + /* MPEG-class control IDs */ /* The MPEG controls are applicable to all codec controls * and the 'MPEG' part of the define is historical */ -- cgit v1.2.3 From 30defecb98400575349a7d32f0526e1dc42ea83e Mon Sep 17 00:00:00 2001 From: Mohammad Rasim Date: Fri, 10 Jan 2020 17:25:45 +0100 Subject: media: rc: add keymap for Videostrong KII Pro This is an NEC remote control device shipped with the Videostrong KII Pro tv box as well as other devices from videostrong. Signed-off-by: Mohammad Rasim Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-videostrong-kii-pro.c | 83 +++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 85 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-videostrong-kii-pro.c (limited to 'include') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 63261ef6380a..aaa1bf81d00d 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-videomate-m1f.o \ rc-videomate-s350.o \ rc-videomate-tv-pvr.o \ + rc-videostrong-kii-pro.o \ rc-wetek-hub.o \ rc-wetek-play2.o \ rc-winfast.o \ diff --git a/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c b/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c new file mode 100644 index 000000000000..414d4d231e7e --- /dev/null +++ b/drivers/media/rc/keymaps/rc-videostrong-kii-pro.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2019 Mohammad Rasim + +#include +#include + +// +// Keytable for the Videostrong KII Pro STB remote control +// + +static struct rc_map_table kii_pro[] = { + { 0x59, KEY_POWER }, + { 0x19, KEY_MUTE }, + { 0x42, KEY_RED }, + { 0x40, KEY_GREEN }, + { 0x00, KEY_YELLOW }, + { 0x03, KEY_BLUE }, + { 0x4a, KEY_BACK }, + { 0x48, KEY_FORWARD }, + { 0x08, KEY_PREVIOUSSONG}, + { 0x0b, KEY_NEXTSONG}, + { 0x46, KEY_PLAYPAUSE }, + { 0x44, KEY_STOP }, + { 0x1f, KEY_FAVORITES}, //KEY_F5? + { 0x04, KEY_PVR }, + { 0x4d, KEY_EPG }, + { 0x02, KEY_INFO }, + { 0x09, KEY_SUBTITLE }, + { 0x01, KEY_AUDIO }, + { 0x0d, KEY_HOMEPAGE }, + { 0x11, KEY_TV }, // DTV ? + { 0x06, KEY_UP }, + { 0x5a, KEY_LEFT }, + { 0x1a, KEY_ENTER }, // KEY_OK ? + { 0x1b, KEY_RIGHT }, + { 0x16, KEY_DOWN }, + { 0x45, KEY_MENU }, + { 0x05, KEY_ESC }, + { 0x13, KEY_VOLUMEUP }, + { 0x17, KEY_VOLUMEDOWN }, + { 0x58, KEY_APPSELECT }, + { 0x12, KEY_VENDOR }, // mouse + { 0x55, KEY_PAGEUP }, // KEY_CHANNELUP ? + { 0x15, KEY_PAGEDOWN }, // KEY_CHANNELDOWN ? + { 0x52, KEY_1 }, + { 0x50, KEY_2 }, + { 0x10, KEY_3 }, + { 0x56, KEY_4 }, + { 0x54, KEY_5 }, + { 0x14, KEY_6 }, + { 0x4e, KEY_7 }, + { 0x4c, KEY_8 }, + { 0x0c, KEY_9 }, + { 0x18, KEY_WWW }, // KEY_F7 + { 0x0f, KEY_0 }, + { 0x51, KEY_BACKSPACE }, +}; + +static struct rc_map_list kii_pro_map = { + .map = { + .scan = kii_pro, + .size = ARRAY_SIZE(kii_pro), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_KII_PRO, + } +}; + +static int __init init_rc_map_kii_pro(void) +{ + return rc_map_register(&kii_pro_map); +} + +static void __exit exit_rc_map_kii_pro(void) +{ + rc_map_unregister(&kii_pro_map); +} + +module_init(init_rc_map_kii_pro) +module_exit(exit_rc_map_kii_pro) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mohammad Rasim "); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index f99575a0d29c..d22810dcd85c 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -274,6 +274,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_VIDEOMATE_K100 "rc-videomate-k100" #define RC_MAP_VIDEOMATE_S350 "rc-videomate-s350" #define RC_MAP_VIDEOMATE_TV_PVR "rc-videomate-tv-pvr" +#define RC_MAP_KII_PRO "rc-videostrong-kii-pro" #define RC_MAP_WETEK_HUB "rc-wetek-hub" #define RC_MAP_WETEK_PLAY2 "rc-wetek-play2" #define RC_MAP_WINFAST "rc-winfast" -- cgit v1.2.3 From 238e4a5baa361256ae1641ad9455bb2bb359273f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:09 +0100 Subject: media: rename VFL_TYPE_GRABBER to _VIDEO We currently have the following devnode types: enum vfl_devnode_type { VFL_TYPE_GRABBER = 0, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, VFL_TYPE_SDR, VFL_TYPE_TOUCH, VFL_TYPE_MAX /* Shall be the last one */ }; They all make sense, except for the first: GRABBER really refers to /dev/videoX devices, which can be capture, output or m2m, so 'grabber' doesn't even refer to their function anymore. Let's call a spade a spade and rename this to VFL_TYPE_VIDEO. Signed-off-by: Hans Verkuil Acked-by: Sakari Ailus Reviewed-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/kapi/v4l2-dev.rst | 4 ++-- .../translations/zh_CN/video4linux/v4l2-framework.txt | 4 ++-- drivers/media/v4l2-core/v4l2-dev.c | 10 +++++----- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- include/media/v4l2-dev.h | 6 ++++-- samples/v4l/v4l2-pci-skeleton.c | 2 +- 6 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/Documentation/media/kapi/v4l2-dev.rst b/Documentation/media/kapi/v4l2-dev.rst index 4c5a15c53dbf..63c064837c00 100644 --- a/Documentation/media/kapi/v4l2-dev.rst +++ b/Documentation/media/kapi/v4l2-dev.rst @@ -185,7 +185,7 @@ This will create the character device for you. .. code-block:: c - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err) { video_device_release(vdev); /* or kfree(my_vdev); */ return err; @@ -201,7 +201,7 @@ types exist: ========================== ==================== ============================== :c:type:`vfl_devnode_type` Device name Usage ========================== ==================== ============================== -``VFL_TYPE_GRABBER`` ``/dev/videoX`` for video input/output devices +``VFL_TYPE_VIDEO`` ``/dev/videoX`` for video input/output devices ``VFL_TYPE_VBI`` ``/dev/vbiX`` for vertical blank data (i.e. closed captions, teletext) ``VFL_TYPE_RADIO`` ``/dev/radioX`` for radio tuners diff --git a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt index 66c7c568bd86..9c39ee58ea50 100644 --- a/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt +++ b/Documentation/translations/zh_CN/video4linux/v4l2-framework.txt @@ -649,7 +649,7 @@ video_device注册 接下来你需要注册视频设备:这会为你创建一个字符设备。 - err = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + err = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (err) { video_device_release(vdev); /* or kfree(my_vdev); */ return err; @@ -660,7 +660,7 @@ video_device注册 注册哪种设备是根据类型(type)参数。存在以下类型: -VFL_TYPE_GRABBER: 用于视频输入/输出设备的 videoX +VFL_TYPE_VIDEO: 用于视频输入/输出设备的 videoX VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视) VFL_TYPE_RADIO: 用于广播调谐器的 radioX diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index da42d172714a..97b6a3af1361 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -542,13 +542,13 @@ static void determine_valid_ioctls(struct video_device *vdev) V4L2_CAP_META_OUTPUT; DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; - bool is_vid = vdev->vfl_type == VFL_TYPE_GRABBER && + bool is_vid = vdev->vfl_type == VFL_TYPE_VIDEO && (vdev->device_caps & vid_caps); bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI; bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO; bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR; bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH; - bool is_meta = vdev->vfl_type == VFL_TYPE_GRABBER && + bool is_meta = vdev->vfl_type == VFL_TYPE_VIDEO && (vdev->device_caps & meta_caps); bool is_rx = vdev->vfl_dir != VFL_DIR_TX; bool is_tx = vdev->vfl_dir != VFL_DIR_RX; @@ -783,7 +783,7 @@ static int video_register_media_controller(struct video_device *vdev) vdev->entity.function = MEDIA_ENT_F_UNKNOWN; switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: intf_type = MEDIA_INTF_T_V4L_VIDEO; vdev->entity.function = MEDIA_ENT_F_IO_V4L; break; @@ -891,7 +891,7 @@ int __video_register_device(struct video_device *vdev, /* Part 1: check device type */ switch (type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: name_base = "video"; break; case VFL_TYPE_VBI: @@ -935,7 +935,7 @@ int __video_register_device(struct video_device *vdev, * of 128-191 and just pick the first free minor there * (new style). */ switch (type) { - case VFL_TYPE_GRABBER: + case VFL_TYPE_VIDEO: minor_offset = 0; minor_cnt = 64; break; diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index aaf83e254272..fbcc7a20eedf 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -941,12 +941,12 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) V4L2_CAP_META_OUTPUT; struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER && + bool is_vid = vfd->vfl_type == VFL_TYPE_VIDEO && (vfd->device_caps & vid_caps); bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; - bool is_meta = vfd->vfl_type == VFL_TYPE_GRABBER && + bool is_meta = vfd->vfl_type == VFL_TYPE_VIDEO && (vfd->device_caps & meta_caps); bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 48531e57cc5a..5e7c0f8acd05 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -24,7 +24,8 @@ /** * enum vfl_devnode_type - type of V4L2 device node * - * @VFL_TYPE_GRABBER: for video input/output devices + * @VFL_TYPE_VIDEO: for video input/output devices + * @VFL_TYPE_GRABBER: deprecated, same as VFL_TYPE_VIDEO * @VFL_TYPE_VBI: for vertical blank data (i.e. closed captions, teletext) * @VFL_TYPE_RADIO: for radio tuners * @VFL_TYPE_SUBDEV: for V4L2 subdevices @@ -33,7 +34,8 @@ * @VFL_TYPE_MAX: number of VFL types, must always be last in the enum */ enum vfl_devnode_type { - VFL_TYPE_GRABBER = 0, + VFL_TYPE_VIDEO, + VFL_TYPE_GRABBER = VFL_TYPE_VIDEO, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index f6a551bd57ef..3fa6582b4a68 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -879,7 +879,7 @@ static int skeleton_probe(struct pci_dev *pdev, const struct pci_device_id *ent) vdev->tvnorms = SKEL_TVNORMS; video_set_drvdata(vdev, skel); - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); if (ret) goto free_hdl; -- cgit v1.2.3 From dc42cf74d98a39af30ecaf78e7fad7e265ae528f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Feb 2020 12:41:19 +0100 Subject: media: v4l2-dev.h: remove VFL_TYPE_GRABBER The last driver was converted to use VFL_TYPE_VIDEO, so the old _GRABBER enum can now be removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-dev.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 5e7c0f8acd05..4602c15ff878 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -25,7 +25,6 @@ * enum vfl_devnode_type - type of V4L2 device node * * @VFL_TYPE_VIDEO: for video input/output devices - * @VFL_TYPE_GRABBER: deprecated, same as VFL_TYPE_VIDEO * @VFL_TYPE_VBI: for vertical blank data (i.e. closed captions, teletext) * @VFL_TYPE_RADIO: for radio tuners * @VFL_TYPE_SUBDEV: for V4L2 subdevices @@ -35,7 +34,6 @@ */ enum vfl_devnode_type { VFL_TYPE_VIDEO, - VFL_TYPE_GRABBER = VFL_TYPE_VIDEO, VFL_TYPE_VBI, VFL_TYPE_RADIO, VFL_TYPE_SUBDEV, -- cgit v1.2.3 From 7bc3e6e55acf065500a24621f3b313e7e5998acf Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 19 Feb 2020 18:22:26 -0600 Subject: proc: Use a list of inodes to flush from proc Rework the flushing of proc to use a list of directory inodes that need to be flushed. The list is kept on struct pid not on struct task_struct, as there is a fixed connection between proc inodes and pids but at least for the case of de_thread the pid of a task_struct changes. This removes the dependency on proc_mnt which allows for different mounts of proc having different mount options even in the same pid namespace and this allows for the removal of proc_mnt which will trivially the first mount of proc to honor it's mount options. This flushing remains an optimization. The functions pid_delete_dentry and pid_revalidate ensure that ordinary dcache management will not attempt to use dentries past the point their respective task has died. When unused the shrinker will eventually be able to remove these dentries. There is a case in de_thread where proc_flush_pid can be called early for a given pid. Which winds up being safe (if suboptimal) as this is just an optiimization. Only pid directories are put on the list as the other per pid files are children of those directories and d_invalidate on the directory will get them as well. So that the pid can be used during flushing it's reference count is taken in release_task and dropped in proc_flush_pid. Further the call of proc_flush_pid is moved after the tasklist_lock is released in release_task so that it is certain that the pid has already been unhashed when flushing it taking place. This removes a small race where a dentry could recreated. As struct pid is supposed to be small and I need a per pid lock I reuse the only lock that currently exists in struct pid the the wait_pidfd.lock. The net result is that this adds all of this functionality with just a little extra list management overhead and a single extra pointer in struct pid. v2: Initialize pid->inodes. I somehow failed to get that initialization into the initial version of the patch. A boot failure was reported by "kernel test robot ", and failure to initialize that pid->inodes matches all of the reported symptoms. Signed-off-by: Eric W. Biederman --- fs/proc/base.c | 111 ++++++++++++++++-------------------------------- fs/proc/inode.c | 2 +- fs/proc/internal.h | 1 + include/linux/pid.h | 1 + include/linux/proc_fs.h | 4 +- kernel/exit.c | 4 +- kernel/pid.c | 1 + 7 files changed, 45 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/fs/proc/base.c b/fs/proc/base.c index c7c64272b0fa..e7efe9d6f3d6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1834,11 +1834,25 @@ void task_dump_owner(struct task_struct *task, umode_t mode, *rgid = gid; } +void proc_pid_evict_inode(struct proc_inode *ei) +{ + struct pid *pid = ei->pid; + + if (S_ISDIR(ei->vfs_inode.i_mode)) { + spin_lock(&pid->wait_pidfd.lock); + hlist_del_init_rcu(&ei->sibling_inodes); + spin_unlock(&pid->wait_pidfd.lock); + } + + put_pid(pid); +} + struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task, umode_t mode) { struct inode * inode; struct proc_inode *ei; + struct pid *pid; /* We need a new inode */ @@ -1856,10 +1870,18 @@ struct inode *proc_pid_make_inode(struct super_block * sb, /* * grab the reference to task. */ - ei->pid = get_task_pid(task, PIDTYPE_PID); - if (!ei->pid) + pid = get_task_pid(task, PIDTYPE_PID); + if (!pid) goto out_unlock; + /* Let the pid remember us for quick removal */ + ei->pid = pid; + if (S_ISDIR(mode)) { + spin_lock(&pid->wait_pidfd.lock); + hlist_add_head_rcu(&ei->sibling_inodes, &pid->inodes); + spin_unlock(&pid->wait_pidfd.lock); + } + task_dump_owner(task, 0, &inode->i_uid, &inode->i_gid); security_task_to_inode(task, inode); @@ -3230,90 +3252,29 @@ static const struct inode_operations proc_tgid_base_inode_operations = { .permission = proc_pid_permission, }; -static void proc_flush_task_mnt(struct vfsmount *mnt, pid_t pid, pid_t tgid) -{ - struct dentry *dentry, *leader, *dir; - char buf[10 + 1]; - struct qstr name; - - name.name = buf; - name.len = snprintf(buf, sizeof(buf), "%u", pid); - /* no ->d_hash() rejects on procfs */ - dentry = d_hash_and_lookup(mnt->mnt_root, &name); - if (dentry) { - d_invalidate(dentry); - dput(dentry); - } - - if (pid == tgid) - return; - - name.name = buf; - name.len = snprintf(buf, sizeof(buf), "%u", tgid); - leader = d_hash_and_lookup(mnt->mnt_root, &name); - if (!leader) - goto out; - - name.name = "task"; - name.len = strlen(name.name); - dir = d_hash_and_lookup(leader, &name); - if (!dir) - goto out_put_leader; - - name.name = buf; - name.len = snprintf(buf, sizeof(buf), "%u", pid); - dentry = d_hash_and_lookup(dir, &name); - if (dentry) { - d_invalidate(dentry); - dput(dentry); - } - - dput(dir); -out_put_leader: - dput(leader); -out: - return; -} - /** - * proc_flush_task - Remove dcache entries for @task from the /proc dcache. - * @task: task that should be flushed. + * proc_flush_pid - Remove dcache entries for @pid from the /proc dcache. + * @pid: pid that should be flushed. * - * When flushing dentries from proc, one needs to flush them from global - * proc (proc_mnt) and from all the namespaces' procs this task was seen - * in. This call is supposed to do all of this job. - * - * Looks in the dcache for - * /proc/@pid - * /proc/@tgid/task/@pid - * if either directory is present flushes it and all of it'ts children - * from the dcache. + * This function walks a list of inodes (that belong to any proc + * filesystem) that are attached to the pid and flushes them from + * the dentry cache. * * It is safe and reasonable to cache /proc entries for a task until * that task exits. After that they just clog up the dcache with * useless entries, possibly causing useful dcache entries to be - * flushed instead. This routine is proved to flush those useless - * dcache entries at process exit time. + * flushed instead. This routine is provided to flush those useless + * dcache entries when a process is reaped. * * NOTE: This routine is just an optimization so it does not guarantee - * that no dcache entries will exist at process exit time it - * just makes it very unlikely that any will persist. + * that no dcache entries will exist after a process is reaped + * it just makes it very unlikely that any will persist. */ -void proc_flush_task(struct task_struct *task) +void proc_flush_pid(struct pid *pid) { - int i; - struct pid *pid, *tgid; - struct upid *upid; - - pid = task_pid(task); - tgid = task_tgid(task); - - for (i = 0; i <= pid->level; i++) { - upid = &pid->numbers[i]; - proc_flush_task_mnt(upid->ns->proc_mnt, upid->nr, - tgid->numbers[i].nr); - } + proc_invalidate_siblings_dcache(&pid->inodes, &pid->wait_pidfd.lock); + put_pid(pid); } static struct dentry *proc_pid_instantiate(struct dentry * dentry, diff --git a/fs/proc/inode.c b/fs/proc/inode.c index d9243b24554a..1e730ea1dcd6 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c @@ -40,7 +40,7 @@ static void proc_evict_inode(struct inode *inode) /* Stop tracking associated processes */ if (ei->pid) { - put_pid(ei->pid); + proc_pid_evict_inode(ei); ei->pid = NULL; } diff --git a/fs/proc/internal.h b/fs/proc/internal.h index fd470172675f..9e294f0290e5 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -158,6 +158,7 @@ extern int proc_pid_statm(struct seq_file *, struct pid_namespace *, extern const struct dentry_operations pid_dentry_operations; extern int pid_getattr(const struct path *, struct kstat *, u32, unsigned int); extern int proc_setattr(struct dentry *, struct iattr *); +extern void proc_pid_evict_inode(struct proc_inode *); extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t); extern void pid_update_inode(struct task_struct *, struct inode *); extern int pid_delete_dentry(const struct dentry *); diff --git a/include/linux/pid.h b/include/linux/pid.h index 998ae7d24450..01a0d4e28506 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -62,6 +62,7 @@ struct pid unsigned int level; /* lists of tasks that use this pid */ struct hlist_head tasks[PIDTYPE_MAX]; + struct hlist_head inodes; /* wait queue for pidfd notifications */ wait_queue_head_t wait_pidfd; struct rcu_head rcu; diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 3dfa92633af3..40a7982b7285 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h @@ -32,7 +32,7 @@ struct proc_ops { typedef int (*proc_write_t)(struct file *, char *, size_t); extern void proc_root_init(void); -extern void proc_flush_task(struct task_struct *); +extern void proc_flush_pid(struct pid *); extern struct proc_dir_entry *proc_symlink(const char *, struct proc_dir_entry *, const char *); @@ -105,7 +105,7 @@ static inline void proc_root_init(void) { } -static inline void proc_flush_task(struct task_struct *task) +static inline void proc_flush_pid(struct pid *pid) { } diff --git a/kernel/exit.c b/kernel/exit.c index 2833ffb0c211..502b4995b688 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -191,6 +191,7 @@ void put_task_struct_rcu_user(struct task_struct *task) void release_task(struct task_struct *p) { struct task_struct *leader; + struct pid *thread_pid; int zap_leader; repeat: /* don't need to get the RCU readlock here - the process is dead and @@ -199,11 +200,11 @@ repeat: atomic_dec(&__task_cred(p)->user->processes); rcu_read_unlock(); - proc_flush_task(p); cgroup_release(p); write_lock_irq(&tasklist_lock); ptrace_release_task(p); + thread_pid = get_pid(p->thread_pid); __exit_signal(p); /* @@ -226,6 +227,7 @@ repeat: } write_unlock_irq(&tasklist_lock); + proc_flush_pid(thread_pid); release_thread(p); put_task_struct_rcu_user(p); diff --git a/kernel/pid.c b/kernel/pid.c index 0f4ecb57214c..ca08d6a3aa77 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -258,6 +258,7 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid, INIT_HLIST_HEAD(&pid->tasks[type]); init_waitqueue_head(&pid->wait_pidfd); + INIT_HLIST_HEAD(&pid->inodes); upid = pid->numbers + ns->level; spin_lock_irq(&pidmap_lock); -- cgit v1.2.3 From ecd942a0ef3a30f6037870bfc0a294d7e9fe9d4f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 24 Feb 2020 08:35:47 +0100 Subject: devlink: add ACL generic packet traps Add packet traps that can report packets that were dropped during ACL processing. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- Documentation/networking/devlink/devlink-trap.rst | 9 +++++++++ include/net/devlink.h | 9 +++++++++ net/core/devlink.c | 3 +++ 3 files changed, 21 insertions(+) (limited to 'include') diff --git a/Documentation/networking/devlink/devlink-trap.rst b/Documentation/networking/devlink/devlink-trap.rst index 47a429bb8658..63350e7332ce 100644 --- a/Documentation/networking/devlink/devlink-trap.rst +++ b/Documentation/networking/devlink/devlink-trap.rst @@ -238,6 +238,12 @@ be added to the following table: - ``drop`` - Traps NVE packets that the device decided to drop because their overlay source MAC is multicast + * - ``ingress_flow_action_drop`` + - ``drop`` + - Traps packets dropped during processing of ingress flow action drop + * - ``egress_flow_action_drop`` + - ``drop`` + - Traps packets dropped during processing of egress flow action drop Driver-specific Packet Traps ============================ @@ -277,6 +283,9 @@ narrow. The description of these groups must be added to the following table: * - ``tunnel_drops`` - Contains packet traps for packets that were dropped by the device during tunnel encapsulation / decapsulation + * - ``acl_drops`` + - Contains packet traps for packets that were dropped by the device during + ACL processing Testing ======= diff --git a/include/net/devlink.h b/include/net/devlink.h index 149c108be66f..07923e619206 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -596,6 +596,8 @@ enum devlink_trap_generic_id { DEVLINK_TRAP_GENERIC_ID_NON_ROUTABLE, DEVLINK_TRAP_GENERIC_ID_DECAP_ERROR, DEVLINK_TRAP_GENERIC_ID_OVERLAY_SMAC_MC, + DEVLINK_TRAP_GENERIC_ID_INGRESS_FLOW_ACTION_DROP, + DEVLINK_TRAP_GENERIC_ID_EGRESS_FLOW_ACTION_DROP, /* Add new generic trap IDs above */ __DEVLINK_TRAP_GENERIC_ID_MAX, @@ -610,6 +612,7 @@ enum devlink_trap_group_generic_id { DEVLINK_TRAP_GROUP_GENERIC_ID_L3_DROPS, DEVLINK_TRAP_GROUP_GENERIC_ID_BUFFER_DROPS, DEVLINK_TRAP_GROUP_GENERIC_ID_TUNNEL_DROPS, + DEVLINK_TRAP_GROUP_GENERIC_ID_ACL_DROPS, /* Add new generic trap group IDs above */ __DEVLINK_TRAP_GROUP_GENERIC_ID_MAX, @@ -671,6 +674,10 @@ enum devlink_trap_group_generic_id { "decap_error" #define DEVLINK_TRAP_GENERIC_NAME_OVERLAY_SMAC_MC \ "overlay_smac_is_mc" +#define DEVLINK_TRAP_GENERIC_NAME_INGRESS_FLOW_ACTION_DROP \ + "ingress_flow_action_drop" +#define DEVLINK_TRAP_GENERIC_NAME_EGRESS_FLOW_ACTION_DROP \ + "egress_flow_action_drop" #define DEVLINK_TRAP_GROUP_GENERIC_NAME_L2_DROPS \ "l2_drops" @@ -680,6 +687,8 @@ enum devlink_trap_group_generic_id { "buffer_drops" #define DEVLINK_TRAP_GROUP_GENERIC_NAME_TUNNEL_DROPS \ "tunnel_drops" +#define DEVLINK_TRAP_GROUP_GENERIC_NAME_ACL_DROPS \ + "acl_drops" #define DEVLINK_TRAP_GENERIC(_type, _init_action, _id, _group, _metadata_cap) \ { \ diff --git a/net/core/devlink.c b/net/core/devlink.c index 216bdd25ce39..0d7c5d3443d2 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -7795,6 +7795,8 @@ static const struct devlink_trap devlink_trap_generic[] = { DEVLINK_TRAP(NON_ROUTABLE, DROP), DEVLINK_TRAP(DECAP_ERROR, EXCEPTION), DEVLINK_TRAP(OVERLAY_SMAC_MC, DROP), + DEVLINK_TRAP(INGRESS_FLOW_ACTION_DROP, DROP), + DEVLINK_TRAP(EGRESS_FLOW_ACTION_DROP, DROP), }; #define DEVLINK_TRAP_GROUP(_id) \ @@ -7808,6 +7810,7 @@ static const struct devlink_trap_group devlink_trap_group_generic[] = { DEVLINK_TRAP_GROUP(L3_DROPS), DEVLINK_TRAP_GROUP(BUFFER_DROPS), DEVLINK_TRAP_GROUP(TUNNEL_DROPS), + DEVLINK_TRAP_GROUP(ACL_DROPS), }; static int devlink_trap_generic_verify(const struct devlink_trap *trap) -- cgit v1.2.3 From acf253c11329caa6be6d2abc14dfc8c0ec83718a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 19 Feb 2020 15:56:30 +0900 Subject: ASoC: soc-pcm: add snd_soc_dai_get_pcm_stream() DAI driver has playback/capture stream. OTOH, we have SNDRV_PCM_STREAM_PLAYBACK/CAPTURE. Because of this kind of implementation, ALSA SoC needs to have many verbose code. To solve this issue, this patch adds snd_soc_dai_get_pcm_stream() macro to get playback/capture stream pointer from stream. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87ftf7jcab.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 7 +++++++ sound/soc/soc-dai.c | 7 +------ sound/soc/soc-pcm.c | 49 +++++++++---------------------------------------- 3 files changed, 17 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 7481e468be39..c1089194ddf1 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -352,6 +352,13 @@ struct snd_soc_dai { unsigned int started:1; }; +static inline struct snd_soc_pcm_stream * +snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + &dai->driver->playback : &dai->driver->capture; +} + static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, const struct snd_pcm_substream *ss) { diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 73a829393652..19142f6e533c 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -390,12 +390,7 @@ int snd_soc_dai_compress_new(struct snd_soc_dai *dai, */ bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int dir) { - struct snd_soc_pcm_stream *stream; - - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - stream = &dai->driver->playback; - else - stream = &dai->driver->capture; + struct snd_soc_pcm_stream *stream = snd_soc_dai_get_pcm_stream(dai, dir); /* If the codec specifies any channels at all, it supports the stream */ return stream->channels_min; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index aff27c8599ef..7cb445bb1b54 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -396,20 +396,16 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) struct snd_pcm_hardware *hw = &runtime->hw; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai; - struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; unsigned int chan_min = 0, chan_max = UINT_MAX; unsigned int rate_min = 0, rate_max = UINT_MAX; unsigned int rates = UINT_MAX; u64 formats = ULLONG_MAX; + int stream = substream->stream; int i; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + cpu_stream = snd_soc_dai_get_pcm_stream(rtd->cpu_dai, stream); /* first calculate min/max only for CODECs in the DAI link */ for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -427,11 +423,8 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) substream->stream)) continue; - codec_dai_drv = codec_dai->driver; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(codec_dai, stream); + chan_min = max(chan_min, codec_stream->channels_min); chan_max = min(chan_max, codec_stream->channels_max); rate_min = max(rate_min, codec_stream->rate_min); @@ -1600,7 +1593,6 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; int i; @@ -1612,11 +1604,7 @@ static void dpcm_runtime_merge_format(struct snd_pcm_substream *substream, if (!snd_soc_dai_stream_valid(dai, stream)) continue; - codec_dai_drv = dai->driver; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); *formats &= codec_stream->formats; } @@ -1641,15 +1629,10 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream); *channels_min = max(*channels_min, cpu_stream->channels_min); *channels_max = min(*channels_max, cpu_stream->channels_max); @@ -1659,12 +1642,7 @@ static void dpcm_runtime_merge_chan(struct snd_pcm_substream *substream, * DAIs connected to a single CPU DAI, use CPU DAI's directly */ if (be->num_codecs == 1) { - codec_dai_drv = be->codec_dais[0]->driver; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(be->codec_dais[0], stream); *channels_min = max(*channels_min, codec_stream->channels_min); @@ -1693,17 +1671,12 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, for_each_dpcm_be(fe, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; - struct snd_soc_dai_driver *cpu_dai_drv = be->cpu_dai->driver; - struct snd_soc_dai_driver *codec_dai_drv; struct snd_soc_pcm_stream *codec_stream; struct snd_soc_pcm_stream *cpu_stream; struct snd_soc_dai *dai; int i; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - cpu_stream = &cpu_dai_drv->playback; - else - cpu_stream = &cpu_dai_drv->capture; + cpu_stream = snd_soc_dai_get_pcm_stream(be->cpu_dai, stream); *rate_min = max(*rate_min, cpu_stream->rate_min); *rate_max = min_not_zero(*rate_max, cpu_stream->rate_max); @@ -1717,11 +1690,7 @@ static void dpcm_runtime_merge_rate(struct snd_pcm_substream *substream, if (!snd_soc_dai_stream_valid(dai, stream)) continue; - codec_dai_drv = dai->driver; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - codec_stream = &codec_dai_drv->playback; - else - codec_stream = &codec_dai_drv->capture; + codec_stream = snd_soc_dai_get_pcm_stream(dai, stream); *rate_min = max(*rate_min, codec_stream->rate_min); *rate_max = min_not_zero(*rate_max, -- cgit v1.2.3 From 0c01f6ca8e4cc1e5505bf4657cf77fbfaa7b0bc2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 19 Feb 2020 15:56:41 +0900 Subject: ASoC: soc-pcm: add snd_soc_dai_get_widget() soc-pcm.c has dai_get_widget(), but it can be more generic. This patch renames it to snd_soc_dai_get_widget(), and use it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87d0abjca1.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 8 ++++++++ sound/soc/intel/skylake/skl-pcm.c | 10 ++-------- sound/soc/soc-dapm.c | 10 ++-------- sound/soc/soc-pcm.c | 17 ++++------------- 4 files changed, 16 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index c1089194ddf1..92c382690930 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -359,6 +359,14 @@ snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream) &dai->driver->playback : &dai->driver->capture; } +static inline +struct snd_soc_dapm_widget *snd_soc_dai_get_widget( + struct snd_soc_dai *dai, int stream) +{ + return (stream == SNDRV_PCM_STREAM_PLAYBACK) ? + dai->playback_widget : dai->capture_widget; +} + static inline void *snd_soc_dai_get_dma_data(const struct snd_soc_dai *dai, const struct snd_pcm_substream *ss) { diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c index b99509675d29..05a9677c5a53 100644 --- a/sound/soc/intel/skylake/skl-pcm.c +++ b/sound/soc/intel/skylake/skl-pcm.c @@ -112,10 +112,7 @@ static void skl_set_suspend_active(struct snd_pcm_substream *substream, struct snd_soc_dapm_widget *w; struct skl_dev *skl = bus_to_skl(bus); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, substream->stream); if (w->ignore_suspend && enable) skl->supend_active++; @@ -475,10 +472,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, if (!mconfig) return -EIO; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, substream->stream); switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 69eff234b26f..f2e678865480 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2620,10 +2620,7 @@ static int dapm_update_dai_unlocked(struct snd_pcm_substream *substream, struct snd_soc_dapm_widget *w; int ret; - if (dir == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, dir); if (!w) return 0; @@ -4389,10 +4386,7 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *w; unsigned int ep; - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - w = dai->playback_widget; - else - w = dai->capture_widget; + w = snd_soc_dai_get_widget(dai, stream); if (w) { dapm_mark_dirty(w, "stream event"); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6f56526bbb26..e183fabc5b6f 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -82,15 +82,6 @@ static int soc_rtd_trigger(struct snd_soc_pcm_runtime *rtd, return 0; } -static inline -struct snd_soc_dapm_widget *dai_get_widget(struct snd_soc_dai *dai, int stream) -{ - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - return dai->playback_widget; - else - return dai->capture_widget; -} - static void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd, int stream, int action) { @@ -1242,7 +1233,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, if (!be->dai_link->no_pcm) continue; - w = dai_get_widget(be->cpu_dai, stream); + w = snd_soc_dai_get_widget(be->cpu_dai, stream); dev_dbg(card->dev, "ASoC: try BE : %s\n", w ? w->name : "(not set)"); @@ -1251,7 +1242,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card, return be; for_each_rtd_codec_dai(be, i, dai) { - w = dai_get_widget(dai, stream); + w = snd_soc_dai_get_widget(dai, stream); if (w == widget) return be; @@ -1326,7 +1317,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, unsigned int i; /* is there a valid CPU DAI widget for this BE */ - widget = dai_get_widget(dpcm->be->cpu_dai, stream); + widget = snd_soc_dai_get_widget(dpcm->be->cpu_dai, stream); /* prune the BE if it's no longer in our active list */ if (widget && widget_in_list(list, widget)) @@ -1335,7 +1326,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, /* is there a valid CODEC DAI widget for this BE */ do_prune = 1; for_each_rtd_codec_dai(dpcm->be, i, dai) { - widget = dai_get_widget(dai, stream); + widget = snd_soc_dai_get_widget(dai, stream); /* prune the BE if it's no longer in our active list */ if (widget && widget_in_list(list, widget)) -- cgit v1.2.3 From 52645e332d227a3d3cd345e97a10d99b7e80fae4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 19 Feb 2020 15:56:52 +0900 Subject: ASoC: soc-pcm: move dpcm_path_put() to soc-pcm.c dpcm_path_put() (A) is calling kfree(*list). The freed list is created by dapm_widget_list_create() (B) which is called from snd_soc_dapm_dai_get_connected_widgets() (C) which is called from dpcm_path_get() (D). (B) dapm_widget_list_create(**list, ...) { ... => *list = kzalloc(); ... } (C) snd_soc_dapm_dai_get_connected_widgets(..., **list, ...) { ... dapm_widget_list_create(list, ...); ... } (D) dpcm_path_get(..., **list) { ... snd_soc_dapm_dai_get_connected_widgets(..., list, ...); ... } (A) dpcm_path_put(**list) { => kfree(*list); } This kind of unbalance code is very difficult to read/understand. To avoid this issue, this patch adds each missing paired function dapm_widget_list_free() for dapm_widget_list_create() (B), and snd_soc_dapm_dai_free_widgets() for snd_soc_dapm_dai_get_connected_widgets() (C). This patch uses these, and moves dpcm_path_put() next to dpcm_path_get(). Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87a75fjc9q.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc-dapm.h | 1 + include/sound/soc-dpcm.h | 7 +------ sound/soc/soc-dapm.c | 10 ++++++++++ sound/soc/soc-pcm.c | 5 +++++ 4 files changed, 17 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 9439e75945f6..464b20acd720 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -484,6 +484,7 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list, bool (*custom_stop_condition)(struct snd_soc_dapm_widget *, enum snd_soc_dapm_direction)); +void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list); struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( struct snd_kcontrol *kcontrol); diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 3e7819d2a6aa..40223577ec4a 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -145,6 +145,7 @@ static inline void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) int dpcm_path_get(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_); +void dpcm_path_put(struct snd_soc_dapm_widget_list **list); int dpcm_process_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list, int new); int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream); @@ -158,10 +159,4 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream); int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event); -static inline void dpcm_path_put(struct snd_soc_dapm_widget_list **list) -{ - kfree(*list); -} - - #endif diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index f2e678865480..8a7d700a0fda 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1105,6 +1105,11 @@ static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) } } +static void dapm_widget_list_free(struct snd_soc_dapm_widget_list **list) +{ + kfree(*list); +} + static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list, struct list_head *widgets) { @@ -1310,6 +1315,11 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, return paths; } +void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list) +{ + dapm_widget_list_free(list); +} + /* * Handler for regulator supply widget. */ diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 1bf2db1732bf..3b3b32923783 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1302,6 +1302,11 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe, return paths; } +void dpcm_path_put(struct snd_soc_dapm_widget_list **list) +{ + snd_soc_dapm_dai_free_widgets(list); +} + static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream, struct snd_soc_dapm_widget_list **list_) { -- cgit v1.2.3 From 571912c69f0ed731bd1e071ade9dc7ca4aa52065 Mon Sep 17 00:00:00 2001 From: Martin Varghese Date: Mon, 24 Feb 2020 10:57:50 +0530 Subject: net: UDP tunnel encapsulation module for tunnelling different protocols like MPLS, IP, NSH etc. The Bareudp tunnel module provides a generic L3 encapsulation tunnelling module for tunnelling different protocols like MPLS, IP,NSH etc inside a UDP tunnel. Signed-off-by: Martin Varghese Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/networking/bareudp.rst | 34 ++ Documentation/networking/index.rst | 1 + drivers/net/Kconfig | 13 + drivers/net/Makefile | 1 + drivers/net/bareudp.c | 743 +++++++++++++++++++++++++++++++++++ include/net/bareudp.h | 19 + include/net/ipv6.h | 6 + include/net/route.h | 6 + include/uapi/linux/if_link.h | 11 + net/ipv4/route.c | 48 +++ net/ipv6/ip6_output.c | 70 ++++ 11 files changed, 952 insertions(+) create mode 100644 Documentation/networking/bareudp.rst create mode 100644 drivers/net/bareudp.c create mode 100644 include/net/bareudp.h (limited to 'include') diff --git a/Documentation/networking/bareudp.rst b/Documentation/networking/bareudp.rst new file mode 100644 index 000000000000..6ee68c16cf5b --- /dev/null +++ b/Documentation/networking/bareudp.rst @@ -0,0 +1,34 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================== +Bare UDP Tunnelling Module Documentation +======================================== + +There are various L3 encapsulation standards using UDP being discussed to +leverage the UDP based load balancing capability of different networks. +MPLSoUDP (__ https://tools.ietf.org/html/rfc7510) is one among them. + +The Bareudp tunnel module provides a generic L3 encapsulation tunnelling +support for tunnelling different L3 protocols like MPLS, IP, NSH etc. inside +a UDP tunnel. + +Usage +------ + +1) Device creation & deletion + + a) ip link add dev bareudp0 type bareudp dstport 6635 ethertype 0x8847. + + This creates a bareudp tunnel device which tunnels L3 traffic with ethertype + 0x8847 (MPLS traffic). The destination port of the UDP header will be set to + 6635.The device will listen on UDP port 6635 to receive traffic. + + b) ip link delete bareudp0 + +2) Device Usage + +The bareudp device could be used along with OVS or flower filter in TC. +The OVS or TC flower layer must set the tunnel information in SKB dst field before +sending packet buffer to the bareudp device for transmission. On reception the +bareudp device extracts and stores the tunnel information in SKB dst field before +passing the packet buffer to the network stack. diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst index d07d9855dcd3..3a83cfb66704 100644 --- a/Documentation/networking/index.rst +++ b/Documentation/networking/index.rst @@ -8,6 +8,7 @@ Contents: netdev-FAQ af_xdp + bareudp batman-adv can can_ucan_protocol diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 25a8f9387d5a..66e410e58c8e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -258,6 +258,19 @@ config GENEVE To compile this driver as a module, choose M here: the module will be called geneve. +config BAREUDP + tristate "Bare UDP Encapsulation" + depends on INET + depends on IPV6 || !IPV6 + select NET_UDP_TUNNEL + select GRO_CELLS + help + This adds a bare UDP tunnel module for tunnelling different + kinds of traffic like MPLS, IP, etc. inside a UDP tunnel. + + To compile this driver as a module, choose M here: the module + will be called bareudp. + config GTP tristate "GPRS Tunneling Protocol datapath (GTP-U)" depends on INET diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 71b88ffc5587..65967246f240 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_VETH) += veth.o obj-$(CONFIG_VIRTIO_NET) += virtio_net.o obj-$(CONFIG_VXLAN) += vxlan.o obj-$(CONFIG_GENEVE) += geneve.o +obj-$(CONFIG_BAREUDP) += bareudp.o obj-$(CONFIG_GTP) += gtp.o obj-$(CONFIG_NLMON) += nlmon.o obj-$(CONFIG_NET_VRF) += vrf.o diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c new file mode 100644 index 000000000000..32518969139e --- /dev/null +++ b/drivers/net/bareudp.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Bareudp: UDP tunnel encasulation for different Payload types like + * MPLS, NSH, IP, etc. + * Copyright (c) 2019 Nokia, Inc. + * Authors: Martin Varghese, + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BAREUDP_BASE_HLEN sizeof(struct udphdr) +#define BAREUDP_IPV4_HLEN (sizeof(struct iphdr) + \ + sizeof(struct udphdr)) +#define BAREUDP_IPV6_HLEN (sizeof(struct ipv6hdr) + \ + sizeof(struct udphdr)) + +static bool log_ecn_error = true; +module_param(log_ecn_error, bool, 0644); +MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + +/* per-network namespace private data for this module */ + +static unsigned int bareudp_net_id; + +struct bareudp_net { + struct list_head bareudp_list; +}; + +/* Pseudo network device */ +struct bareudp_dev { + struct net *net; /* netns for packet i/o */ + struct net_device *dev; /* netdev for bareudp tunnel */ + __be16 ethertype; + __be16 port; + u16 sport_min; + struct socket __rcu *sock; + struct list_head next; /* bareudp node on namespace list */ + struct gro_cells gro_cells; +}; + +static int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) +{ + struct metadata_dst *tun_dst = NULL; + struct pcpu_sw_netstats *stats; + struct bareudp_dev *bareudp; + unsigned short family; + unsigned int len; + __be16 proto; + void *oiph; + int err; + + bareudp = rcu_dereference_sk_user_data(sk); + if (!bareudp) + goto drop; + + if (skb->protocol == htons(ETH_P_IP)) + family = AF_INET; + else + family = AF_INET6; + + proto = bareudp->ethertype; + + if (iptunnel_pull_header(skb, BAREUDP_BASE_HLEN, + proto, + !net_eq(bareudp->net, + dev_net(bareudp->dev)))) { + bareudp->dev->stats.rx_dropped++; + goto drop; + } + + tun_dst = udp_tun_rx_dst(skb, family, TUNNEL_KEY, 0, 0); + if (!tun_dst) { + bareudp->dev->stats.rx_dropped++; + goto drop; + } + skb_dst_set(skb, &tun_dst->dst); + skb->dev = bareudp->dev; + oiph = skb_network_header(skb); + skb_reset_network_header(skb); + + if (family == AF_INET) + err = IP_ECN_decapsulate(oiph, skb); +#if IS_ENABLED(CONFIG_IPV6) + else + err = IP6_ECN_decapsulate(oiph, skb); +#endif + + if (unlikely(err)) { + if (log_ecn_error) { + if (family == AF_INET) + net_info_ratelimited("non-ECT from %pI4 " + "with TOS=%#x\n", + &((struct iphdr *)oiph)->saddr, + ((struct iphdr *)oiph)->tos); +#if IS_ENABLED(CONFIG_IPV6) + else + net_info_ratelimited("non-ECT from %pI6\n", + &((struct ipv6hdr *)oiph)->saddr); +#endif + } + if (err > 1) { + ++bareudp->dev->stats.rx_frame_errors; + ++bareudp->dev->stats.rx_errors; + goto drop; + } + } + + len = skb->len; + err = gro_cells_receive(&bareudp->gro_cells, skb); + if (likely(err == NET_RX_SUCCESS)) { + stats = this_cpu_ptr(bareudp->dev->tstats); + u64_stats_update_begin(&stats->syncp); + stats->rx_packets++; + stats->rx_bytes += len; + u64_stats_update_end(&stats->syncp); + } + return 0; +drop: + /* Consume bad packet */ + kfree_skb(skb); + + return 0; +} + +static int bareudp_err_lookup(struct sock *sk, struct sk_buff *skb) +{ + return 0; +} + +static int bareudp_init(struct net_device *dev) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + int err; + + dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!dev->tstats) + return -ENOMEM; + + err = gro_cells_init(&bareudp->gro_cells, dev); + if (err) { + free_percpu(dev->tstats); + return err; + } + return 0; +} + +static void bareudp_uninit(struct net_device *dev) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + + gro_cells_destroy(&bareudp->gro_cells); + free_percpu(dev->tstats); +} + +static struct socket *bareudp_create_sock(struct net *net, __be16 port) +{ + struct udp_port_cfg udp_conf; + struct socket *sock; + int err; + + memset(&udp_conf, 0, sizeof(udp_conf)); +#if IS_ENABLED(CONFIG_IPV6) + udp_conf.family = AF_INET6; +#else + udp_conf.family = AF_INET; +#endif + udp_conf.local_udp_port = port; + /* Open UDP socket */ + err = udp_sock_create(net, &udp_conf, &sock); + if (err < 0) + return ERR_PTR(err); + + return sock; +} + +/* Create new listen socket if needed */ +static int bareudp_socket_create(struct bareudp_dev *bareudp, __be16 port) +{ + struct udp_tunnel_sock_cfg tunnel_cfg; + struct socket *sock; + + sock = bareudp_create_sock(bareudp->net, port); + if (IS_ERR(sock)) + return PTR_ERR(sock); + + /* Mark socket as an encapsulation socket */ + memset(&tunnel_cfg, 0, sizeof(tunnel_cfg)); + tunnel_cfg.sk_user_data = bareudp; + tunnel_cfg.encap_type = 1; + tunnel_cfg.encap_rcv = bareudp_udp_encap_recv; + tunnel_cfg.encap_err_lookup = bareudp_err_lookup; + tunnel_cfg.encap_destroy = NULL; + setup_udp_tunnel_sock(bareudp->net, sock, &tunnel_cfg); + + if (sock->sk->sk_family == AF_INET6) + udp_encap_enable(); + + rcu_assign_pointer(bareudp->sock, sock); + return 0; +} + +static int bareudp_open(struct net_device *dev) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + int ret = 0; + + ret = bareudp_socket_create(bareudp, bareudp->port); + return ret; +} + +static void bareudp_sock_release(struct bareudp_dev *bareudp) +{ + struct socket *sock; + + sock = bareudp->sock; + rcu_assign_pointer(bareudp->sock, NULL); + synchronize_net(); + udp_tunnel_sock_release(sock); +} + +static int bareudp_stop(struct net_device *dev) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + + bareudp_sock_release(bareudp); + return 0; +} + +static int bareudp_xmit_skb(struct sk_buff *skb, struct net_device *dev, + struct bareudp_dev *bareudp, + const struct ip_tunnel_info *info) +{ + bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev)); + bool use_cache = ip_tunnel_dst_cache_usable(skb, info); + struct socket *sock = rcu_dereference(bareudp->sock); + bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); + const struct ip_tunnel_key *key = &info->key; + struct rtable *rt; + __be16 sport, df; + int min_headroom; + __u8 tos, ttl; + __be32 saddr; + int err; + + if (!sock) + return -ESHUTDOWN; + + rt = ip_route_output_tunnel(skb, dev, bareudp->net, &saddr, info, + IPPROTO_UDP, use_cache); + + if (IS_ERR(rt)) + return PTR_ERR(rt); + + skb_tunnel_check_pmtu(skb, &rt->dst, + BAREUDP_IPV4_HLEN + info->options_len); + + sport = udp_flow_src_port(bareudp->net, skb, + bareudp->sport_min, USHRT_MAX, + true); + tos = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); + ttl = key->ttl; + df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; + skb_scrub_packet(skb, xnet); + + if (!skb_pull(skb, skb_network_offset(skb))) + goto free_dst; + + min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + + BAREUDP_BASE_HLEN + info->options_len + sizeof(struct iphdr); + + err = skb_cow_head(skb, min_headroom); + if (unlikely(err)) + goto free_dst; + + err = udp_tunnel_handle_offloads(skb, udp_sum); + if (err) + goto free_dst; + + skb_set_inner_protocol(skb, bareudp->ethertype); + udp_tunnel_xmit_skb(rt, sock->sk, skb, saddr, info->key.u.ipv4.dst, + tos, ttl, df, sport, bareudp->port, + !net_eq(bareudp->net, dev_net(bareudp->dev)), + !(info->key.tun_flags & TUNNEL_CSUM)); + return 0; + +free_dst: + dst_release(&rt->dst); + return err; +} + +#if IS_ENABLED(CONFIG_IPV6) +static int bareudp6_xmit_skb(struct sk_buff *skb, struct net_device *dev, + struct bareudp_dev *bareudp, + const struct ip_tunnel_info *info) +{ + bool xnet = !net_eq(bareudp->net, dev_net(bareudp->dev)); + bool use_cache = ip_tunnel_dst_cache_usable(skb, info); + struct socket *sock = rcu_dereference(bareudp->sock); + bool udp_sum = !!(info->key.tun_flags & TUNNEL_CSUM); + const struct ip_tunnel_key *key = &info->key; + struct dst_entry *dst = NULL; + struct in6_addr saddr, daddr; + int min_headroom; + __u8 prio, ttl; + __be16 sport; + int err; + + if (!sock) + return -ESHUTDOWN; + + dst = ip6_dst_lookup_tunnel(skb, dev, bareudp->net, sock, &saddr, info, + IPPROTO_UDP, use_cache); + if (IS_ERR(dst)) + return PTR_ERR(dst); + + skb_tunnel_check_pmtu(skb, dst, BAREUDP_IPV6_HLEN + info->options_len); + + sport = udp_flow_src_port(bareudp->net, skb, + bareudp->sport_min, USHRT_MAX, + true); + prio = ip_tunnel_ecn_encap(key->tos, ip_hdr(skb), skb); + ttl = key->ttl; + + skb_scrub_packet(skb, xnet); + + if (!skb_pull(skb, skb_network_offset(skb))) + goto free_dst; + + min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + + BAREUDP_BASE_HLEN + info->options_len + sizeof(struct iphdr); + + err = skb_cow_head(skb, min_headroom); + if (unlikely(err)) + goto free_dst; + + err = udp_tunnel_handle_offloads(skb, udp_sum); + if (err) + goto free_dst; + + daddr = info->key.u.ipv6.dst; + udp_tunnel6_xmit_skb(dst, sock->sk, skb, dev, + &saddr, &daddr, prio, ttl, + info->key.label, sport, bareudp->port, + !(info->key.tun_flags & TUNNEL_CSUM)); + return 0; + +free_dst: + dst_release(dst); + return err; +} +#endif + +static netdev_tx_t bareudp_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + struct ip_tunnel_info *info = NULL; + int err; + + if (skb->protocol != bareudp->ethertype) { + err = -EINVAL; + goto tx_error; + } + + info = skb_tunnel_info(skb); + if (unlikely(!info || !(info->mode & IP_TUNNEL_INFO_TX))) { + err = -EINVAL; + goto tx_error; + } + + rcu_read_lock(); +#if IS_ENABLED(CONFIG_IPV6) + if (info->mode & IP_TUNNEL_INFO_IPV6) + err = bareudp6_xmit_skb(skb, dev, bareudp, info); + else +#endif + err = bareudp_xmit_skb(skb, dev, bareudp, info); + + rcu_read_unlock(); + + if (likely(!err)) + return NETDEV_TX_OK; +tx_error: + dev_kfree_skb(skb); + + if (err == -ELOOP) + dev->stats.collisions++; + else if (err == -ENETUNREACH) + dev->stats.tx_carrier_errors++; + + dev->stats.tx_errors++; + return NETDEV_TX_OK; +} + +static int bareudp_fill_metadata_dst(struct net_device *dev, + struct sk_buff *skb) +{ + struct ip_tunnel_info *info = skb_tunnel_info(skb); + struct bareudp_dev *bareudp = netdev_priv(dev); + bool use_cache; + + use_cache = ip_tunnel_dst_cache_usable(skb, info); + + if (ip_tunnel_info_af(info) == AF_INET) { + struct rtable *rt; + __be32 saddr; + + rt = ip_route_output_tunnel(skb, dev, bareudp->net, &saddr, + info, IPPROTO_UDP, use_cache); + if (IS_ERR(rt)) + return PTR_ERR(rt); + + ip_rt_put(rt); + info->key.u.ipv4.src = saddr; +#if IS_ENABLED(CONFIG_IPV6) + } else if (ip_tunnel_info_af(info) == AF_INET6) { + struct dst_entry *dst; + struct in6_addr saddr; + struct socket *sock = rcu_dereference(bareudp->sock); + + dst = ip6_dst_lookup_tunnel(skb, dev, bareudp->net, sock, + &saddr, info, IPPROTO_UDP, + use_cache); + if (IS_ERR(dst)) + return PTR_ERR(dst); + + dst_release(dst); + info->key.u.ipv6.src = saddr; +#endif + } else { + return -EINVAL; + } + + info->key.tp_src = udp_flow_src_port(bareudp->net, skb, + bareudp->sport_min, + USHRT_MAX, true); + info->key.tp_dst = bareudp->port; + return 0; +} + +static const struct net_device_ops bareudp_netdev_ops = { + .ndo_init = bareudp_init, + .ndo_uninit = bareudp_uninit, + .ndo_open = bareudp_open, + .ndo_stop = bareudp_stop, + .ndo_start_xmit = bareudp_xmit, + .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_fill_metadata_dst = bareudp_fill_metadata_dst, +}; + +static const struct nla_policy bareudp_policy[IFLA_BAREUDP_MAX + 1] = { + [IFLA_BAREUDP_PORT] = { .type = NLA_U16 }, + [IFLA_BAREUDP_ETHERTYPE] = { .type = NLA_U16 }, + [IFLA_BAREUDP_SRCPORT_MIN] = { .type = NLA_U16 }, +}; + +/* Info for udev, that this is a virtual tunnel endpoint */ +static struct device_type bareudp_type = { + .name = "bareudp", +}; + +/* Initialize the device structure. */ +static void bareudp_setup(struct net_device *dev) +{ + dev->netdev_ops = &bareudp_netdev_ops; + dev->needs_free_netdev = true; + SET_NETDEV_DEVTYPE(dev, &bareudp_type); + dev->features |= NETIF_F_SG | NETIF_F_HW_CSUM; + dev->features |= NETIF_F_RXCSUM; + dev->features |= NETIF_F_GSO_SOFTWARE; + dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM; + dev->hw_features |= NETIF_F_GSO_SOFTWARE; + dev->hard_header_len = 0; + dev->addr_len = 0; + dev->mtu = ETH_DATA_LEN; + dev->min_mtu = IPV4_MIN_MTU; + dev->max_mtu = IP_MAX_MTU - BAREUDP_BASE_HLEN; + dev->type = ARPHRD_NONE; + netif_keep_dst(dev); + dev->priv_flags |= IFF_NO_QUEUE; + dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; +} + +static int bareudp_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + if (!data) { + NL_SET_ERR_MSG(extack, + "Not enough attributes provided to perform the operation"); + return -EINVAL; + } + return 0; +} + +static int bareudp2info(struct nlattr *data[], struct bareudp_conf *conf) +{ + if (!data[IFLA_BAREUDP_PORT] || !data[IFLA_BAREUDP_ETHERTYPE]) + return -EINVAL; + + if (data[IFLA_BAREUDP_PORT]) + conf->port = nla_get_u16(data[IFLA_BAREUDP_PORT]); + + if (data[IFLA_BAREUDP_ETHERTYPE]) + conf->ethertype = nla_get_u16(data[IFLA_BAREUDP_ETHERTYPE]); + + if (data[IFLA_BAREUDP_SRCPORT_MIN]) + conf->sport_min = nla_get_u16(data[IFLA_BAREUDP_SRCPORT_MIN]); + + return 0; +} + +static struct bareudp_dev *bareudp_find_dev(struct bareudp_net *bn, + const struct bareudp_conf *conf) +{ + struct bareudp_dev *bareudp, *t = NULL; + + list_for_each_entry(bareudp, &bn->bareudp_list, next) { + if (conf->port == bareudp->port) + t = bareudp; + } + return t; +} + +static int bareudp_configure(struct net *net, struct net_device *dev, + struct bareudp_conf *conf) +{ + struct bareudp_net *bn = net_generic(net, bareudp_net_id); + struct bareudp_dev *t, *bareudp = netdev_priv(dev); + int err; + + bareudp->net = net; + bareudp->dev = dev; + t = bareudp_find_dev(bn, conf); + if (t) + return -EBUSY; + + bareudp->port = conf->port; + bareudp->ethertype = conf->ethertype; + bareudp->sport_min = conf->sport_min; + err = register_netdevice(dev); + if (err) + return err; + + list_add(&bareudp->next, &bn->bareudp_list); + return 0; +} + +static int bareudp_link_config(struct net_device *dev, + struct nlattr *tb[]) +{ + int err; + + if (tb[IFLA_MTU]) { + err = dev_set_mtu(dev, nla_get_u32(tb[IFLA_MTU])); + if (err) + return err; + } + return 0; +} + +static int bareudp_newlink(struct net *net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + struct bareudp_conf conf; + int err; + + err = bareudp2info(data, &conf); + if (err) + return err; + + err = bareudp_configure(net, dev, &conf); + if (err) + return err; + + err = bareudp_link_config(dev, tb); + if (err) + return err; + + return 0; +} + +static void bareudp_dellink(struct net_device *dev, struct list_head *head) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + + list_del(&bareudp->next); + unregister_netdevice_queue(dev, head); +} + +static size_t bareudp_get_size(const struct net_device *dev) +{ + return nla_total_size(sizeof(__be16)) + /* IFLA_BAREUDP_PORT */ + nla_total_size(sizeof(__be16)) + /* IFLA_BAREUDP_ETHERTYPE */ + nla_total_size(sizeof(__u16)) + /* IFLA_BAREUDP_SRCPORT_MIN */ + 0; +} + +static int bareudp_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct bareudp_dev *bareudp = netdev_priv(dev); + + if (nla_put_be16(skb, IFLA_BAREUDP_PORT, bareudp->port)) + goto nla_put_failure; + if (nla_put_be16(skb, IFLA_BAREUDP_ETHERTYPE, bareudp->ethertype)) + goto nla_put_failure; + if (nla_put_u16(skb, IFLA_BAREUDP_SRCPORT_MIN, bareudp->sport_min)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static struct rtnl_link_ops bareudp_link_ops __read_mostly = { + .kind = "bareudp", + .maxtype = IFLA_BAREUDP_MAX, + .policy = bareudp_policy, + .priv_size = sizeof(struct bareudp_dev), + .setup = bareudp_setup, + .validate = bareudp_validate, + .newlink = bareudp_newlink, + .dellink = bareudp_dellink, + .get_size = bareudp_get_size, + .fill_info = bareudp_fill_info, +}; + +struct net_device *bareudp_dev_create(struct net *net, const char *name, + u8 name_assign_type, + struct bareudp_conf *conf) +{ + struct nlattr *tb[IFLA_MAX + 1]; + struct net_device *dev; + LIST_HEAD(list_kill); + int err; + + memset(tb, 0, sizeof(tb)); + dev = rtnl_create_link(net, name, name_assign_type, + &bareudp_link_ops, tb, NULL); + if (IS_ERR(dev)) + return dev; + + err = bareudp_configure(net, dev, conf); + if (err) { + free_netdev(dev); + return ERR_PTR(err); + } + err = dev_set_mtu(dev, IP_MAX_MTU - BAREUDP_BASE_HLEN); + if (err) + goto err; + + err = rtnl_configure_link(dev, NULL); + if (err < 0) + goto err; + + return dev; +err: + bareudp_dellink(dev, &list_kill); + unregister_netdevice_many(&list_kill); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(bareudp_dev_create); + +static __net_init int bareudp_init_net(struct net *net) +{ + struct bareudp_net *bn = net_generic(net, bareudp_net_id); + + INIT_LIST_HEAD(&bn->bareudp_list); + return 0; +} + +static void bareudp_destroy_tunnels(struct net *net, struct list_head *head) +{ + struct bareudp_net *bn = net_generic(net, bareudp_net_id); + struct bareudp_dev *bareudp, *next; + + list_for_each_entry_safe(bareudp, next, &bn->bareudp_list, next) + unregister_netdevice_queue(bareudp->dev, head); +} + +static void __net_exit bareudp_exit_batch_net(struct list_head *net_list) +{ + struct net *net; + LIST_HEAD(list); + + rtnl_lock(); + list_for_each_entry(net, net_list, exit_list) + bareudp_destroy_tunnels(net, &list); + + /* unregister the devices gathered above */ + unregister_netdevice_many(&list); + rtnl_unlock(); +} + +static struct pernet_operations bareudp_net_ops = { + .init = bareudp_init_net, + .exit_batch = bareudp_exit_batch_net, + .id = &bareudp_net_id, + .size = sizeof(struct bareudp_net), +}; + +static int __init bareudp_init_module(void) +{ + int rc; + + rc = register_pernet_subsys(&bareudp_net_ops); + if (rc) + goto out1; + + rc = rtnl_link_register(&bareudp_link_ops); + if (rc) + goto out2; + + return 0; +out2: + unregister_pernet_subsys(&bareudp_net_ops); +out1: + return rc; +} +late_initcall(bareudp_init_module); + +static void __exit bareudp_cleanup_module(void) +{ + rtnl_link_unregister(&bareudp_link_ops); + unregister_pernet_subsys(&bareudp_net_ops); +} +module_exit(bareudp_cleanup_module); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Martin Varghese "); +MODULE_DESCRIPTION("Interface driver for UDP encapsulated traffic"); diff --git a/include/net/bareudp.h b/include/net/bareudp.h new file mode 100644 index 000000000000..513fae6f6ba1 --- /dev/null +++ b/include/net/bareudp.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __NET_BAREUDP_H +#define __NET_BAREUDP_H + +#include +#include + +struct bareudp_conf { + __be16 ethertype; + __be16 port; + u16 sport_min; +}; + +struct net_device *bareudp_dev_create(struct net *net, const char *name, + u8 name_assign_type, + struct bareudp_conf *info); + +#endif diff --git a/include/net/ipv6.h b/include/net/ipv6.h index cec1a54401f2..1bf8065fe871 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -1027,6 +1027,12 @@ struct dst_entry *ip6_dst_lookup_flow(struct net *net, const struct sock *sk, st struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, const struct in6_addr *final_dst, bool connected); +struct dst_entry *ip6_dst_lookup_tunnel(struct sk_buff *skb, + struct net_device *dev, + struct net *net, struct socket *sock, + struct in6_addr *saddr, + const struct ip_tunnel_info *info, + u8 protocol, bool use_cache); struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *orig_dst); diff --git a/include/net/route.h b/include/net/route.h index a9c60fc68e36..81750ae50833 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -128,6 +128,12 @@ static inline struct rtable *__ip_route_output_key(struct net *net, struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, const struct sock *sk); +struct rtable *ip_route_output_tunnel(struct sk_buff *skb, + struct net_device *dev, + struct net *net, __be32 *saddr, + const struct ip_tunnel_info *info, + u8 protocol, bool use_cache); + struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 024af2d1d0af..fb4b33af23d5 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -590,6 +590,17 @@ enum ifla_geneve_df { GENEVE_DF_MAX = __GENEVE_DF_END - 1, }; +/* Bareudp section */ +enum { + IFLA_BAREUDP_UNSPEC, + IFLA_BAREUDP_PORT, + IFLA_BAREUDP_ETHERTYPE, + IFLA_BAREUDP_SRCPORT_MIN, + __IFLA_BAREUDP_MAX +}; + +#define IFLA_BAREUDP_MAX (__IFLA_BAREUDP_MAX - 1) + /* PPP section */ enum { IFLA_PPP_UNSPEC, diff --git a/net/ipv4/route.c b/net/ipv4/route.c index ebe7060d0fc9..042599cc691d 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2774,6 +2774,54 @@ struct rtable *ip_route_output_flow(struct net *net, struct flowi4 *flp4, } EXPORT_SYMBOL_GPL(ip_route_output_flow); +struct rtable *ip_route_output_tunnel(struct sk_buff *skb, + struct net_device *dev, + struct net *net, __be32 *saddr, + const struct ip_tunnel_info *info, + u8 protocol, bool use_cache) +{ +#ifdef CONFIG_DST_CACHE + struct dst_cache *dst_cache; +#endif + struct rtable *rt = NULL; + struct flowi4 fl4; + __u8 tos; + +#ifdef CONFIG_DST_CACHE + dst_cache = (struct dst_cache *)&info->dst_cache; + if (use_cache) { + rt = dst_cache_get_ip4(dst_cache, saddr); + if (rt) + return rt; + } +#endif + memset(&fl4, 0, sizeof(fl4)); + fl4.flowi4_mark = skb->mark; + fl4.flowi4_proto = protocol; + fl4.daddr = info->key.u.ipv4.dst; + fl4.saddr = info->key.u.ipv4.src; + tos = info->key.tos; + fl4.flowi4_tos = RT_TOS(tos); + + rt = ip_route_output_key(net, &fl4); + if (IS_ERR(rt)) { + netdev_dbg(dev, "no route to %pI4\n", &fl4.daddr); + return ERR_PTR(-ENETUNREACH); + } + if (rt->dst.dev == dev) { /* is this necessary? */ + netdev_dbg(dev, "circular route to %pI4\n", &fl4.daddr); + ip_rt_put(rt); + return ERR_PTR(-ELOOP); + } +#ifdef CONFIG_DST_CACHE + if (use_cache) + dst_cache_set_ip4(dst_cache, &rt->dst, fl4.saddr); +#endif + *saddr = fl4.saddr; + return rt; +} +EXPORT_SYMBOL_GPL(ip_route_output_tunnel); + /* called with rcu_read_lock held */ static int rt_fill_info(struct net *net, __be32 dst, __be32 src, struct rtable *rt, u32 table_id, struct flowi4 *fl4, diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 087304427bbb..8a8c2d0cfcc8 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -54,6 +54,7 @@ #include #include #include +#include static int ip6_finish_output2(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -1196,6 +1197,75 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, } EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); +/** + * ip6_dst_lookup_tunnel - perform route lookup on tunnel + * @skb: Packet for which lookup is done + * @dev: Tunnel device + * @net: Network namespace of tunnel device + * @sk: Socket which provides route info + * @saddr: Memory to store the src ip address + * @info: Tunnel information + * @protocol: IP protocol + * @use_cahce: Flag to enable cache usage + * This function performs a route lookup on a tunnel + * + * It returns a valid dst pointer and stores src address to be used in + * tunnel in param saddr on success, else a pointer encoded error code. + */ + +struct dst_entry *ip6_dst_lookup_tunnel(struct sk_buff *skb, + struct net_device *dev, + struct net *net, + struct socket *sock, + struct in6_addr *saddr, + const struct ip_tunnel_info *info, + u8 protocol, + bool use_cache) +{ + struct dst_entry *dst = NULL; +#ifdef CONFIG_DST_CACHE + struct dst_cache *dst_cache; +#endif + struct flowi6 fl6; + __u8 prio; + +#ifdef CONFIG_DST_CACHE + dst_cache = (struct dst_cache *)&info->dst_cache; + if (use_cache) { + dst = dst_cache_get_ip6(dst_cache, saddr); + if (dst) + return dst; + } +#endif + memset(&fl6, 0, sizeof(fl6)); + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = protocol; + fl6.daddr = info->key.u.ipv6.dst; + fl6.saddr = info->key.u.ipv6.src; + prio = info->key.tos; + fl6.flowlabel = ip6_make_flowinfo(RT_TOS(prio), + info->key.label); + + dst = ipv6_stub->ipv6_dst_lookup_flow(net, sock->sk, &fl6, + NULL); + if (IS_ERR(dst)) { + netdev_dbg(dev, "no route to %pI6\n", &fl6.daddr); + return ERR_PTR(-ENETUNREACH); + } + if (dst->dev == dev) { /* is this necessary? */ + netdev_dbg(dev, "circular route to %pI6\n", &fl6.daddr); + dst_release(dst); + return ERR_PTR(-ELOOP); + } +#ifdef CONFIG_DST_CACHE + if (use_cache) + dst_cache_set_ip6(dst_cache, dst, &fl6.saddr); +#endif + *saddr = fl6.saddr; + return dst; +} +EXPORT_SYMBOL_GPL(ip6_dst_lookup_tunnel); + static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src, gfp_t gfp) { -- cgit v1.2.3 From 4b5f67232d956a5f837082578ba682410ccab498 Mon Sep 17 00:00:00 2001 From: Martin Varghese Date: Mon, 24 Feb 2020 10:58:35 +0530 Subject: net: Special handling for IP & MPLS. Special handling is needed in bareudp module for IP & MPLS as they support more than one ethertypes. MPLS has 2 ethertypes. 0x8847 for MPLS unicast and 0x8848 for MPLS multicast. While decapsulating MPLS packet from UDP packet the tunnel destination IP address is checked to determine the ethertype. The ethertype of the packet will be set to 0x8848 if the tunnel destination IP address is a multicast IP address. The ethertype of the packet will be set to 0x8847 if the tunnel destination IP address is a unicast IP address. IP has 2 ethertypes.0x0800 for IPV4 and 0x86dd for IPv6. The version field of the IP header tunnelled will be checked to determine the ethertype. This special handling to tunnel additional ethertypes will be disabled by default and can be enabled using a flag called multiproto. This flag can be used only with ethertypes 0x8847 and 0x0800. Signed-off-by: Martin Varghese Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- Documentation/networking/bareudp.rst | 20 ++++++++++- drivers/net/bareudp.c | 67 ++++++++++++++++++++++++++++++++++-- include/net/bareudp.h | 1 + include/uapi/linux/if_link.h | 1 + 4 files changed, 85 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/networking/bareudp.rst b/Documentation/networking/bareudp.rst index 6ee68c16cf5b..465a8b251bfe 100644 --- a/Documentation/networking/bareudp.rst +++ b/Documentation/networking/bareudp.rst @@ -12,6 +12,15 @@ The Bareudp tunnel module provides a generic L3 encapsulation tunnelling support for tunnelling different L3 protocols like MPLS, IP, NSH etc. inside a UDP tunnel. +Special Handling +---------------- +The bareudp device supports special handling for MPLS & IP as they can have +multiple ethertypes. +MPLS procotcol can have ethertypes ETH_P_MPLS_UC (unicast) & ETH_P_MPLS_MC (multicast). +IP protocol can have ethertypes ETH_P_IP (v4) & ETH_P_IPV6 (v6). +This special handling can be enabled only for ethertypes ETH_P_IP & ETH_P_MPLS_UC +with a flag called multiproto mode. + Usage ------ @@ -25,7 +34,16 @@ Usage b) ip link delete bareudp0 -2) Device Usage +2) Device creation with multiple proto mode enabled + +There are two ways to create a bareudp device for MPLS & IP with multiproto mode +enabled. + + a) ip link add dev bareudp0 type bareudp dstport 6635 ethertype 0x8847 multiproto + + b) ip link add dev bareudp0 type bareudp dstport 6635 ethertype mpls + +3) Device Usage The bareudp device could be used along with OVS or flower filter in TC. The OVS or TC flower layer must set the tunnel information in SKB dst field before diff --git a/drivers/net/bareudp.c b/drivers/net/bareudp.c index 32518969139e..77e72477499d 100644 --- a/drivers/net/bareudp.c +++ b/drivers/net/bareudp.c @@ -45,6 +45,7 @@ struct bareudp_dev { __be16 ethertype; __be16 port; u16 sport_min; + bool multi_proto_mode; struct socket __rcu *sock; struct list_head next; /* bareudp node on namespace list */ struct gro_cells gro_cells; @@ -70,7 +71,52 @@ static int bareudp_udp_encap_recv(struct sock *sk, struct sk_buff *skb) else family = AF_INET6; - proto = bareudp->ethertype; + if (bareudp->ethertype == htons(ETH_P_IP)) { + struct iphdr *iphdr; + + iphdr = (struct iphdr *)(skb->data + BAREUDP_BASE_HLEN); + if (iphdr->version == 4) { + proto = bareudp->ethertype; + } else if (bareudp->multi_proto_mode && (iphdr->version == 6)) { + proto = htons(ETH_P_IPV6); + } else { + bareudp->dev->stats.rx_dropped++; + goto drop; + } + } else if (bareudp->ethertype == htons(ETH_P_MPLS_UC)) { + struct iphdr *tunnel_hdr; + + tunnel_hdr = (struct iphdr *)skb_network_header(skb); + if (tunnel_hdr->version == 4) { + if (!ipv4_is_multicast(tunnel_hdr->daddr)) { + proto = bareudp->ethertype; + } else if (bareudp->multi_proto_mode && + ipv4_is_multicast(tunnel_hdr->daddr)) { + proto = htons(ETH_P_MPLS_MC); + } else { + bareudp->dev->stats.rx_dropped++; + goto drop; + } + } else { + int addr_type; + struct ipv6hdr *tunnel_hdr_v6; + + tunnel_hdr_v6 = (struct ipv6hdr *)skb_network_header(skb); + addr_type = + ipv6_addr_type((struct in6_addr *)&tunnel_hdr_v6->daddr); + if (!(addr_type & IPV6_ADDR_MULTICAST)) { + proto = bareudp->ethertype; + } else if (bareudp->multi_proto_mode && + (addr_type & IPV6_ADDR_MULTICAST)) { + proto = htons(ETH_P_MPLS_MC); + } else { + bareudp->dev->stats.rx_dropped++; + goto drop; + } + } + } else { + proto = bareudp->ethertype; + } if (iptunnel_pull_header(skb, BAREUDP_BASE_HLEN, proto, @@ -369,8 +415,12 @@ static netdev_tx_t bareudp_xmit(struct sk_buff *skb, struct net_device *dev) int err; if (skb->protocol != bareudp->ethertype) { - err = -EINVAL; - goto tx_error; + if (!bareudp->multi_proto_mode || + (skb->protocol != htons(ETH_P_MPLS_MC) && + skb->protocol != htons(ETH_P_IPV6))) { + err = -EINVAL; + goto tx_error; + } } info = skb_tunnel_info(skb); @@ -463,6 +513,7 @@ static const struct nla_policy bareudp_policy[IFLA_BAREUDP_MAX + 1] = { [IFLA_BAREUDP_PORT] = { .type = NLA_U16 }, [IFLA_BAREUDP_ETHERTYPE] = { .type = NLA_U16 }, [IFLA_BAREUDP_SRCPORT_MIN] = { .type = NLA_U16 }, + [IFLA_BAREUDP_MULTIPROTO_MODE] = { .type = NLA_FLAG }, }; /* Info for udev, that this is a virtual tunnel endpoint */ @@ -545,9 +596,15 @@ static int bareudp_configure(struct net *net, struct net_device *dev, if (t) return -EBUSY; + if (conf->multi_proto_mode && + (conf->ethertype != htons(ETH_P_MPLS_UC) && + conf->ethertype != htons(ETH_P_IP))) + return -EINVAL; + bareudp->port = conf->port; bareudp->ethertype = conf->ethertype; bareudp->sport_min = conf->sport_min; + bareudp->multi_proto_mode = conf->multi_proto_mode; err = register_netdevice(dev); if (err) return err; @@ -604,6 +661,7 @@ static size_t bareudp_get_size(const struct net_device *dev) return nla_total_size(sizeof(__be16)) + /* IFLA_BAREUDP_PORT */ nla_total_size(sizeof(__be16)) + /* IFLA_BAREUDP_ETHERTYPE */ nla_total_size(sizeof(__u16)) + /* IFLA_BAREUDP_SRCPORT_MIN */ + nla_total_size(0) + /* IFLA_BAREUDP_MULTIPROTO_MODE */ 0; } @@ -617,6 +675,9 @@ static int bareudp_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; if (nla_put_u16(skb, IFLA_BAREUDP_SRCPORT_MIN, bareudp->sport_min)) goto nla_put_failure; + if (bareudp->multi_proto_mode && + nla_put_flag(skb, IFLA_BAREUDP_MULTIPROTO_MODE)) + goto nla_put_failure; return 0; diff --git a/include/net/bareudp.h b/include/net/bareudp.h index 513fae6f6ba1..cb03f6f15956 100644 --- a/include/net/bareudp.h +++ b/include/net/bareudp.h @@ -10,6 +10,7 @@ struct bareudp_conf { __be16 ethertype; __be16 port; u16 sport_min; + bool multi_proto_mode; }; struct net_device *bareudp_dev_create(struct net *net, const char *name, diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index fb4b33af23d5..61e0801c82df 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -596,6 +596,7 @@ enum { IFLA_BAREUDP_PORT, IFLA_BAREUDP_ETHERTYPE, IFLA_BAREUDP_SRCPORT_MIN, + IFLA_BAREUDP_MULTIPROTO_MODE, __IFLA_BAREUDP_MAX }; -- cgit v1.2.3 From d2aaa8d8bfba93237ac944ee058fb98e2c2ef983 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 20 Feb 2020 11:49:55 +0200 Subject: ASoC: soc-pcm: fix state tracking error in snd_soc_component_open/close() ASoC component open/close and snd_soc_component_module_get/put are called independently for each component-substream pair, so the logic added in commit dd03907bf129 ("ASoC: soc-pcm: call snd_soc_component_open/close() once") was not sufficient and led to PCM playback and module unload errors. Implement handling of failures directly in soc_pcm_components_open(), so that any successfully opened components are closed upon error with other components. This allows to clean up error handling in soc_pcm_open() without adding more state tracking. Fixes: dd03907bf129 ("ASoC: soc-pcm: call snd_soc_component_open/close() once") Signed-off-by: Kai Vehmanen Tested-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20200220094955.16968-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 7 ++----- sound/soc/soc-component.c | 35 +++++++---------------------------- sound/soc/soc-pcm.c | 27 +++++++++++++++++++++------ 3 files changed, 30 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index 1866ecc8e94b..154d02fbbfed 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -147,6 +147,8 @@ struct snd_soc_component { unsigned int active; + unsigned int suspended:1; /* is in suspend PM state */ + struct list_head list; struct list_head card_aux_list; /* for auxiliary bound components */ struct list_head card_list; @@ -180,11 +182,6 @@ struct snd_soc_component { struct dentry *debugfs_root; const char *debugfs_prefix; #endif - - /* bit field */ - unsigned int suspended:1; /* is in suspend PM state */ - unsigned int opened:1; - unsigned int module:1; }; #define for_each_component_dais(component, dai)\ diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index ee00c09df5e7..14e175cdeeb8 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -297,55 +297,34 @@ EXPORT_SYMBOL_GPL(snd_soc_component_set_jack); int snd_soc_component_module_get(struct snd_soc_component *component, int upon_open) { - if (component->module) - return 0; - if (component->driver->module_get_upon_open == !!upon_open && !try_module_get(component->dev->driver->owner)) return -ENODEV; - component->module = 1; - return 0; } void snd_soc_component_module_put(struct snd_soc_component *component, int upon_open) { - if (component->module && - component->driver->module_get_upon_open == !!upon_open) + if (component->driver->module_get_upon_open == !!upon_open) module_put(component->dev->driver->owner); - - component->module = 0; } int snd_soc_component_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - int ret = 0; - - if (!component->opened && - component->driver->open) - ret = component->driver->open(component, substream); - - if (ret == 0) - component->opened = 1; - - return ret; + if (component->driver->open) + return component->driver->open(component, substream); + return 0; } int snd_soc_component_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { - int ret = 0; - - if (component->opened && - component->driver->close) - ret = component->driver->close(component, substream); - - component->opened = 0; - - return ret; + if (component->driver->close) + return component->driver->close(component, substream); + return 0; } int snd_soc_component_prepare(struct snd_soc_component *component, diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index aff27c8599ef..235baeb2d56a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -469,28 +469,43 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) static int soc_pcm_components_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *last = NULL; struct snd_soc_component *component; int i, ret = 0; for_each_rtd_components(rtd, i, component) { + last = component; + ret = snd_soc_component_module_get_when_open(component); if (ret < 0) { dev_err(component->dev, "ASoC: can't get module %s\n", component->name); - return ret; + break; } ret = snd_soc_component_open(component, substream); if (ret < 0) { + snd_soc_component_module_put_when_close(component); dev_err(component->dev, "ASoC: can't open component %s: %d\n", component->name, ret); - return ret; + break; } } - return 0; + if (ret < 0) { + /* rollback on error */ + for_each_rtd_components(rtd, i, component) { + if (component == last) + break; + + snd_soc_component_close(component, substream); + snd_soc_component_module_put_when_close(component); + } + } + + return ret; } static int soc_pcm_components_close(struct snd_pcm_substream *substream) @@ -585,7 +600,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (ret < 0) { pr_err("ASoC: %s startup failed: %d\n", rtd->dai_link->name, ret); - goto component_err; + goto rtd_startup_err; } /* startup the audio subsystem */ @@ -681,9 +696,9 @@ cpu_dai_err: snd_soc_dai_shutdown(cpu_dai, substream); soc_rtd_shutdown(rtd, substream); -component_err: +rtd_startup_err: soc_pcm_components_close(substream); - +component_err: mutex_unlock(&rtd->card->pcm_mutex); for_each_rtd_components(rtd, i, component) { -- cgit v1.2.3 From ce69e2162f158d9d4a0e513971d02dabc7d14cb7 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 24 Feb 2020 16:53:58 -0600 Subject: mdio_bus: Add generic mdio_find_bus() It appears most ethernet drivers follow one of two main strategies for mdio bus/phy management. A monolithic model where the net driver itself creates, probes and uses the phy, and one where an external mdio/phy driver instantiates the mdio bus/phy and the net driver only attaches to a known phy. Usually in this latter model the phys are discovered via DT relationships or simply phy name/address hardcoding. This is a shame because modern well behaved mdio buses are self describing and can be probed. The mdio layer itself is fully capable of this, yet there isn't a clean way for a standalone net driver to attach and enumerate the discovered devices. This is because outside of of_mdio_find_bus() there isn't a straightforward way to acquire the mii_bus pointer. So, lets add a mdio_find_bus which can return the mii_bus based only on its name. Signed-off-by: Jeremy Linton Acked-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/mdio_bus.c | 17 +++++++++++++++++ include/linux/phy.h | 1 + 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 9bb9f37f21dc..3ab9ca7614d1 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -462,6 +462,23 @@ static struct class mdio_bus_class = { .dev_groups = mdio_bus_groups, }; +/** + * mdio_find_bus - Given the name of a mdiobus, find the mii_bus. + * @mdio_bus_np: Pointer to the mii_bus. + * + * Returns a reference to the mii_bus, or NULL if none found. The + * embedded struct device will have its reference count incremented, + * and this must be put_deviced'ed once the bus is finished with. + */ +struct mii_bus *mdio_find_bus(const char *mdio_name) +{ + struct device *d; + + d = class_find_device_by_name(&mdio_bus_class, mdio_name); + return d ? to_mii_bus(d) : NULL; +} +EXPORT_SYMBOL(mdio_find_bus); + #if IS_ENABLED(CONFIG_OF_MDIO) /** * of_mdio_find_bus - Given an mii_bus node, find the mii_bus. diff --git a/include/linux/phy.h b/include/linux/phy.h index 80f8b2158271..e72dbd0d2d6a 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -289,6 +289,7 @@ static inline struct mii_bus *devm_mdiobus_alloc(struct device *dev) return devm_mdiobus_alloc_size(dev, 0); } +struct mii_bus *mdio_find_bus(const char *mdio_name); void devm_mdiobus_free(struct device *dev, struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); -- cgit v1.2.3 From 3c58482a382bae89410439247152eb342e9872f7 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 24 Feb 2020 15:01:41 +0100 Subject: bpf: Provide bpf_prog_run_pin_on_cpu() helper BPF programs require to run on one CPU to completion as they use per CPU storage, but according to Alexei they don't need reentrancy protection as obviously BPF programs running in thread context can always be 'preempted' by hard and soft interrupts and instrumentation and the same program can run concurrently on a different CPU. The currently used mechanism to ensure CPUness is to wrap the invocation into a preempt_disable/enable() pair. Disabling preemption is also disabling migration for a task. preempt_disable/enable() is used because there is no explicit way to reliably disable only migration. Provide a separate macro to invoke a BPF program which can be used in migrateable task context. It wraps BPF_PROG_RUN() in a migrate_disable/enable() pair which maps on non RT enabled kernels to preempt_disable/enable(). On RT enabled kernels this merely disables migration. Both methods ensure that the invoked BPF program runs on one CPU to completion. Signed-off-by: Thomas Gleixner Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200224145643.474592620@linutronix.de --- include/linux/filter.h | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index f349e2c0884c..38f60188bb26 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -576,8 +576,30 @@ DECLARE_STATIC_KEY_FALSE(bpf_stats_enabled_key); } \ ret; }) -#define BPF_PROG_RUN(prog, ctx) __BPF_PROG_RUN(prog, ctx, \ - bpf_dispatcher_nopfunc) +#define BPF_PROG_RUN(prog, ctx) \ + __BPF_PROG_RUN(prog, ctx, bpf_dispatcher_nopfunc) + +/* + * Use in preemptible and therefore migratable context to make sure that + * the execution of the BPF program runs on one CPU. + * + * This uses migrate_disable/enable() explicitly to document that the + * invocation of a BPF program does not require reentrancy protection + * against a BPF program which is invoked from a preempting task. + * + * For non RT enabled kernels migrate_disable/enable() maps to + * preempt_disable/enable(), i.e. it disables also preemption. + */ +static inline u32 bpf_prog_run_pin_on_cpu(const struct bpf_prog *prog, + const void *ctx) +{ + u32 ret; + + migrate_disable(); + ret = __BPF_PROG_RUN(prog, ctx, bpf_dispatcher_nopfunc); + migrate_enable(); + return ret; +} #define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN -- cgit v1.2.3 From 37e1d9202225635772b32e340294208367279c2b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 24 Feb 2020 15:01:42 +0100 Subject: bpf: Replace cant_sleep() with cant_migrate() As already discussed in the previous change which introduced BPF_RUN_PROG_PIN_ON_CPU() BPF only requires to disable migration to guarantee per CPUness. If RT substitutes the preempt disable based migration protection then the cant_sleep() check will obviously trigger as preemption is not disabled. Replace it by cant_migrate() which maps to cant_sleep() on a non RT kernel and will verify that migration is disabled on a full RT kernel. Signed-off-by: Thomas Gleixner Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200224145643.583038889@linutronix.de --- include/linux/filter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 38f60188bb26..1982a52eb4c9 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -561,7 +561,7 @@ DECLARE_STATIC_KEY_FALSE(bpf_stats_enabled_key); #define __BPF_PROG_RUN(prog, ctx, dfunc) ({ \ u32 ret; \ - cant_sleep(); \ + cant_migrate(); \ if (static_branch_unlikely(&bpf_stats_enabled_key)) { \ struct bpf_prog_stats *stats; \ u64 start = sched_clock(); \ -- cgit v1.2.3 From 3d9f773cf2876c01a505b9fe27270901d464e90a Mon Sep 17 00:00:00 2001 From: David Miller Date: Mon, 24 Feb 2020 15:01:43 +0100 Subject: bpf: Use bpf_prog_run_pin_on_cpu() at simple call sites. All of these cases are strictly of the form: preempt_disable(); BPF_PROG_RUN(...); preempt_enable(); Replace this with bpf_prog_run_pin_on_cpu() which wraps BPF_PROG_RUN() with: migrate_disable(); BPF_PROG_RUN(...); migrate_enable(); On non RT enabled kernels this maps to preempt_disable/enable() and on RT enabled kernels this solely prevents migration, which is sufficient as there is no requirement to prevent reentrancy to any BPF program from a preempting task. The only requirement is that the program stays on the same CPU. Therefore, this is a trivially correct transformation. The seccomp loop does not need protection over the loop. It only needs protection per BPF filter program [ tglx: Converted to bpf_prog_run_pin_on_cpu() ] Signed-off-by: David S. Miller Signed-off-by: Thomas Gleixner Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200224145643.691493094@linutronix.de --- include/linux/filter.h | 4 +--- kernel/seccomp.c | 4 +--- net/core/flow_dissector.c | 4 +--- net/core/skmsg.c | 8 ++------ net/kcm/kcmsock.c | 4 +--- 5 files changed, 6 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index 1982a52eb4c9..9270de2a0df8 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -717,9 +717,7 @@ static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog, if (unlikely(prog->cb_access)) memset(cb_data, 0, BPF_SKB_CB_LEN); - preempt_disable(); - res = BPF_PROG_RUN(prog, skb); - preempt_enable(); + res = bpf_prog_run_pin_on_cpu(prog, skb); return res; } diff --git a/kernel/seccomp.c b/kernel/seccomp.c index b6ea3dcb57bf..787041eb011b 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -268,16 +268,14 @@ static u32 seccomp_run_filters(const struct seccomp_data *sd, * All filters in the list are evaluated and the lowest BPF return * value always takes priority (ignoring the DATA). */ - preempt_disable(); for (; f; f = f->prev) { - u32 cur_ret = BPF_PROG_RUN(f->prog, sd); + u32 cur_ret = bpf_prog_run_pin_on_cpu(f->prog, sd); if (ACTION_ONLY(cur_ret) < ACTION_ONLY(ret)) { ret = cur_ret; *match = f; } } - preempt_enable(); return ret; } #endif /* CONFIG_SECCOMP_FILTER */ diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index a1670dff0629..3eff84824c8b 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -920,9 +920,7 @@ bool bpf_flow_dissect(struct bpf_prog *prog, struct bpf_flow_dissector *ctx, (int)FLOW_DISSECTOR_F_STOP_AT_ENCAP); flow_keys->flags = flags; - preempt_disable(); - result = BPF_PROG_RUN(prog, ctx); - preempt_enable(); + result = bpf_prog_run_pin_on_cpu(prog, ctx); flow_keys->nhoff = clamp_t(u16, flow_keys->nhoff, nhoff, hlen); flow_keys->thoff = clamp_t(u16, flow_keys->thoff, diff --git a/net/core/skmsg.c b/net/core/skmsg.c index eeb28cb85664..c479372f2cd2 100644 --- a/net/core/skmsg.c +++ b/net/core/skmsg.c @@ -628,7 +628,6 @@ int sk_psock_msg_verdict(struct sock *sk, struct sk_psock *psock, struct bpf_prog *prog; int ret; - preempt_disable(); rcu_read_lock(); prog = READ_ONCE(psock->progs.msg_parser); if (unlikely(!prog)) { @@ -638,7 +637,7 @@ int sk_psock_msg_verdict(struct sock *sk, struct sk_psock *psock, sk_msg_compute_data_pointers(msg); msg->sk = sk; - ret = BPF_PROG_RUN(prog, msg); + ret = bpf_prog_run_pin_on_cpu(prog, msg); ret = sk_psock_map_verd(ret, msg->sk_redir); psock->apply_bytes = msg->apply_bytes; if (ret == __SK_REDIRECT) { @@ -653,7 +652,6 @@ int sk_psock_msg_verdict(struct sock *sk, struct sk_psock *psock, } out: rcu_read_unlock(); - preempt_enable(); return ret; } EXPORT_SYMBOL_GPL(sk_psock_msg_verdict); @@ -665,9 +663,7 @@ static int sk_psock_bpf_run(struct sk_psock *psock, struct bpf_prog *prog, skb->sk = psock->sk; bpf_compute_data_end_sk_skb(skb); - preempt_disable(); - ret = BPF_PROG_RUN(prog, skb); - preempt_enable(); + ret = bpf_prog_run_pin_on_cpu(prog, skb); /* strparser clones the skb before handing it to a upper layer, * meaning skb_orphan has been called. We NULL sk on the way out * to ensure we don't trigger a BUG_ON() in skb/sk operations diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index ea9e73428ed9..56fac24a627a 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -380,9 +380,7 @@ static int kcm_parse_func_strparser(struct strparser *strp, struct sk_buff *skb) struct bpf_prog *prog = psock->bpf_prog; int res; - preempt_disable(); - res = BPF_PROG_RUN(prog, skb); - preempt_enable(); + res = bpf_prog_run_pin_on_cpu(prog, skb); return res; } -- cgit v1.2.3 From 2a916f2f546ca1c1e3323e2a4269307f6d9890eb Mon Sep 17 00:00:00 2001 From: David Miller Date: Mon, 24 Feb 2020 15:01:46 +0100 Subject: bpf: Use migrate_disable/enable in array macros and cgroup/lirc code. Replace the preemption disable/enable with migrate_disable/enable() to reflect the actual requirement and to allow PREEMPT_RT to substitute it with an actual migration disable mechanism which does not disable preemption. Including the code paths that go via __bpf_prog_run_save_cb(). Signed-off-by: David S. Miller Signed-off-by: Thomas Gleixner Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200224145643.998293311@linutronix.de --- include/linux/bpf.h | 8 ++++---- include/linux/filter.h | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 49b1a70e12c8..76b3a0eb1502 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -885,7 +885,7 @@ int bpf_prog_array_copy(struct bpf_prog_array *old_array, struct bpf_prog *_prog; \ struct bpf_prog_array *_array; \ u32 _ret = 1; \ - preempt_disable(); \ + migrate_disable(); \ rcu_read_lock(); \ _array = rcu_dereference(array); \ if (unlikely(check_non_null && !_array))\ @@ -898,7 +898,7 @@ int bpf_prog_array_copy(struct bpf_prog_array *old_array, } \ _out: \ rcu_read_unlock(); \ - preempt_enable(); \ + migrate_enable(); \ _ret; \ }) @@ -932,7 +932,7 @@ _out: \ u32 ret; \ u32 _ret = 1; \ u32 _cn = 0; \ - preempt_disable(); \ + migrate_disable(); \ rcu_read_lock(); \ _array = rcu_dereference(array); \ _item = &_array->items[0]; \ @@ -944,7 +944,7 @@ _out: \ _item++; \ } \ rcu_read_unlock(); \ - preempt_enable(); \ + migrate_enable(); \ if (_ret) \ _ret = (_cn ? NET_XMIT_CN : NET_XMIT_SUCCESS); \ else \ diff --git a/include/linux/filter.h b/include/linux/filter.h index 9270de2a0df8..43b5e455d2f5 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -677,6 +677,7 @@ static inline u8 *bpf_skb_cb(struct sk_buff *skb) return qdisc_skb_cb(skb)->data; } +/* Must be invoked with migration disabled */ static inline u32 __bpf_prog_run_save_cb(const struct bpf_prog *prog, struct sk_buff *skb) { @@ -702,9 +703,9 @@ static inline u32 bpf_prog_run_save_cb(const struct bpf_prog *prog, { u32 res; - preempt_disable(); + migrate_disable(); res = __bpf_prog_run_save_cb(prog, skb); - preempt_enable(); + migrate_enable(); return res; } -- cgit v1.2.3 From c518cfa0c5ad75ddf3d743f1e35b9cf5fc2c346e Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 24 Feb 2020 15:01:47 +0100 Subject: bpf: Provide recursion prevention helpers The places which need to prevent the execution of trace type BPF programs to prevent deadlocks on the hash bucket lock do this open coded. Provide two inline functions, bpf_disable/enable_instrumentation() to replace these open coded protection constructs. Use migrate_disable/enable() instead of preempt_disable/enable() right away so this works on RT enabled kernels. On a !RT kernel migrate_disable / enable() are mapped to preempt_disable/enable(). These helpers use this_cpu_inc/dec() instead of __this_cpu_inc/dec() on an RT enabled kernel because migrate disabled regions are preemptible and preemption might hit in the middle of a RMW operation which can lead to inconsistent state. Signed-off-by: Thomas Gleixner Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200224145644.103910133@linutronix.de --- include/linux/bpf.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 76b3a0eb1502..1acd5bf70350 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -961,6 +961,36 @@ _out: \ #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); +/* + * Block execution of BPF programs attached to instrumentation (perf, + * kprobes, tracepoints) to prevent deadlocks on map operations as any of + * these events can happen inside a region which holds a map bucket lock + * and can deadlock on it. + * + * Use the preemption safe inc/dec variants on RT because migrate disable + * is preemptible on RT and preemption in the middle of the RMW operation + * might lead to inconsistent state. Use the raw variants for non RT + * kernels as migrate_disable() maps to preempt_disable() so the slightly + * more expensive save operation can be avoided. + */ +static inline void bpf_disable_instrumentation(void) +{ + migrate_disable(); + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + this_cpu_inc(bpf_prog_active); + else + __this_cpu_inc(bpf_prog_active); +} + +static inline void bpf_enable_instrumentation(void) +{ + if (IS_ENABLED(CONFIG_PREEMPT_RT)) + this_cpu_dec(bpf_prog_active); + else + __this_cpu_dec(bpf_prog_active); + migrate_enable(); +} + extern const struct file_operations bpf_map_fops; extern const struct file_operations bpf_prog_fops; -- cgit v1.2.3 From 0231453bc08f63584545dda1c05d61b19755d3a9 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 14 Jan 2020 18:08:43 -0600 Subject: soundwire: bus: add clock stop helpers SoundWire supports two clock stop modes. Add support to handle the clock stop modes and add pm_runtime calls in the bus. Credits: this patch is based on an earlier internal contribution by Vinod Koul, Sanyog Kale, Shreyas Nc and Hardik Shah. Signed-off-by: Bard Liao Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200115000844.14695-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 332 ++++++++++++++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 24 +++ 2 files changed, 356 insertions(+) (limited to 'include') diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 23bc24c8e9d1..3395abd2ed39 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -2,6 +2,7 @@ // Copyright(c) 2015-17 Intel Corporation. #include +#include #include #include #include @@ -359,6 +360,52 @@ static int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value) return sdw_nwrite_no_pm(slave, addr, 1, &value); } +static int +sdw_bread_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr) +{ + struct sdw_msg msg; + u8 buf; + int ret; + + ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num, + SDW_MSG_FLAG_READ, &buf); + if (ret) + return ret; + + ret = sdw_transfer(bus, &msg); + if (ret < 0) + return ret; + else + return buf; +} + +static int +sdw_bwrite_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value) +{ + struct sdw_msg msg; + int ret; + + ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num, + SDW_MSG_FLAG_WRITE, &value); + if (ret) + return ret; + + return sdw_transfer(bus, &msg); +} + +static int +sdw_read_no_pm(struct sdw_slave *slave, u32 addr) +{ + u8 buf; + int ret; + + ret = sdw_nread_no_pm(slave, addr, 1, &buf); + if (ret < 0) + return ret; + else + return buf; +} + /** * sdw_nread() - Read "n" contiguous SDW Slave registers * @slave: SDW Slave @@ -673,6 +720,291 @@ static void sdw_modify_slave_status(struct sdw_slave *slave, mutex_unlock(&slave->bus->bus_lock); } +static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave) +{ + enum sdw_clk_stop_mode mode; + + /* + * Query for clock stop mode if Slave implements + * ops->get_clk_stop_mode, else read from property. + */ + if (slave->ops && slave->ops->get_clk_stop_mode) { + mode = slave->ops->get_clk_stop_mode(slave); + } else { + if (slave->prop.clk_stop_mode1) + mode = SDW_CLK_STOP_MODE1; + else + mode = SDW_CLK_STOP_MODE0; + } + + return mode; +} + +static int sdw_slave_clk_stop_callback(struct sdw_slave *slave, + enum sdw_clk_stop_mode mode, + enum sdw_clk_stop_type type) +{ + int ret; + + if (slave->ops && slave->ops->clk_stop) { + ret = slave->ops->clk_stop(slave, mode, type); + if (ret < 0) { + dev_err(&slave->dev, + "Clk Stop type =%d failed: %d\n", type, ret); + return ret; + } + } + + return 0; +} + +static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave, + enum sdw_clk_stop_mode mode, + bool prepare) +{ + bool wake_en; + u32 val = 0; + int ret; + + wake_en = slave->prop.wake_capable; + + if (prepare) { + val = SDW_SCP_SYSTEMCTRL_CLK_STP_PREP; + + if (mode == SDW_CLK_STOP_MODE1) + val |= SDW_SCP_SYSTEMCTRL_CLK_STP_MODE1; + + if (wake_en) + val |= SDW_SCP_SYSTEMCTRL_WAKE_UP_EN; + } else { + val = sdw_read_no_pm(slave, SDW_SCP_SYSTEMCTRL); + + val &= ~(SDW_SCP_SYSTEMCTRL_CLK_STP_PREP); + } + + ret = sdw_write_no_pm(slave, SDW_SCP_SYSTEMCTRL, val); + + if (ret != 0) + dev_err(&slave->dev, + "Clock Stop prepare failed for slave: %d", ret); + + return ret; +} + +static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num) +{ + int retry = bus->clk_stop_timeout; + int val; + + do { + val = sdw_bread_no_pm(bus, dev_num, SDW_SCP_STAT) & + SDW_SCP_STAT_CLK_STP_NF; + if (!val) { + dev_info(bus->dev, "clock stop prep/de-prep done slave:%d", + dev_num); + return 0; + } + + usleep_range(1000, 1500); + retry--; + } while (retry); + + dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d", + dev_num); + + return -ETIMEDOUT; +} + +/** + * sdw_bus_prep_clk_stop: prepare Slave(s) for clock stop + * + * @bus: SDW bus instance + * + * Query Slave for clock stop mode and prepare for that mode. + */ +int sdw_bus_prep_clk_stop(struct sdw_bus *bus) +{ + enum sdw_clk_stop_mode slave_mode; + bool simple_clk_stop = true; + struct sdw_slave *slave; + bool is_slave = false; + int ret = 0; + + /* + * In order to save on transition time, prepare + * each Slave and then wait for all Slave(s) to be + * prepared for clock stop. + */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + slave_mode = sdw_get_clk_stop_mode(slave); + slave->curr_clk_stop_mode = slave_mode; + + ret = sdw_slave_clk_stop_callback(slave, slave_mode, + SDW_CLK_PRE_PREPARE); + if (ret < 0) { + dev_err(&slave->dev, + "pre-prepare failed:%d", ret); + return ret; + } + + ret = sdw_slave_clk_stop_prepare(slave, + slave_mode, true); + if (ret < 0) { + dev_err(&slave->dev, + "pre-prepare failed:%d", ret); + return ret; + } + + if (slave_mode == SDW_CLK_STOP_MODE1) + simple_clk_stop = false; + } + + if (is_slave && !simple_clk_stop) { + ret = sdw_bus_wait_for_clk_prep_deprep(bus, + SDW_BROADCAST_DEV_NUM); + if (ret < 0) + return ret; + } + + /* Inform slaves that prep is done */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + slave_mode = slave->curr_clk_stop_mode; + + if (slave_mode == SDW_CLK_STOP_MODE1) { + ret = sdw_slave_clk_stop_callback(slave, + slave_mode, + SDW_CLK_POST_PREPARE); + + if (ret < 0) { + dev_err(&slave->dev, + "post-prepare failed:%d", ret); + } + } + } + + return ret; +} +EXPORT_SYMBOL(sdw_bus_prep_clk_stop); + +/** + * sdw_bus_clk_stop: stop bus clock + * + * @bus: SDW bus instance + * + * After preparing the Slaves for clock stop, stop the clock by broadcasting + * write to SCP_CTRL register. + */ +int sdw_bus_clk_stop(struct sdw_bus *bus) +{ + int ret; + + /* + * broadcast clock stop now, attached Slaves will ACK this, + * unattached will ignore + */ + ret = sdw_bwrite_no_pm(bus, SDW_BROADCAST_DEV_NUM, + SDW_SCP_CTRL, SDW_SCP_CTRL_CLK_STP_NOW); + if (ret < 0) { + dev_err(bus->dev, + "ClockStopNow Broadcast message failed %d", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL(sdw_bus_clk_stop); + +/** + * sdw_bus_exit_clk_stop: Exit clock stop mode + * + * @bus: SDW bus instance + * + * This De-prepares the Slaves by exiting Clock Stop Mode 0. For the Slaves + * exiting Clock Stop Mode 1, they will be de-prepared after they enumerate + * back. + */ +int sdw_bus_exit_clk_stop(struct sdw_bus *bus) +{ + enum sdw_clk_stop_mode mode; + bool simple_clk_stop = true; + struct sdw_slave *slave; + bool is_slave = false; + int ret; + + /* + * In order to save on transition time, de-prepare + * each Slave and then wait for all Slave(s) to be + * de-prepared after clock resume. + */ + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + /* Identify if Slave(s) are available on Bus */ + is_slave = true; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + mode = slave->curr_clk_stop_mode; + + if (mode == SDW_CLK_STOP_MODE1) { + simple_clk_stop = false; + continue; + } + + ret = sdw_slave_clk_stop_callback(slave, mode, + SDW_CLK_PRE_DEPREPARE); + if (ret < 0) + dev_warn(&slave->dev, + "clk stop deprep failed:%d", ret); + + ret = sdw_slave_clk_stop_prepare(slave, mode, + false); + + if (ret < 0) + dev_warn(&slave->dev, + "clk stop deprep failed:%d", ret); + } + + if (is_slave && !simple_clk_stop) + sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM); + + list_for_each_entry(slave, &bus->slaves, node) { + if (!slave->dev_num) + continue; + + if (slave->status != SDW_SLAVE_ATTACHED && + slave->status != SDW_SLAVE_ALERT) + continue; + + mode = slave->curr_clk_stop_mode; + sdw_slave_clk_stop_callback(slave, mode, + SDW_CLK_POST_DEPREPARE); + } + + return 0; +} +EXPORT_SYMBOL(sdw_bus_exit_clk_stop); + int sdw_configure_dpn_intr(struct sdw_slave *slave, int port, bool enable, int mask) { diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index b451bb622335..b8427df034ce 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -79,6 +79,21 @@ enum sdw_slave_status { SDW_SLAVE_RESERVED = 3, }; +/** + * enum sdw_clk_stop_type: clock stop operations + * + * @SDW_CLK_PRE_PREPARE: pre clock stop prepare + * @SDW_CLK_POST_PREPARE: post clock stop prepare + * @SDW_CLK_PRE_DEPREPARE: pre clock stop de-prepare + * @SDW_CLK_POST_DEPREPARE: post clock stop de-prepare + */ +enum sdw_clk_stop_type { + SDW_CLK_PRE_PREPARE = 0, + SDW_CLK_POST_PREPARE, + SDW_CLK_PRE_DEPREPARE, + SDW_CLK_POST_DEPREPARE, +}; + /** * enum sdw_command_response - Command response as defined by SDW spec * @SDW_CMD_OK: cmd was successful @@ -533,6 +548,11 @@ struct sdw_slave_ops { int (*port_prep)(struct sdw_slave *slave, struct sdw_prepare_ch *prepare_ch, enum sdw_port_prep_ops pre_ops); + int (*get_clk_stop_mode)(struct sdw_slave *slave); + int (*clk_stop)(struct sdw_slave *slave, + enum sdw_clk_stop_mode mode, + enum sdw_clk_stop_type type); + }; /** @@ -575,6 +595,7 @@ struct sdw_slave { #endif struct list_head node; struct completion *port_ready; + enum sdw_clk_stop_mode curr_clk_stop_mode; u16 dev_num; u16 dev_num_sticky; bool probed; @@ -892,6 +913,9 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream); int sdw_enable_stream(struct sdw_stream_runtime *stream); int sdw_disable_stream(struct sdw_stream_runtime *stream); int sdw_deprepare_stream(struct sdw_stream_runtime *stream); +int sdw_bus_prep_clk_stop(struct sdw_bus *bus); +int sdw_bus_clk_stop(struct sdw_bus *bus); +int sdw_bus_exit_clk_stop(struct sdw_bus *bus); /* messaging and data APIs */ -- cgit v1.2.3 From 3d5f7d9f6a38ddcc105ebfb23b640630bbabba65 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Mon, 17 Feb 2020 17:40:32 +0530 Subject: PCI: endpoint: Add core init notifying feature Add a new feature core_init_notifier for cores that can notify about their availability for initialization. Signed-off-by: Vidya Sagar Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- include/linux/pci-epc.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index ccaf6e3fa931..9ffe6bd081ae 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -120,6 +120,7 @@ struct pci_epc { */ struct pci_epc_features { unsigned int linkup_notifier : 1; + unsigned int core_init_notifier : 1; unsigned int msi_capable : 1; unsigned int msix_capable : 1; u8 reserved_bar; -- cgit v1.2.3 From 0ef22dcf0c1871888c4c0ee46a9d9c494f2fe997 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Mon, 17 Feb 2020 17:40:34 +0530 Subject: PCI: endpoint: Add notification for core init completion Add support to send notifications to EPF from EPC once the core registers initialization is complete. Signed-off-by: Vidya Sagar Signed-off-by: Lorenzo Pieralisi Acked-by: Kishon Vijay Abraham I --- drivers/pci/endpoint/pci-epc-core.c | 19 ++++++++++++++++++- include/linux/pci-epc.h | 1 + include/linux/pci-epf.h | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index dc1c673534e0..0d22a377a0cf 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -540,10 +540,27 @@ void pci_epc_linkup(struct pci_epc *epc) if (!epc || IS_ERR(epc)) return; - atomic_notifier_call_chain(&epc->notifier, 0, NULL); + atomic_notifier_call_chain(&epc->notifier, LINK_UP, NULL); } EXPORT_SYMBOL_GPL(pci_epc_linkup); +/** + * pci_epc_init_notify() - Notify the EPF device that EPC device's core + * initialization is completed. + * @epc: the EPC device whose core initialization is completeds + * + * Invoke to Notify the EPF device that the EPC device's initialization + * is completed. + */ +void pci_epc_init_notify(struct pci_epc *epc) +{ + if (!epc || IS_ERR(epc)) + return; + + atomic_notifier_call_chain(&epc->notifier, CORE_INIT, NULL); +} +EXPORT_SYMBOL_GPL(pci_epc_init_notify); + /** * pci_epc_destroy() - destroy the EPC device * @epc: the EPC device that has to be destroyed diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 9ffe6bd081ae..0d7e91bad91e 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -165,6 +165,7 @@ void devm_pci_epc_destroy(struct device *dev, struct pci_epc *epc); void pci_epc_destroy(struct pci_epc *epc); int pci_epc_add_epf(struct pci_epc *epc, struct pci_epf *epf); void pci_epc_linkup(struct pci_epc *epc); +void pci_epc_init_notify(struct pci_epc *epc); void pci_epc_remove_epf(struct pci_epc *epc, struct pci_epf *epf); int pci_epc_write_header(struct pci_epc *epc, u8 func_no, struct pci_epf_header *hdr); diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h index bcdf4f07bde7..0c628e30c582 100644 --- a/include/linux/pci-epf.h +++ b/include/linux/pci-epf.h @@ -15,6 +15,11 @@ struct pci_epf; +enum pci_notify_event { + CORE_INIT, + LINK_UP, +}; + enum pci_barno { BAR_0, BAR_1, -- cgit v1.2.3 From 2008495d81159a66de4dc3ee4252a5fc60294a82 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 25 Feb 2020 11:45:18 +0100 Subject: flow_offload: pass action cookie through offload structures Extend struct flow_action_entry in order to hold TC action cookie specified by user inserting the action. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/flow_offload.h | 11 +++++++++++ net/core/flow_offload.c | 21 +++++++++++++++++++++ net/sched/cls_api.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index c6f7bd22db60..4e864c34a1b0 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -156,6 +156,16 @@ enum flow_action_mangle_base { typedef void (*action_destr)(void *priv); +struct flow_action_cookie { + u32 cookie_len; + u8 cookie[]; +}; + +struct flow_action_cookie *flow_action_cookie_create(void *data, + unsigned int len, + gfp_t gfp); +void flow_action_cookie_destroy(struct flow_action_cookie *cookie); + struct flow_action_entry { enum flow_action_id id; action_destr destructor; @@ -214,6 +224,7 @@ struct flow_action_entry { u8 ttl; } mpls_mangle; }; + struct flow_action_cookie *cookie; /* user defined action cookie */ }; struct flow_action { diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index 45b6a59ac124..d21348202ba6 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -167,6 +167,27 @@ void flow_rule_match_enc_opts(const struct flow_rule *rule, } EXPORT_SYMBOL(flow_rule_match_enc_opts); +struct flow_action_cookie *flow_action_cookie_create(void *data, + unsigned int len, + gfp_t gfp) +{ + struct flow_action_cookie *cookie; + + cookie = kmalloc(sizeof(*cookie) + len, gfp); + if (!cookie) + return NULL; + cookie->cookie_len = len; + memcpy(cookie->cookie, data, len); + return cookie; +} +EXPORT_SYMBOL(flow_action_cookie_create); + +void flow_action_cookie_destroy(struct flow_action_cookie *cookie) +{ + kfree(cookie); +} +EXPORT_SYMBOL(flow_action_cookie_destroy); + struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb, void *cb_ident, void *cb_priv, void (*release)(void *cb_priv)) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 13c33eaf1ca1..4e766c5ab77a 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3382,14 +3382,40 @@ int tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp, } EXPORT_SYMBOL(tc_setup_cb_reoffload); +static int tcf_act_get_cookie(struct flow_action_entry *entry, + const struct tc_action *act) +{ + struct tc_cookie *cookie; + int err = 0; + + rcu_read_lock(); + cookie = rcu_dereference(act->act_cookie); + if (cookie) { + entry->cookie = flow_action_cookie_create(cookie->data, + cookie->len, + GFP_ATOMIC); + if (!entry->cookie) + err = -ENOMEM; + } + rcu_read_unlock(); + return err; +} + +static void tcf_act_put_cookie(struct flow_action_entry *entry) +{ + flow_action_cookie_destroy(entry->cookie); +} + void tc_cleanup_flow_action(struct flow_action *flow_action) { struct flow_action_entry *entry; int i; - flow_action_for_each(i, entry, flow_action) + flow_action_for_each(i, entry, flow_action) { + tcf_act_put_cookie(entry); if (entry->destructor) entry->destructor(entry->destructor_priv); + } } EXPORT_SYMBOL(tc_cleanup_flow_action); @@ -3447,6 +3473,9 @@ int tc_setup_flow_action(struct flow_action *flow_action, entry = &flow_action->entries[j]; spin_lock_bh(&act->tcfa_lock); + err = tcf_act_get_cookie(entry, act); + if (err) + goto err_out_locked; if (is_tcf_gact_ok(act)) { entry->id = FLOW_ACTION_ACCEPT; } else if (is_tcf_gact_shot(act)) { -- cgit v1.2.3 From 85b0589ede83d7b4aeb2bc3cb8910183876cd5ee Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 25 Feb 2020 11:45:19 +0100 Subject: devlink: add trap metadata type for cookie Allow driver to indicate cookie metadata for registered traps. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/devlink.h | 1 + include/uapi/linux/devlink.h | 2 ++ net/core/devlink.c | 3 +++ 3 files changed, 6 insertions(+) (limited to 'include') diff --git a/include/net/devlink.h b/include/net/devlink.h index 07923e619206..014a8b3d1499 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -541,6 +541,7 @@ struct devlink_trap_group { }; #define DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT BIT(0) +#define DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE BIT(1) /** * struct devlink_trap - Immutable packet trap attributes. diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index ae37fd4d194a..be2a2948f452 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -252,6 +252,8 @@ enum devlink_trap_type { enum { /* Trap can report input port as metadata */ DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT, + /* Trap can report flow action cookie as metadata */ + DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE, }; enum devlink_attr { diff --git a/net/core/devlink.c b/net/core/devlink.c index 0d7c5d3443d2..12e6ef749b8a 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -5540,6 +5540,9 @@ static int devlink_trap_metadata_put(struct sk_buff *msg, if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT) && nla_put_flag(msg, DEVLINK_ATTR_TRAP_METADATA_TYPE_IN_PORT)) goto nla_put_failure; + if ((trap->metadata_cap & DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE) && + nla_put_flag(msg, DEVLINK_ATTR_TRAP_METADATA_TYPE_FA_COOKIE)) + goto nla_put_failure; nla_nest_end(msg, attr); -- cgit v1.2.3 From 742b8cceaabc3ed09d4c4d527d9c6311c4925545 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 25 Feb 2020 11:45:20 +0100 Subject: drop_monitor: extend by passing cookie from driver If driver passed along the cookie, push it through Netlink. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/drop_monitor.h | 3 +++ include/uapi/linux/net_dropmon.h | 1 + net/core/drop_monitor.c | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/drop_monitor.h b/include/net/drop_monitor.h index 2ab668461463..ddd441a60e03 100644 --- a/include/net/drop_monitor.h +++ b/include/net/drop_monitor.h @@ -6,17 +6,20 @@ #include #include #include +#include /** * struct net_dm_hw_metadata - Hardware-supplied packet metadata. * @trap_group_name: Hardware trap group name. * @trap_name: Hardware trap name. * @input_dev: Input netdevice. + * @fa_cookie: Flow action user cookie. */ struct net_dm_hw_metadata { const char *trap_group_name; const char *trap_name; struct net_device *input_dev; + const struct flow_action_cookie *fa_cookie; }; #if IS_ENABLED(CONFIG_NET_DROP_MONITOR) diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h index 8bf79a9eb234..66048cc5d7b3 100644 --- a/include/uapi/linux/net_dropmon.h +++ b/include/uapi/linux/net_dropmon.h @@ -92,6 +92,7 @@ enum net_dm_attr { NET_DM_ATTR_HW_TRAP_COUNT, /* u32 */ NET_DM_ATTR_SW_DROPS, /* flag */ NET_DM_ATTR_HW_DROPS, /* flag */ + NET_DM_ATTR_FLOW_ACTION_COOKIE, /* binary */ __NET_DM_ATTR_MAX, NET_DM_ATTR_MAX = __NET_DM_ATTR_MAX - 1 diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c index 31700e0c3928..d58c1c45a895 100644 --- a/net/core/drop_monitor.c +++ b/net/core/drop_monitor.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -700,6 +701,13 @@ static void net_dm_packet_work(struct work_struct *work) net_dm_packet_report(skb); } +static size_t +net_dm_flow_action_cookie_size(const struct net_dm_hw_metadata *hw_metadata) +{ + return hw_metadata->fa_cookie ? + nla_total_size(hw_metadata->fa_cookie->cookie_len) : 0; +} + static size_t net_dm_hw_packet_report_size(size_t payload_len, const struct net_dm_hw_metadata *hw_metadata) @@ -717,6 +725,8 @@ net_dm_hw_packet_report_size(size_t payload_len, nla_total_size(strlen(hw_metadata->trap_name) + 1) + /* NET_DM_ATTR_IN_PORT */ net_dm_in_port_size() + + /* NET_DM_ATTR_FLOW_ACTION_COOKIE */ + net_dm_flow_action_cookie_size(hw_metadata) + /* NET_DM_ATTR_TIMESTAMP */ nla_total_size(sizeof(u64)) + /* NET_DM_ATTR_ORIG_LEN */ @@ -762,6 +772,12 @@ static int net_dm_hw_packet_report_fill(struct sk_buff *msg, goto nla_put_failure; } + if (hw_metadata->fa_cookie && + nla_put(msg, NET_DM_ATTR_FLOW_ACTION_COOKIE, + hw_metadata->fa_cookie->cookie_len, + hw_metadata->fa_cookie->cookie)) + goto nla_put_failure; + if (nla_put_u64_64bit(msg, NET_DM_ATTR_TIMESTAMP, ktime_to_ns(skb->tstamp), NET_DM_ATTR_PAD)) goto nla_put_failure; @@ -794,11 +810,12 @@ nla_put_failure: static struct net_dm_hw_metadata * net_dm_hw_metadata_clone(const struct net_dm_hw_metadata *hw_metadata) { + const struct flow_action_cookie *fa_cookie; struct net_dm_hw_metadata *n_hw_metadata; const char *trap_group_name; const char *trap_name; - n_hw_metadata = kmalloc(sizeof(*hw_metadata), GFP_ATOMIC); + n_hw_metadata = kzalloc(sizeof(*hw_metadata), GFP_ATOMIC); if (!n_hw_metadata) return NULL; @@ -812,12 +829,25 @@ net_dm_hw_metadata_clone(const struct net_dm_hw_metadata *hw_metadata) goto free_trap_group; n_hw_metadata->trap_name = trap_name; + if (hw_metadata->fa_cookie) { + size_t cookie_size = sizeof(*fa_cookie) + + hw_metadata->fa_cookie->cookie_len; + + fa_cookie = kmemdup(hw_metadata->fa_cookie, cookie_size, + GFP_ATOMIC); + if (!fa_cookie) + goto free_trap_name; + n_hw_metadata->fa_cookie = fa_cookie; + } + n_hw_metadata->input_dev = hw_metadata->input_dev; if (n_hw_metadata->input_dev) dev_hold(n_hw_metadata->input_dev); return n_hw_metadata; +free_trap_name: + kfree(trap_name); free_trap_group: kfree(trap_group_name); free_hw_metadata: @@ -830,6 +860,7 @@ net_dm_hw_metadata_free(const struct net_dm_hw_metadata *hw_metadata) { if (hw_metadata->input_dev) dev_put(hw_metadata->input_dev); + kfree(hw_metadata->fa_cookie); kfree(hw_metadata->trap_name); kfree(hw_metadata->trap_group_name); kfree(hw_metadata); -- cgit v1.2.3 From 5a2e106c746d2740f425cce3ac039321c924dc85 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 25 Feb 2020 11:45:21 +0100 Subject: devlink: extend devlink_trap_report() to accept cookie and pass Add cookie argument to devlink_trap_report() allowing driver to pass in the user cookie. Pass on the cookie down to drop monitor code. Signed-off-by: Jiri Pirko Signed-off-by: Ido Schimmel Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c | 4 ++-- drivers/net/netdevsim/dev.c | 2 +- include/net/devlink.h | 7 ++++--- net/core/devlink.c | 11 ++++++++--- 4 files changed, 15 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c index 04f2445f6d43..a55577a50e90 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_trap.c @@ -71,7 +71,7 @@ static void mlxsw_sp_rx_drop_listener(struct sk_buff *skb, u8 local_port, in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core, local_port); skb_push(skb, ETH_HLEN); - devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port); + devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, NULL); consume_skb(skb); } @@ -95,7 +95,7 @@ static void mlxsw_sp_rx_exception_listener(struct sk_buff *skb, u8 local_port, in_devlink_port = mlxsw_core_port_devlink_port_get(mlxsw_sp->core, local_port); skb_push(skb, ETH_HLEN); - devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port); + devlink_trap_report(devlink, skb, trap_ctx, in_devlink_port, NULL); skb_pull(skb, ETH_HLEN); skb->offload_fwd_mark = 1; netif_receive_skb(skb); diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index d7706a0346f2..aa17533c06e1 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -385,7 +385,7 @@ static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) */ local_bh_disable(); devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, - &nsim_dev_port->devlink_port); + &nsim_dev_port->devlink_port, NULL); local_bh_enable(); consume_skb(skb); } diff --git a/include/net/devlink.h b/include/net/devlink.h index 014a8b3d1499..c9ca86b054bc 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -16,6 +16,7 @@ #include #include #include +#include #include struct devlink_ops; @@ -1050,9 +1051,9 @@ int devlink_traps_register(struct devlink *devlink, void devlink_traps_unregister(struct devlink *devlink, const struct devlink_trap *traps, size_t traps_count); -void devlink_trap_report(struct devlink *devlink, - struct sk_buff *skb, void *trap_ctx, - struct devlink_port *in_devlink_port); +void devlink_trap_report(struct devlink *devlink, struct sk_buff *skb, + void *trap_ctx, struct devlink_port *in_devlink_port, + const struct flow_action_cookie *fa_cookie); void *devlink_trap_ctx_priv(void *trap_ctx); #if IS_ENABLED(CONFIG_NET_DEVLINK) diff --git a/net/core/devlink.c b/net/core/devlink.c index 12e6ef749b8a..49706031ab45 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -8205,12 +8205,14 @@ devlink_trap_stats_update(struct devlink_stats __percpu *trap_stats, static void devlink_trap_report_metadata_fill(struct net_dm_hw_metadata *hw_metadata, const struct devlink_trap_item *trap_item, - struct devlink_port *in_devlink_port) + struct devlink_port *in_devlink_port, + const struct flow_action_cookie *fa_cookie) { struct devlink_trap_group_item *group_item = trap_item->group_item; hw_metadata->trap_group_name = group_item->group->name; hw_metadata->trap_name = trap_item->trap->name; + hw_metadata->fa_cookie = fa_cookie; spin_lock(&in_devlink_port->type_lock); if (in_devlink_port->type == DEVLINK_PORT_TYPE_ETH) @@ -8224,9 +8226,12 @@ devlink_trap_report_metadata_fill(struct net_dm_hw_metadata *hw_metadata, * @skb: Trapped packet. * @trap_ctx: Trap context. * @in_devlink_port: Input devlink port. + * @fa_cookie: Flow action cookie. Could be NULL. */ void devlink_trap_report(struct devlink *devlink, struct sk_buff *skb, - void *trap_ctx, struct devlink_port *in_devlink_port) + void *trap_ctx, struct devlink_port *in_devlink_port, + const struct flow_action_cookie *fa_cookie) + { struct devlink_trap_item *trap_item = trap_ctx; struct net_dm_hw_metadata hw_metadata = {}; @@ -8235,7 +8240,7 @@ void devlink_trap_report(struct devlink *devlink, struct sk_buff *skb, devlink_trap_stats_update(trap_item->group_item->stats, skb->len); devlink_trap_report_metadata_fill(&hw_metadata, trap_item, - in_devlink_port); + in_devlink_port, fa_cookie); net_dm_hw_report(skb, &hw_metadata); } EXPORT_SYMBOL_GPL(devlink_trap_report); -- cgit v1.2.3 From 2c6cff682d6681fb1cdb03b3cdbbecd3fb0e4c89 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 25 Feb 2020 11:00:41 -0600 Subject: soundwire: add helper macros for devID fields Move bit extractors to macros, so that the definitions can be used by other drivers parsing the MIPI definitions extracted from firmware tables (ACPI or DT). Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200225170041.23644-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 21 +++++---------------- include/linux/soundwire/sdw.h | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index 6106577fb3ed..ab35b09e7231 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -504,22 +504,11 @@ void sdw_extract_slave_id(struct sdw_bus *bus, { dev_dbg(bus->dev, "SDW Slave Addr: %llx\n", addr); - /* - * Spec definition - * Register Bit Contents - * DevId_0 [7:4] 47:44 sdw_version - * DevId_0 [3:0] 43:40 unique_id - * DevId_1 39:32 mfg_id [15:8] - * DevId_2 31:24 mfg_id [7:0] - * DevId_3 23:16 part_id [15:8] - * DevId_4 15:08 part_id [7:0] - * DevId_5 07:00 class_id - */ - id->sdw_version = (addr >> 44) & GENMASK(3, 0); - id->unique_id = (addr >> 40) & GENMASK(3, 0); - id->mfg_id = (addr >> 24) & GENMASK(15, 0); - id->part_id = (addr >> 8) & GENMASK(15, 0); - id->class_id = addr & GENMASK(7, 0); + id->sdw_version = SDW_VERSION(addr); + id->unique_id = SDW_UNIQUE_ID(addr); + id->mfg_id = SDW_MFG_ID(addr); + id->part_id = SDW_PART_ID(addr); + id->class_id = SDW_CLASS_ID(addr); dev_dbg(bus->dev, "SDW Slave class_id %x, part_id %x, mfg_id %x, unique_id %x, version %x\n", diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index b451bb622335..56273c5c1f6b 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -424,6 +424,29 @@ struct sdw_slave_id { __u8 sdw_version:4; }; +/* + * Helper macros to extract the MIPI-defined IDs + * + * Spec definition + * Register Bit Contents + * DevId_0 [7:4] 47:44 sdw_version + * DevId_0 [3:0] 43:40 unique_id + * DevId_1 39:32 mfg_id [15:8] + * DevId_2 31:24 mfg_id [7:0] + * DevId_3 23:16 part_id [15:8] + * DevId_4 15:08 part_id [7:0] + * DevId_5 07:00 class_id + * + * The MIPI DisCo for SoundWire defines in addition the link_id as bits 51:48 + */ + +#define SDW_DISCO_LINK_ID(adr) (((adr) >> 48) & GENMASK(3, 0)) +#define SDW_VERSION(adr) (((adr) >> 44) & GENMASK(3, 0)) +#define SDW_UNIQUE_ID(adr) (((adr) >> 40) & GENMASK(3, 0)) +#define SDW_MFG_ID(adr) (((adr) >> 24) & GENMASK(15, 0)) +#define SDW_PART_ID(adr) (((adr) >> 8) & GENMASK(15, 0)) +#define SDW_CLASS_ID(adr) ((adr) & GENMASK(7, 0)) + /** * struct sdw_slave_intr_status - Slave interrupt status * @control_port: control port status -- cgit v1.2.3 From c7896490dd1a4e6b346e8a475e4a433356362770 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 7 Jan 2020 15:10:28 +0100 Subject: leds: ns2: Absorb platform data Nothing in the kernel includes the external header so just push the contents into the ns2 leds driver. If someone wants to use platform data or board files to describe this device they should be able to do so using GPIO machine descriptors but in any case device tree should be the way forward for these systems in all cases I can think of, and the driver already supports that. Cc: Simon Guinot Cc: Vincent Donnefort Signed-off-by: Linus Walleij Tested-by: Simon Guinot Signed-off-by: Pavel Machek --- drivers/leds/leds-ns2.c | 30 +++++++++++++++++-- include/linux/platform_data/leds-kirkwood-ns2.h | 38 ------------------------- 2 files changed, 27 insertions(+), 41 deletions(-) delete mode 100644 include/linux/platform_data/leds-kirkwood-ns2.h (limited to 'include') diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index 7c500dfdcfa3..6d37dda12c39 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -12,14 +12,38 @@ #include #include #include -#include +#include #include #include -#include #include -#include #include "leds.h" +enum ns2_led_modes { + NS_V2_LED_OFF, + NS_V2_LED_ON, + NS_V2_LED_SATA, +}; + +struct ns2_led_modval { + enum ns2_led_modes mode; + int cmd_level; + int slow_level; +}; + +struct ns2_led { + const char *name; + const char *default_trigger; + unsigned cmd; + unsigned slow; + int num_modes; + struct ns2_led_modval *modval; +}; + +struct ns2_led_platform_data { + int num_leds; + struct ns2_led *leds; +}; + /* * The Network Space v2 dual-GPIO LED is wired to a CPLD. Three different LED * modes are available: off, on and SATA activity blinking. The LED modes are diff --git a/include/linux/platform_data/leds-kirkwood-ns2.h b/include/linux/platform_data/leds-kirkwood-ns2.h deleted file mode 100644 index eb8a6860e816..000000000000 --- a/include/linux/platform_data/leds-kirkwood-ns2.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Platform data structure for Network Space v2 LED driver - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __LEDS_KIRKWOOD_NS2_H -#define __LEDS_KIRKWOOD_NS2_H - -enum ns2_led_modes { - NS_V2_LED_OFF, - NS_V2_LED_ON, - NS_V2_LED_SATA, -}; - -struct ns2_led_modval { - enum ns2_led_modes mode; - int cmd_level; - int slow_level; -}; - -struct ns2_led { - const char *name; - const char *default_trigger; - unsigned cmd; - unsigned slow; - int num_modes; - struct ns2_led_modval *modval; -}; - -struct ns2_led_platform_data { - int num_leds; - struct ns2_led *leds; -}; - -#endif /* __LEDS_KIRKWOOD_NS2_H */ -- cgit v1.2.3 From 76afa64374a79c22b2dab61aebef99a967783bf0 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 25 Feb 2020 21:39:12 +0800 Subject: ASoC: Add initial support for multiple CPU DAIs ASoC core supports multiple codec DAIs but supports only a CPU DAI. To support multiple cpu DAIs, add cpu_dai and num_cpu_dai in snd_soc_dai_link and snd_soc_pcm_runtime structures similar to support for codec_dai. This is intended as a preparatory patch to eventually support the unification of the Codec and CPU DAI. Inline with multiple codec DAI approach, add support to allocate, init, bind and probe multiple cpu_dai on init if driver specifies that. Also add support to loop over multiple cpu_dai during suspend and resume. This is intended as a preparatory patch to eventually unify the CPU and Codec DAI into DAI components. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Signed-off-by: Kuninori Morimoto Signed-off-by: Shreyas NC Link: https://lore.kernel.org/r/20200225133917.21314-2-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc.h | 15 +++++ sound/soc/soc-core.c | 168 ++++++++++++++++++++++++++++----------------------- 2 files changed, 106 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 8a2266676b2d..81e5d17be935 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -855,6 +855,11 @@ struct snd_soc_dai_link { ((platform) = &link->platforms[i]); \ (i)++) +#define for_each_link_cpus(link, i, cpu) \ + for ((i) = 0; \ + ((i) < link->num_cpus) && ((cpu) = &link->cpus[i]); \ + (i)++) + /* * Sample 1 : Single CPU/Codec/Platform * @@ -1132,6 +1137,9 @@ struct snd_soc_pcm_runtime { struct snd_soc_dai **codec_dais; unsigned int num_codecs; + struct snd_soc_dai **cpu_dais; + unsigned int num_cpus; + struct delayed_work delayed_work; void (*close_delayed_work_func)(struct snd_soc_pcm_runtime *rtd); #ifdef CONFIG_DEBUG_FS @@ -1159,6 +1167,13 @@ struct snd_soc_pcm_runtime { #define for_each_rtd_codec_dai_rollback(rtd, i, dai) \ for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);) +#define for_each_rtd_cpu_dai(rtd, i, dai)\ + for ((i) = 0; \ + ((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \ + (i)++) +#define for_each_rtd_cpu_dai_rollback(rtd, i, dai) \ + for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);) + void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd); /* mixer control */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 518b652cf872..f2cfbf182f49 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -483,6 +483,14 @@ static struct snd_soc_pcm_runtime *soc_new_pcm_runtime( if (!rtd->codec_dais) goto free_rtd; + /* + * for rtd->cpu_dais + */ + rtd->cpu_dais = devm_kcalloc(dev, dai_link->num_cpus, + sizeof(struct snd_soc_dai *), + GFP_KERNEL); + if (!rtd->cpu_dais) + goto free_rtd; /* * rtd remaining settings */ @@ -833,7 +841,7 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { int i; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *cpu, *codec, *platform; for_each_link_codecs(link, i, codec) { /* @@ -882,44 +890,38 @@ static int soc_dai_link_sanity_check(struct snd_soc_card *card, return -EPROBE_DEFER; } - /* FIXME */ - if (link->num_cpus > 1) { - dev_err(card->dev, - "ASoC: multi cpu is not yet supported %s\n", - link->name); - return -EINVAL; - } - - /* - * CPU device may be specified by either name or OF node, but - * can be left unspecified, and will be matched based on DAI - * name alone.. - */ - if (link->cpus->name && link->cpus->of_node) { - dev_err(card->dev, - "ASoC: Neither/both cpu name/of_node are set for %s\n", - link->name); - return -EINVAL; - } + for_each_link_cpus(link, i, cpu) { + /* + * CPU device may be specified by either name or OF node, but + * can be left unspecified, and will be matched based on DAI + * name alone.. + */ + if (cpu->name && cpu->of_node) { + dev_err(card->dev, + "ASoC: Neither/both cpu name/of_node are set for %s\n", + link->name); + return -EINVAL; + } - /* - * Defer card registration if cpu dai component is not added to - * component list. - */ - if ((link->cpus->of_node || link->cpus->name) && - !soc_find_component(link->cpus)) - return -EPROBE_DEFER; + /* + * Defer card registration if cpu dai component is not added to + * component list. + */ + if ((cpu->of_node || cpu->name) && + !soc_find_component(cpu)) + return -EPROBE_DEFER; - /* - * At least one of CPU DAI name or CPU device name/node must be - * specified - */ - if (!link->cpus->dai_name && - !(link->cpus->name || link->cpus->of_node)) { - dev_err(card->dev, - "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", - link->name); - return -EINVAL; + /* + * At least one of CPU DAI name or CPU device name/node must be + * specified + */ + if (!cpu->dai_name && + !(cpu->name || cpu->of_node)) { + dev_err(card->dev, + "ASoC: Neither cpu_dai_name nor cpu_name/of_node are set for %s\n", + link->name); + return -EINVAL; + } } return 0; @@ -962,7 +964,7 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct snd_soc_pcm_runtime *rtd; - struct snd_soc_dai_link_component *codec, *platform; + struct snd_soc_dai_link_component *codec, *platform, *cpu; struct snd_soc_component *component; int i, ret; @@ -987,14 +989,19 @@ int snd_soc_add_pcm_runtime(struct snd_soc_card *card, if (!rtd) return -ENOMEM; - /* FIXME: we need multi CPU support in the future */ - rtd->cpu_dai = snd_soc_find_dai(dai_link->cpus); - if (!rtd->cpu_dai) { - dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", - dai_link->cpus->dai_name); - goto _err_defer; + rtd->num_cpus = dai_link->num_cpus; + for_each_link_cpus(dai_link, i, cpu) { + rtd->cpu_dais[i] = snd_soc_find_dai(cpu); + if (!rtd->cpu_dais[i]) { + dev_info(card->dev, "ASoC: CPU DAI %s not registered\n", + cpu->dai_name); + goto _err_defer; + } + snd_soc_rtd_add_component(rtd, rtd->cpu_dais[i]->component); } - snd_soc_rtd_add_component(rtd, rtd->cpu_dai->component); + + /* Single cpu links expect cpu and cpu_dai in runtime data */ + rtd->cpu_dai = rtd->cpu_dais[0]; /* Find CODEC from registered CODECs */ rtd->num_codecs = dai_link->num_codecs; @@ -1114,7 +1121,8 @@ static int soc_init_pcm_runtime(struct snd_soc_card *card, dai_link->stream_name, ret); return ret; } - ret = soc_dai_pcm_new(&cpu_dai, 1, rtd); + ret = soc_dai_pcm_new(rtd->cpu_dais, + rtd->num_cpus, rtd); if (ret < 0) return ret; ret = soc_dai_pcm_new(rtd->codec_dais, @@ -1306,6 +1314,7 @@ static void soc_remove_link_dais(struct snd_soc_card *card) { int i; struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_pcm_runtime *rtd; int order; @@ -1315,14 +1324,15 @@ static void soc_remove_link_dais(struct snd_soc_card *card) for_each_rtd_codec_dai(rtd, i, codec_dai) soc_remove_dai(codec_dai, order); - soc_remove_dai(rtd->cpu_dai, order); + for_each_rtd_cpu_dai(rtd, i, cpu_dai) + soc_remove_dai(cpu_dai, order); } } } static int soc_probe_link_dais(struct snd_soc_card *card) { - struct snd_soc_dai *codec_dai; + struct snd_soc_dai *codec_dai, *cpu_dai; struct snd_soc_pcm_runtime *rtd; int i, order, ret; @@ -1333,9 +1343,12 @@ static int soc_probe_link_dais(struct snd_soc_card *card) "ASoC: probe %s dai link %d late %d\n", card->name, rtd->num, order); - ret = soc_probe_dai(rtd->cpu_dai, order); - if (ret) - return ret; + /* probe the CPU DAI */ + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + ret = soc_probe_dai(cpu_dai, order); + if (ret) + return ret; + } /* probe the CODEC DAI */ for_each_rtd_codec_dai(rtd, i, codec_dai) { @@ -1467,8 +1480,9 @@ static void soc_remove_aux_devices(struct snd_soc_card *card) int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int dai_fmt) { - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *cpu_dai; struct snd_soc_dai *codec_dai; + unsigned int inv_dai_fmt; unsigned int i; int ret; @@ -1485,33 +1499,33 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, * Flip the polarity for the "CPU" end of a CODEC<->CODEC link * the component which has non_legacy_dai_naming is Codec */ - if (cpu_dai->component->driver->non_legacy_dai_naming) { - unsigned int inv_dai_fmt; - - inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; - switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; - break; - case SND_SOC_DAIFMT_CBM_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; - break; - case SND_SOC_DAIFMT_CBS_CFM: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; - break; - case SND_SOC_DAIFMT_CBS_CFS: - inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; - break; - } - - dai_fmt = inv_dai_fmt; + inv_dai_fmt = dai_fmt & ~SND_SOC_DAIFMT_MASTER_MASK; + switch (dai_fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + break; + case SND_SOC_DAIFMT_CBM_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBS_CFM; + break; + case SND_SOC_DAIFMT_CBS_CFM: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFS; + break; + case SND_SOC_DAIFMT_CBS_CFS: + inv_dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + break; } + for_each_rtd_cpu_dai(rtd, i, cpu_dai) { + unsigned int fmt = dai_fmt; - ret = snd_soc_dai_set_fmt(cpu_dai, dai_fmt); - if (ret != 0 && ret != -ENOTSUPP) { - dev_warn(cpu_dai->dev, - "ASoC: Failed to set DAI format: %d\n", ret); - return ret; + if (cpu_dai->component->driver->non_legacy_dai_naming) + fmt = inv_dai_fmt; + + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret != 0 && ret != -ENOTSUPP) { + dev_warn(cpu_dai->dev, + "ASoC: Failed to set DAI format: %d\n", ret); + return ret; + } } return 0; -- cgit v1.2.3 From 36d73c4a9ed7c8a0988cfb9d1282c62d8c422a3b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 25 Feb 2020 11:00:40 -0600 Subject: ASoC: soc-dai: add get_sdw_stream() callback We only have a set() operation, provide the dual get() operation to retrieve the stream information. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200225170041.23644-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-dai.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 92c382690930..7f70db149b81 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -202,6 +202,8 @@ struct snd_soc_dai_ops { int (*set_sdw_stream)(struct snd_soc_dai *dai, void *stream, int direction); + void *(*get_sdw_stream)(struct snd_soc_dai *dai, int direction); + /* * DAI digital mute - optional. * Called by soc-core to minimise any pops. @@ -423,4 +425,23 @@ static inline int snd_soc_dai_set_sdw_stream(struct snd_soc_dai *dai, return -ENOTSUPP; } +/** + * snd_soc_dai_get_sdw_stream() - Retrieves SDW stream from DAI + * @dai: DAI + * @direction: Stream direction(Playback/Capture) + * + * This routine only retrieves that was previously configured + * with snd_soc_dai_get_sdw_stream() + * + * Returns pointer to stream or NULL; + */ +static inline void *snd_soc_dai_get_sdw_stream(struct snd_soc_dai *dai, + int direction) +{ + if (dai->driver->ops->get_sdw_stream) + return dai->driver->ops->get_sdw_stream(dai, direction); + else + return NULL; +} + #endif -- cgit v1.2.3 From feaa8baee82ababa46af95b03cfc28680ad647a6 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 24 Feb 2020 12:58:03 -0800 Subject: bus: ti-sysc: Implement SoC revision handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need to know SoC type and features for cases where the same SoC may be installed in various versions on the same board and would need a separate dts file otherwise for the different variants. For example, am3703 is pin compatible with omap3630, but has sgx and iva accelerators disabled. We must not try to access the sgx or iva module registers on am3703, and need to set the unavailable devices disabled early. Let's also detect omap3430 as that is needed for display subsystem (DSS) reset later on, and GP vs EMU or HS devices. Further SoC specific disabled device detection can be added as needed, such as dra71x vs dra76x rtc and usb4. Cc: Adam Ford Cc: André Hentschel Cc: H. Nikolaus Schaller Cc: Keerthy Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/pdata-quirks.c | 6 ++ drivers/bus/ti-sysc.c | 194 +++++++++++++++++++++++++++++++++- include/linux/platform_data/ti-sysc.h | 1 + 3 files changed, 200 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index dbb7c2acef31..2a4fe3e68b82 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -397,10 +397,16 @@ static int ti_sysc_shutdown_module(struct device *dev, return omap_hwmod_shutdown(cookie->data); } +static bool ti_sysc_soc_type_gp(void) +{ + return omap_type() == OMAP2_DEVICE_TYPE_GP; +} + static struct of_dev_auxdata omap_auxdata_lookup[]; static struct ti_sysc_platform_data ti_sysc_pdata = { .auxdata = omap_auxdata_lookup, + .soc_type_gp = ti_sysc_soc_type_gp, .init_clockdomain = ti_sysc_clkdm_init, .clkdm_deny_idle = ti_sysc_clkdm_deny_idle, .clkdm_allow_idle = ti_sysc_clkdm_allow_idle, diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 4e87eb5e8ed7..4c377c576582 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -15,15 +16,47 @@ #include #include #include +#include #include #include #include +#define DIS_ISP BIT(2) +#define DIS_IVA BIT(1) +#define DIS_SGX BIT(0) + +#define SOC_FLAG(match, flag) { .machine = match, .data = (void *)(flag), } + #define MAX_MODULE_SOFTRESET_WAIT 10000 -static const char * const reg_names[] = { "rev", "sysc", "syss", }; +enum sysc_soc { + SOC_UNKNOWN, + SOC_2420, + SOC_2430, + SOC_3430, + SOC_3630, + SOC_4430, + SOC_4460, + SOC_4470, + SOC_5430, + SOC_AM3, + SOC_AM4, + SOC_DRA7, +}; + +struct sysc_address { + unsigned long base; + struct list_head node; +}; + +struct sysc_soc_info { + unsigned long general_purpose:1; + enum sysc_soc soc; + struct mutex list_lock; /* disabled modules list lock */ + struct list_head disabled_modules; +}; enum sysc_clocks { SYSC_FCK, @@ -39,6 +72,8 @@ enum sysc_clocks { SYSC_MAX_CLOCKS, }; +static struct sysc_soc_info *sysc_soc; +static const char * const reg_names[] = { "rev", "sysc", "syss", }; static const char * const clock_names[SYSC_MAX_CLOCKS] = { "fck", "ick", "opt0", "opt1", "opt2", "opt3", "opt4", "opt5", "opt6", "opt7", @@ -2382,6 +2417,154 @@ static void ti_sysc_idle(struct work_struct *work) pm_runtime_put_sync(ddata->dev); } +/* + * SoC model and features detection. Only needed for SoCs that need + * special handling for quirks, no need to list others. + */ +static const struct soc_device_attribute sysc_soc_match[] = { + SOC_FLAG("OMAP242*", SOC_2420), + SOC_FLAG("OMAP243*", SOC_2430), + SOC_FLAG("OMAP3[45]*", SOC_3430), + SOC_FLAG("OMAP3[67]*", SOC_3630), + SOC_FLAG("OMAP443*", SOC_4430), + SOC_FLAG("OMAP446*", SOC_4460), + SOC_FLAG("OMAP447*", SOC_4470), + SOC_FLAG("OMAP54*", SOC_5430), + SOC_FLAG("AM433", SOC_AM3), + SOC_FLAG("AM43*", SOC_AM4), + SOC_FLAG("DRA7*", SOC_DRA7), + + { /* sentinel */ }, +}; + +/* + * List of SoCs variants with disabled features. By default we assume all + * devices in the device tree are available so no need to list those SoCs. + */ +static const struct soc_device_attribute sysc_soc_feat_match[] = { + /* OMAP3430/3530 and AM3517 variants with some accelerators disabled */ + SOC_FLAG("AM3505", DIS_SGX), + SOC_FLAG("OMAP3525", DIS_SGX), + SOC_FLAG("OMAP3515", DIS_IVA | DIS_SGX), + SOC_FLAG("OMAP3503", DIS_ISP | DIS_IVA | DIS_SGX), + + /* OMAP3630/DM3730 variants with some accelerators disabled */ + SOC_FLAG("AM3703", DIS_IVA | DIS_SGX), + SOC_FLAG("DM3725", DIS_SGX), + SOC_FLAG("OMAP3611", DIS_ISP | DIS_IVA | DIS_SGX), + SOC_FLAG("OMAP3615/AM3715", DIS_IVA), + SOC_FLAG("OMAP3621", DIS_ISP), + + { /* sentinel */ }, +}; + +static int sysc_add_disabled(unsigned long base) +{ + struct sysc_address *disabled_module; + + disabled_module = kzalloc(sizeof(*disabled_module), GFP_KERNEL); + if (!disabled_module) + return -ENOMEM; + + disabled_module->base = base; + + mutex_lock(&sysc_soc->list_lock); + list_add(&disabled_module->node, &sysc_soc->disabled_modules); + mutex_unlock(&sysc_soc->list_lock); + + return 0; +} + +/* + * One time init to detect the booted SoC and disable unavailable features. + * Note that we initialize static data shared across all ti-sysc instances + * so ddata is only used for SoC type. This can be called from module_init + * once we no longer need to rely on platform data. + */ +static int sysc_init_soc(struct sysc *ddata) +{ + const struct soc_device_attribute *match; + struct ti_sysc_platform_data *pdata; + unsigned long features = 0; + + if (sysc_soc) + return 0; + + sysc_soc = kzalloc(sizeof(*sysc_soc), GFP_KERNEL); + if (!sysc_soc) + return -ENOMEM; + + mutex_init(&sysc_soc->list_lock); + INIT_LIST_HEAD(&sysc_soc->disabled_modules); + sysc_soc->general_purpose = true; + + pdata = dev_get_platdata(ddata->dev); + if (pdata && pdata->soc_type_gp) + sysc_soc->general_purpose = pdata->soc_type_gp(); + + match = soc_device_match(sysc_soc_match); + if (match && match->data) + sysc_soc->soc = (int)match->data; + + match = soc_device_match(sysc_soc_feat_match); + if (!match) + return 0; + + if (match->data) + features = (unsigned long)match->data; + + /* + * Add disabled devices to the list based on the module base. + * Note that this must be done before we attempt to access the + * device and have module revision checks working. + */ + if (features & DIS_ISP) + sysc_add_disabled(0x480bd400); + if (features & DIS_IVA) + sysc_add_disabled(0x5d000000); + if (features & DIS_SGX) + sysc_add_disabled(0x50000000); + + return 0; +} + +static void sysc_cleanup_soc(void) +{ + struct sysc_address *disabled_module; + struct list_head *pos, *tmp; + + if (!sysc_soc) + return; + + mutex_lock(&sysc_soc->list_lock); + list_for_each_safe(pos, tmp, &sysc_soc->disabled_modules) { + disabled_module = list_entry(pos, struct sysc_address, node); + list_del(pos); + kfree(disabled_module); + } + mutex_unlock(&sysc_soc->list_lock); +} + +static int sysc_check_disabled_devices(struct sysc *ddata) +{ + struct sysc_address *disabled_module; + struct list_head *pos; + int error = 0; + + mutex_lock(&sysc_soc->list_lock); + list_for_each(pos, &sysc_soc->disabled_modules) { + disabled_module = list_entry(pos, struct sysc_address, node); + if (ddata->module_pa == disabled_module->base) { + dev_dbg(ddata->dev, "module disabled for this SoC\n"); + error = -ENODEV; + break; + } + } + mutex_unlock(&sysc_soc->list_lock); + + return error; +} + static const struct of_device_id sysc_match_table[] = { { .compatible = "simple-bus", }, { /* sentinel */ }, @@ -2400,6 +2583,10 @@ static int sysc_probe(struct platform_device *pdev) ddata->dev = &pdev->dev; platform_set_drvdata(pdev, ddata); + error = sysc_init_soc(ddata); + if (error) + return error; + error = sysc_init_match(ddata); if (error) return error; @@ -2430,6 +2617,10 @@ static int sysc_probe(struct platform_device *pdev) sysc_init_early_quirks(ddata); + error = sysc_check_disabled_devices(ddata); + if (error) + return error; + error = sysc_get_clocks(ddata); if (error) return error; @@ -2560,6 +2751,7 @@ static void __exit sysc_exit(void) { bus_unregister_notifier(&platform_bus_type, &sysc_nb); platform_driver_unregister(&sysc_driver); + sysc_cleanup_soc(); } module_exit(sysc_exit); diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 2cbde6542849..accab5325cf3 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -141,6 +141,7 @@ struct clk; struct ti_sysc_platform_data { struct of_dev_auxdata *auxdata; + bool (*soc_type_gp)(void); int (*init_clockdomain)(struct device *dev, struct clk *fck, struct clk *ick, struct ti_sysc_cookie *cookie); void (*clkdm_deny_idle)(struct device *dev, -- cgit v1.2.3 From e8639e1c986a8a9d0f94549170f6db579376c3ae Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 24 Feb 2020 12:58:03 -0800 Subject: bus: ti-sysc: Handle module unlock quirk needed for some RTC The RTC modules on am3 and am4 need quirk handling to unlock and lock them for reset so let's add the quirk handling based on what we already have for legacy platform data. In later patches we will simply drop the RTC related platform data and the old quirk handling. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 74 ++++++++++++++++++++++++++++++++--- include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 69 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 4c377c576582..6caa2222091e 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -110,6 +110,8 @@ static const char * const clock_names[SYSC_MAX_CLOCKS] = { * @reset_done_quirk: module specific reset done quirk * @module_enable_quirk: module specific enable quirk * @module_disable_quirk: module specific disable quirk + * @module_unlock_quirk: module specific sysconfig unlock quirk + * @module_lock_quirk: module specific sysconfig lock quirk */ struct sysc { struct device *dev; @@ -137,6 +139,8 @@ struct sysc { void (*reset_done_quirk)(struct sysc *sysc); void (*module_enable_quirk)(struct sysc *sysc); void (*module_disable_quirk)(struct sysc *sysc); + void (*module_unlock_quirk)(struct sysc *sysc); + void (*module_lock_quirk)(struct sysc *sysc); }; static void sysc_parse_dts_quirks(struct sysc *ddata, struct device_node *np, @@ -896,6 +900,22 @@ static void sysc_show_registers(struct sysc *ddata) buf); } +/** + * sysc_write_sysconfig - handle sysconfig quirks for register write + * @ddata: device driver data + * @value: register value + */ +static void sysc_write_sysconfig(struct sysc *ddata, u32 value) +{ + if (ddata->module_unlock_quirk) + ddata->module_unlock_quirk(ddata); + + sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], value); + + if (ddata->module_lock_quirk) + ddata->module_lock_quirk(ddata); +} + #define SYSC_IDLE_MASK (SYSC_NR_IDLEMODES - 1) #define SYSC_CLOCACT_ICK 2 @@ -942,7 +962,7 @@ static int sysc_enable_module(struct device *dev) reg &= ~(SYSC_IDLE_MASK << regbits->sidle_shift); reg |= best_mode << regbits->sidle_shift; - sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); + sysc_write_sysconfig(ddata, reg); set_midle: /* Set MIDLE mode */ @@ -961,14 +981,14 @@ set_midle: reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); reg |= best_mode << regbits->midle_shift; - sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); + sysc_write_sysconfig(ddata, reg); set_autoidle: /* Autoidle bit must enabled separately if available */ if (regbits->autoidle_shift >= 0 && ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) { reg |= 1 << regbits->autoidle_shift; - sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); + sysc_write_sysconfig(ddata, reg); } if (ddata->module_enable_quirk) @@ -1026,7 +1046,7 @@ static int sysc_disable_module(struct device *dev) reg &= ~(SYSC_IDLE_MASK << regbits->midle_shift); reg |= best_mode << regbits->midle_shift; - sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); + sysc_write_sysconfig(ddata, reg); set_sidle: /* Set SIDLE mode */ @@ -1049,7 +1069,7 @@ set_sidle: if (regbits->autoidle_shift >= 0 && ddata->cfg.sysc_val & BIT(regbits->autoidle_shift)) reg |= 1 << regbits->autoidle_shift; - sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); + sysc_write_sysconfig(ddata, reg); return 0; } @@ -1301,6 +1321,8 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("gpu", 0x50000000, 0x14, -ENODEV, -ENODEV, 0x00010201, 0xffffffff, 0), SYSC_QUIRK("gpu", 0x50000000, 0xfe00, 0xfe10, -ENODEV, 0x40000000 , 0xffffffff, SYSC_MODULE_QUIRK_SGX), + SYSC_QUIRK("rtc", 0, 0x74, 0x78, -ENODEV, 0x4eb01908, 0xffff00f0, + SYSC_MODULE_QUIRK_RTC_UNLOCK), SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050, 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), SYSC_QUIRK("usb_otg_hs", 0, 0, 0x10, -ENODEV, 0x4ea2080d, 0xffffffff, @@ -1356,7 +1378,6 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("slimbus", 0, 0, 0x10, -ENODEV, 0x40002903, 0xffffffff, 0), SYSC_QUIRK("spinlock", 0, 0, 0x10, -ENODEV, 0x50020000, 0xffffffff, 0), SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -ENODEV, 0x00000020, 0xffffffff, 0), - SYSC_QUIRK("rtc", 0, 0x74, 0x78, -ENODEV, 0x4eb01908, 0xffff00f0, 0), SYSC_QUIRK("timer32k", 0, 0, 0x4, -ENODEV, 0x00000060, 0xffffffff, 0), SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0), SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000008, 0xffffffff, 0), @@ -1478,6 +1499,40 @@ static void sysc_post_reset_quirk_i2c(struct sysc *ddata) sysc_clk_quirk_i2c(ddata, true); } +/* RTC on am3 and 4 needs to be unlocked and locked for sysconfig */ +static void sysc_quirk_rtc(struct sysc *ddata, bool lock) +{ + u32 val, kick0_val = 0, kick1_val = 0; + unsigned long flags; + int error; + + if (!lock) { + kick0_val = 0x83e70b13; + kick1_val = 0x95a4f1e0; + } + + local_irq_save(flags); + /* RTC_STATUS BUSY bit may stay active for 1/32768 seconds (~30 usec) */ + error = readl_poll_timeout(ddata->module_va + 0x44, val, + !(val & BIT(0)), 100, 50); + if (error) + dev_warn(ddata->dev, "rtc busy timeout\n"); + /* Now we have ~15 microseconds to read/write various registers */ + sysc_write(ddata, 0x6c, kick0_val); + sysc_write(ddata, 0x70, kick1_val); + local_irq_restore(flags); +} + +static void sysc_module_unlock_quirk_rtc(struct sysc *ddata) +{ + sysc_quirk_rtc(ddata, false); +} + +static void sysc_module_lock_quirk_rtc(struct sysc *ddata) +{ + sysc_quirk_rtc(ddata, true); +} + /* 36xx SGX needs a quirk for to bypass OCP IPG interrupt logic */ static void sysc_module_enable_quirk_sgx(struct sysc *ddata) { @@ -1532,6 +1587,13 @@ static void sysc_init_module_quirks(struct sysc *ddata) if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_AESS) ddata->module_enable_quirk = sysc_module_enable_quirk_aess; + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_RTC_UNLOCK) { + ddata->module_unlock_quirk = sysc_module_unlock_quirk_rtc; + ddata->module_lock_quirk = sysc_module_lock_quirk_rtc; + + return; + } + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_SGX) ddata->module_enable_quirk = sysc_module_enable_quirk_sgx; diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index accab5325cf3..0b33c3b7302f 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -49,6 +49,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_MODULE_QUIRK_RTC_UNLOCK BIT(22) #define SYSC_QUIRK_CLKDM_NOAUTO BIT(21) #define SYSC_QUIRK_FORCE_MSTANDBY BIT(20) #define SYSC_MODULE_QUIRK_AESS BIT(19) -- cgit v1.2.3 From 7324a7a0d5e232551eedad69fea3e4b91973d7c6 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 24 Feb 2020 12:58:03 -0800 Subject: bus: ti-sysc: Implement display subsystem reset quirk The display subsystem (DSS) needs the child outputs disabled for reset. In order to prepare to probe DSS without legacy platform data, let's implement sysc_pre_reset_quirk_dss() similar to what we have for the platform data with omap_dss_reset(). Note that we cannot directly use the old omap_dss_reset() without platform data callbacks and updating omap_dss_reset() to understand struct device. And we will be dropping omap_dss_reset() anyways when all the SoCs are probing with device tree, so let's not mess with the legacy code at all. Cc: Jyri Sarha Cc: Laurent Pinchart Cc: Tomi Valkeinen Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 131 +++++++++++++++++++++++++++++++++- include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 129 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index b29f4e451dc1..e30c97ca5579 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -1303,11 +1303,11 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK("dcan", 0x48480000, 0x20, -ENODEV, -ENODEV, 0xa3170504, 0xffffffff, SYSC_QUIRK_CLKDM_NOAUTO), SYSC_QUIRK("dss", 0x4832a000, 0, 0x10, 0x14, 0x00000020, 0xffffffff, - SYSC_QUIRK_OPT_CLKS_IN_RESET), + SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000040, 0xffffffff, - SYSC_QUIRK_OPT_CLKS_IN_RESET), + SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), SYSC_QUIRK("dss", 0x58000000, 0, -ENODEV, 0x14, 0x00000061, 0xffffffff, - SYSC_QUIRK_OPT_CLKS_IN_RESET), + SYSC_QUIRK_OPT_CLKS_IN_RESET | SYSC_MODULE_QUIRK_DSS_RESET), SYSC_QUIRK("dwc3", 0x48880000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, SYSC_QUIRK_CLKDM_NOAUTO), SYSC_QUIRK("dwc3", 0x488c0000, 0, 0x10, -ENODEV, 0x500a0200, 0xffffffff, @@ -1468,6 +1468,128 @@ static void sysc_init_revision_quirks(struct sysc *ddata) } } +/* + * DSS needs dispc outputs disabled to reset modules. Returns mask of + * enabled DSS interrupts. Eventually we may be able to do this on + * dispc init rather than top-level DSS init. + */ +static u32 sysc_quirk_dispc(struct sysc *ddata, int dispc_offset, + bool disable) +{ + bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false; + const int lcd_en_mask = BIT(0), digit_en_mask = BIT(1); + int manager_count; + bool framedonetv_irq; + u32 val, irq_mask = 0; + + switch (sysc_soc->soc) { + case SOC_2420 ... SOC_3630: + manager_count = 2; + framedonetv_irq = false; + break; + case SOC_4430 ... SOC_4470: + manager_count = 3; + break; + case SOC_5430: + case SOC_DRA7: + manager_count = 4; + break; + case SOC_AM4: + manager_count = 1; + break; + case SOC_UNKNOWN: + default: + return 0; + }; + + /* Remap the whole module range to be able to reset dispc outputs */ + devm_iounmap(ddata->dev, ddata->module_va); + ddata->module_va = devm_ioremap(ddata->dev, + ddata->module_pa, + ddata->module_size); + if (!ddata->module_va) + return -EIO; + + /* DISP_CONTROL */ + val = sysc_read(ddata, dispc_offset + 0x40); + lcd_en = val & lcd_en_mask; + digit_en = val & digit_en_mask; + if (lcd_en) + irq_mask |= BIT(0); /* FRAMEDONE */ + if (digit_en) { + if (framedonetv_irq) + irq_mask |= BIT(24); /* FRAMEDONETV */ + else + irq_mask |= BIT(2) | BIT(3); /* EVSYNC bits */ + } + if (disable & (lcd_en | digit_en)) + sysc_write(ddata, dispc_offset + 0x40, + val & ~(lcd_en_mask | digit_en_mask)); + + if (manager_count <= 2) + return irq_mask; + + /* DISPC_CONTROL2 */ + val = sysc_read(ddata, dispc_offset + 0x238); + lcd2_en = val & lcd_en_mask; + if (lcd2_en) + irq_mask |= BIT(22); /* FRAMEDONE2 */ + if (disable && lcd2_en) + sysc_write(ddata, dispc_offset + 0x238, + val & ~lcd_en_mask); + + if (manager_count <= 3) + return irq_mask; + + /* DISPC_CONTROL3 */ + val = sysc_read(ddata, dispc_offset + 0x848); + lcd3_en = val & lcd_en_mask; + if (lcd3_en) + irq_mask |= BIT(30); /* FRAMEDONE3 */ + if (disable && lcd3_en) + sysc_write(ddata, dispc_offset + 0x848, + val & ~lcd_en_mask); + + return irq_mask; +} + +/* DSS needs child outputs disabled and SDI registers cleared for reset */ +static void sysc_pre_reset_quirk_dss(struct sysc *ddata) +{ + const int dispc_offset = 0x1000; + int error; + u32 irq_mask, val; + + /* Get enabled outputs */ + irq_mask = sysc_quirk_dispc(ddata, dispc_offset, false); + if (!irq_mask) + return; + + /* Clear IRQSTATUS */ + sysc_write(ddata, 0x1000 + 0x18, irq_mask); + + /* Disable outputs */ + val = sysc_quirk_dispc(ddata, dispc_offset, true); + + /* Poll IRQSTATUS */ + error = readl_poll_timeout(ddata->module_va + dispc_offset + 0x18, + val, val != irq_mask, 100, 50); + if (error) + dev_warn(ddata->dev, "%s: timed out %08x !+ %08x\n", + __func__, val, irq_mask); + + if (sysc_soc->soc == SOC_3430) { + /* Clear DSS_SDI_CONTROL */ + sysc_write(ddata, dispc_offset + 0x44, 0); + + /* Clear DSS_PLL_CONTROL */ + sysc_write(ddata, dispc_offset + 0x48, 0); + } + + /* Clear DSS_CONTROL to switch DSS clock sources to PRCM if not */ + sysc_write(ddata, dispc_offset + 0x40, 0); +} + /* 1-wire needs module's internal clocks enabled for reset */ static void sysc_pre_reset_quirk_hdq1w(struct sysc *ddata) { @@ -1606,6 +1728,9 @@ static void sysc_init_module_quirks(struct sysc *ddata) if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_AESS) ddata->module_enable_quirk = sysc_module_enable_quirk_aess; + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_DSS_RESET) + ddata->pre_reset_quirk = sysc_pre_reset_quirk_dss; + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_RTC_UNLOCK) { ddata->module_unlock_quirk = sysc_module_unlock_quirk_rtc; ddata->module_lock_quirk = sysc_module_lock_quirk_rtc; diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 0b33c3b7302f..ecd3a979a14d 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -49,6 +49,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_MODULE_QUIRK_DSS_RESET BIT(23) #define SYSC_MODULE_QUIRK_RTC_UNLOCK BIT(22) #define SYSC_QUIRK_CLKDM_NOAUTO BIT(21) #define SYSC_QUIRK_FORCE_MSTANDBY BIT(20) -- cgit v1.2.3 From f70ce185687bbe4e2d7ff126a8c890631f5fc2af Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 27 Feb 2020 04:37:11 +0100 Subject: sysfs: add sysfs_file_change_owner() Add helpers to change the owner of a sysfs files. This function will be used to correctly account for kobject ownership changes, e.g. when moving network devices between network namespaces. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: David S. Miller --- fs/sysfs/file.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 10 ++++++++++ 2 files changed, 57 insertions(+) (limited to 'include') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 130fc6fbcc03..4ca936ca3ba4 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -558,3 +558,50 @@ void sysfs_remove_bin_file(struct kobject *kobj, kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); + +static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid, + kgid_t kgid) +{ + struct iattr newattrs = { + .ia_valid = ATTR_UID | ATTR_GID, + .ia_uid = kuid, + .ia_gid = kgid, + }; + return kernfs_setattr(kn, &newattrs); +} + +/** + * sysfs_file_change_owner - change owner of a sysfs file. + * @kobj: object. + * @name: name of the file to change. + * @kuid: new owner's kuid + * @kgid: new owner's kgid + * + * This function looks up the sysfs entry @name under @kobj and changes the + * ownership to @kuid/@kgid. + * + * Returns 0 on success or error code on failure. + */ +int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, + kgid_t kgid) +{ + struct kernfs_node *kn; + int error; + + if (!name) + return -EINVAL; + + if (!kobj->state_in_sysfs) + return -EINVAL; + + kn = kernfs_find_and_get(kobj->sd, name); + if (!kn) + return -ENOENT; + + error = internal_change_owner(kn, kuid, kgid); + + kernfs_put(kn); + + return error; +} +EXPORT_SYMBOL_GPL(sysfs_file_change_owner); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fa7ee503fb76..a7884024a911 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -310,6 +310,9 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn) return kernfs_enable_ns(kn); } +int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, + kgid_t kgid); + #else /* CONFIG_SYSFS */ static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) @@ -522,6 +525,13 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn) { } +static inline int sysfs_file_change_owner(struct kobject *kobj, + const char *name, kuid_t kuid, + kgid_t kgid) +{ + return 0; +} + #endif /* CONFIG_SYSFS */ static inline int __must_check sysfs_create_file(struct kobject *kobj, -- cgit v1.2.3 From 0666a3aee762cd4f7981c2eed0fd8cab87533539 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 27 Feb 2020 04:37:12 +0100 Subject: sysfs: add sysfs_link_change_owner() Add a helper to change the owner of a sysfs link. This function will be used to correctly account for kobject ownership changes, e.g. when moving network devices between network namespaces. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: David S. Miller --- fs/sysfs/file.c | 41 +++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 10 ++++++++++ 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 4ca936ca3ba4..332cd69b378c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -570,6 +570,47 @@ static int internal_change_owner(struct kernfs_node *kn, kuid_t kuid, return kernfs_setattr(kn, &newattrs); } +/** + * sysfs_link_change_owner - change owner of a sysfs file. + * @kobj: object of the kernfs_node the symlink is located in. + * @targ: object of the kernfs_node the symlink points to. + * @name: name of the link. + * @kuid: new owner's kuid + * @kgid: new owner's kgid + * + * This function looks up the sysfs symlink entry @name under @kobj and changes + * the ownership to @kuid/@kgid. The symlink is looked up in the namespace of + * @targ. + * + * Returns 0 on success or error code on failure. + */ +int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ, + const char *name, kuid_t kuid, kgid_t kgid) +{ + struct kernfs_node *kn = NULL; + int error; + + if (!name || !kobj->state_in_sysfs || !targ->state_in_sysfs) + return -EINVAL; + + error = -ENOENT; + kn = kernfs_find_and_get_ns(kobj->sd, name, targ->sd->ns); + if (!kn) + goto out; + + error = -EINVAL; + if (kernfs_type(kn) != KERNFS_LINK) + goto out; + if (kn->symlink.target_kn->priv != targ) + goto out; + + error = internal_change_owner(kn, kuid, kgid); + +out: + kernfs_put(kn); + return error; +} + /** * sysfs_file_change_owner - change owner of a sysfs file. * @kobj: object. diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index a7884024a911..7e15ebfd750e 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -312,6 +312,8 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn) int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, kgid_t kgid); +int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ, + const char *name, kuid_t kuid, kgid_t kgid); #else /* CONFIG_SYSFS */ @@ -532,6 +534,14 @@ static inline int sysfs_file_change_owner(struct kobject *kobj, return 0; } +static inline int sysfs_link_change_owner(struct kobject *kobj, + struct kobject *targ, + const char *name, kuid_t kuid, + kgid_t kgid) +{ + return 0; +} + #endif /* CONFIG_SYSFS */ static inline int __must_check sysfs_create_file(struct kobject *kobj, -- cgit v1.2.3 From 303a42769c4c4d8e5e3ad928df87eb36f8c1fa60 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 27 Feb 2020 04:37:13 +0100 Subject: sysfs: add sysfs_group{s}_change_owner() Add helpers to change the owner of sysfs groups. This function will be used to correctly account for kobject ownership changes, e.g. when moving network devices between network namespaces. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: David S. Miller --- fs/sysfs/group.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 20 +++++++++ 2 files changed, 135 insertions(+) (limited to 'include') diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index c4ab045926b7..5afe0e7ff7cd 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "sysfs.h" @@ -457,3 +458,117 @@ int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, return PTR_ERR_OR_ZERO(link); } EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj); + +static int sysfs_group_attrs_change_owner(struct kernfs_node *grp_kn, + const struct attribute_group *grp, + struct iattr *newattrs) +{ + struct kernfs_node *kn; + int error; + + if (grp->attrs) { + struct attribute *const *attr; + + for (attr = grp->attrs; *attr; attr++) { + kn = kernfs_find_and_get(grp_kn, (*attr)->name); + if (!kn) + return -ENOENT; + + error = kernfs_setattr(kn, newattrs); + kernfs_put(kn); + if (error) + return error; + } + } + + if (grp->bin_attrs) { + struct bin_attribute *const *bin_attr; + + for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) { + kn = kernfs_find_and_get(grp_kn, (*bin_attr)->attr.name); + if (!kn) + return -ENOENT; + + error = kernfs_setattr(kn, newattrs); + kernfs_put(kn); + if (error) + return error; + } + } + + return 0; +} + +/** + * sysfs_group_change_owner - change owner of an attribute group. + * @kobj: The kobject containing the group. + * @grp: The attribute group. + * @kuid: new owner's kuid + * @kgid: new owner's kgid + * + * Returns 0 on success or error code on failure. + */ +int sysfs_group_change_owner(struct kobject *kobj, + const struct attribute_group *grp, kuid_t kuid, + kgid_t kgid) +{ + struct kernfs_node *grp_kn; + int error; + struct iattr newattrs = { + .ia_valid = ATTR_UID | ATTR_GID, + .ia_uid = kuid, + .ia_gid = kgid, + }; + + if (!kobj->state_in_sysfs) + return -EINVAL; + + if (grp->name) { + grp_kn = kernfs_find_and_get(kobj->sd, grp->name); + } else { + kernfs_get(kobj->sd); + grp_kn = kobj->sd; + } + if (!grp_kn) + return -ENOENT; + + error = kernfs_setattr(grp_kn, &newattrs); + if (!error) + error = sysfs_group_attrs_change_owner(grp_kn, grp, &newattrs); + + kernfs_put(grp_kn); + + return error; +} +EXPORT_SYMBOL_GPL(sysfs_group_change_owner); + +/** + * sysfs_groups_change_owner - change owner of a set of attribute groups. + * @kobj: The kobject containing the groups. + * @groups: The attribute groups. + * @kuid: new owner's kuid + * @kgid: new owner's kgid + * + * Returns 0 on success or error code on failure. + */ +int sysfs_groups_change_owner(struct kobject *kobj, + const struct attribute_group **groups, + kuid_t kuid, kgid_t kgid) +{ + int error = 0, i; + + if (!kobj->state_in_sysfs) + return -EINVAL; + + if (!groups) + return 0; + + for (i = 0; groups[i]; i++) { + error = sysfs_group_change_owner(kobj, groups[i], kuid, kgid); + if (error) + break; + } + + return error; +} +EXPORT_SYMBOL_GPL(sysfs_groups_change_owner); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 7e15ebfd750e..3fcaabdb05ef 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -314,6 +314,12 @@ int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, kgid_t kgid); int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ, const char *name, kuid_t kuid, kgid_t kgid); +int sysfs_groups_change_owner(struct kobject *kobj, + const struct attribute_group **groups, + kuid_t kuid, kgid_t kgid); +int sysfs_group_change_owner(struct kobject *kobj, + const struct attribute_group *groups, kuid_t kuid, + kgid_t kgid); #else /* CONFIG_SYSFS */ @@ -542,6 +548,20 @@ static inline int sysfs_link_change_owner(struct kobject *kobj, return 0; } +static inline int sysfs_groups_change_owner(struct kobject *kobj, + const struct attribute_group **groups, + kuid_t kuid, kgid_t kgid) +{ + return 0; +} + +static inline int sysfs_group_change_owner(struct kobject *kobj, + const struct attribute_group **groups, + kuid_t kuid, kgid_t kgid) +{ + return 0; +} + #endif /* CONFIG_SYSFS */ static inline int __must_check sysfs_create_file(struct kobject *kobj, -- cgit v1.2.3 From 2c4f9401ceb00167a3bfd322a28aa87b646a253f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 27 Feb 2020 04:37:14 +0100 Subject: sysfs: add sysfs_change_owner() Add a helper to change the owner of sysfs objects. This function will be used to correctly account for kobject ownership changes, e.g. when moving network devices between network namespaces. This mirrors how a kobject is added through driver core which in its guts is done via kobject_add_internal() which in summary creates the main directory via create_dir(), populates that directory with the groups associated with the ktype of the kobject (if any) and populates the directory with the basic attributes associated with the ktype of the kobject (if any). These are the basic steps that are associated with adding a kobject in sysfs. Any additional properties are added by the specific subsystem itself (not by driver core) after it has registered the device. So for the example of network devices, a network device will e.g. register a queue subdirectory under the basic sysfs directory for the network device and than further subdirectories within that queues subdirectory. But that is all specific to network devices and they call the corresponding sysfs functions to do that directly when they create those queue objects. So anything that a subsystem adds outside of what driver core does must also be changed by it (That's already true for removal of files it created outside of driver core.) and it's the same for ownership changes. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: David S. Miller --- fs/sysfs/file.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 6 ++++++ 2 files changed, 66 insertions(+) (limited to 'include') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 332cd69b378c..26bbf960e2a2 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -646,3 +646,63 @@ int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, return error; } EXPORT_SYMBOL_GPL(sysfs_file_change_owner); + +/** + * sysfs_change_owner - change owner of the given object. + * @kobj: object. + * @kuid: new owner's kuid + * @kgid: new owner's kgid + * + * Change the owner of the default directory, files, groups, and attributes of + * @kobj to @kuid/@kgid. Note that sysfs_change_owner mirrors how the sysfs + * entries for a kobject are added by driver core. In summary, + * sysfs_change_owner() takes care of the default directory entry for @kobj, + * the default attributes associated with the ktype of @kobj and the default + * attributes associated with the ktype of @kobj. + * Additional properties not added by driver core have to be changed by the + * driver or subsystem which created them. This is similar to how + * driver/subsystem specific entries are removed. + * + * Returns 0 on success or error code on failure. + */ +int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid) +{ + int error; + const struct kobj_type *ktype; + + if (!kobj->state_in_sysfs) + return -EINVAL; + + /* Change the owner of the kobject itself. */ + error = internal_change_owner(kobj->sd, kuid, kgid); + if (error) + return error; + + ktype = get_ktype(kobj); + if (ktype) { + struct attribute **kattr; + + /* + * Change owner of the default attributes associated with the + * ktype of @kobj. + */ + for (kattr = ktype->default_attrs; kattr && *kattr; kattr++) { + error = sysfs_file_change_owner(kobj, (*kattr)->name, + kuid, kgid); + if (error) + return error; + } + + /* + * Change owner of the default groups associated with the + * ktype of @kobj. + */ + error = sysfs_groups_change_owner(kobj, ktype->default_groups, + kuid, kgid); + if (error) + return error; + } + + return 0; +} +EXPORT_SYMBOL_GPL(sysfs_change_owner); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 3fcaabdb05ef..9e531ec76274 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -312,6 +312,7 @@ static inline void sysfs_enable_ns(struct kernfs_node *kn) int sysfs_file_change_owner(struct kobject *kobj, const char *name, kuid_t kuid, kgid_t kgid); +int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid); int sysfs_link_change_owner(struct kobject *kobj, struct kobject *targ, const char *name, kuid_t kuid, kgid_t kgid); int sysfs_groups_change_owner(struct kobject *kobj, @@ -548,6 +549,11 @@ static inline int sysfs_link_change_owner(struct kobject *kobj, return 0; } +static inline int sysfs_change_owner(struct kobject *kobj, kuid_t kuid, kgid_t kgid) +{ + return 0; +} + static inline int sysfs_groups_change_owner(struct kobject *kobj, const struct attribute_group **groups, kuid_t kuid, kgid_t kgid) -- cgit v1.2.3 From b8f33e5d76a7a1b87e0cc760d05bf2477b4e91d6 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Thu, 27 Feb 2020 04:37:15 +0100 Subject: device: add device_change_owner() Add a helper to change the owner of a device's sysfs entries. This needs to happen when the ownership of a device is changed, e.g. when moving network devices between network namespaces. This function will be used to correctly account for ownership changes, e.g. when moving network devices between network namespaces. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Christian Brauner Signed-off-by: David S. Miller --- drivers/base/core.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 1 + 2 files changed, 117 insertions(+) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 42a672456432..988f34ce2eb0 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3458,6 +3458,122 @@ out: } EXPORT_SYMBOL_GPL(device_move); +static int device_attrs_change_owner(struct device *dev, kuid_t kuid, + kgid_t kgid) +{ + struct kobject *kobj = &dev->kobj; + struct class *class = dev->class; + const struct device_type *type = dev->type; + int error; + + if (class) { + /* + * Change the device groups of the device class for @dev to + * @kuid/@kgid. + */ + error = sysfs_groups_change_owner(kobj, class->dev_groups, kuid, + kgid); + if (error) + return error; + } + + if (type) { + /* + * Change the device groups of the device type for @dev to + * @kuid/@kgid. + */ + error = sysfs_groups_change_owner(kobj, type->groups, kuid, + kgid); + if (error) + return error; + } + + /* Change the device groups of @dev to @kuid/@kgid. */ + error = sysfs_groups_change_owner(kobj, dev->groups, kuid, kgid); + if (error) + return error; + + if (device_supports_offline(dev) && !dev->offline_disabled) { + /* Change online device attributes of @dev to @kuid/@kgid. */ + error = sysfs_file_change_owner(kobj, dev_attr_online.attr.name, + kuid, kgid); + if (error) + return error; + } + + return 0; +} + +/** + * device_change_owner - change the owner of an existing device. + * @dev: device. + * @kuid: new owner's kuid + * @kgid: new owner's kgid + * + * This changes the owner of @dev and its corresponding sysfs entries to + * @kuid/@kgid. This function closely mirrors how @dev was added via driver + * core. + * + * Returns 0 on success or error code on failure. + */ +int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid) +{ + int error; + struct kobject *kobj = &dev->kobj; + + dev = get_device(dev); + if (!dev) + return -EINVAL; + + /* + * Change the kobject and the default attributes and groups of the + * ktype associated with it to @kuid/@kgid. + */ + error = sysfs_change_owner(kobj, kuid, kgid); + if (error) + goto out; + + /* + * Change the uevent file for @dev to the new owner. The uevent file + * was created in a separate step when @dev got added and we mirror + * that step here. + */ + error = sysfs_file_change_owner(kobj, dev_attr_uevent.attr.name, kuid, + kgid); + if (error) + goto out; + + /* + * Change the device groups, the device groups associated with the + * device class, and the groups associated with the device type of @dev + * to @kuid/@kgid. + */ + error = device_attrs_change_owner(dev, kuid, kgid); + if (error) + goto out; + +#ifdef CONFIG_BLOCK + if (sysfs_deprecated && dev->class == &block_class) + goto out; +#endif + + /* + * Change the owner of the symlink located in the class directory of + * the device class associated with @dev which points to the actual + * directory entry for @dev to @kuid/@kgid. This ensures that the + * symlink shows the same permissions as its target. + */ + error = sysfs_link_change_owner(&dev->class->p->subsys.kobj, &dev->kobj, + dev_name(dev), kuid, kgid); + if (error) + goto out; + +out: + put_device(dev); + return error; +} +EXPORT_SYMBOL_GPL(device_change_owner); + /** * device_shutdown - call ->shutdown() on each device to shutdown. */ diff --git a/include/linux/device.h b/include/linux/device.h index 0cd7c647c16c..3e40533d2037 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -817,6 +817,7 @@ extern struct device *device_find_child_by_name(struct device *parent, extern int device_rename(struct device *dev, const char *new_name); extern int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); +extern int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid); extern const char *device_get_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid, const char **tmp); -- cgit v1.2.3 From cc2550b421aa30e3da67e5a7f6d14f3ecd3527b3 Mon Sep 17 00:00:00 2001 From: afzal mohammed Date: Thu, 27 Feb 2020 16:29:02 +0530 Subject: clocksource: Replace setup_irq() by request_irq() request_irq() is preferred over setup_irq(). The early boot setup_irq() invocations happen either via 'init_IRQ()' or 'time_init()', while memory allocators are ready by 'mm_init()'. Per tglx[1], setup_irq() existed in olden days when allocators were not ready by the time early interrupts were initialized. Hence replace setup_irq() by request_irq(). Seldom remove_irq() usage has been observed coupled with setup_irq(), wherever that has been found, it too has been replaced by free_irq(). A build error that was reported by kbuild test robot in the previous version of the patch also has been fixed. [1] https://lkml.kernel.org/r/alpine.DEB.2.20.1710191609480.1971@nanos Signed-off-by: afzal mohammed Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/91961c77c1cf93d41523f5e1ac52043f32f97077.1582799709.git.afzal.mohd.ma@gmail.com --- drivers/clocksource/bcm2835_timer.c | 8 ++--- drivers/clocksource/bcm_kona_timer.c | 10 ++----- drivers/clocksource/dw_apb_timer.c | 11 ++----- drivers/clocksource/exynos_mct.c | 12 +++----- drivers/clocksource/mxs_timer.c | 10 ++----- drivers/clocksource/nomadik-mtu.c | 11 ++----- drivers/clocksource/samsung_pwm_timer.c | 12 +++----- drivers/clocksource/timer-atlas7.c | 50 ++++++++++++++----------------- drivers/clocksource/timer-cs5535.c | 10 ++----- drivers/clocksource/timer-efm32.c | 10 ++----- drivers/clocksource/timer-fsl-ftm.c | 10 ++----- drivers/clocksource/timer-imx-gpt.c | 10 ++----- drivers/clocksource/timer-integrator-ap.c | 11 ++----- drivers/clocksource/timer-meson6.c | 11 ++----- drivers/clocksource/timer-orion.c | 9 ++---- drivers/clocksource/timer-prima2.c | 14 +++------ drivers/clocksource/timer-pxa.c | 10 ++----- drivers/clocksource/timer-sp804.c | 11 ++----- drivers/clocksource/timer-u300.c | 9 ++---- drivers/clocksource/timer-vf-pit.c | 10 ++----- drivers/clocksource/timer-vt8500.c | 11 ++----- drivers/clocksource/timer-zevio.c | 13 ++++---- include/linux/dw_apb_timer.h | 1 - 23 files changed, 83 insertions(+), 191 deletions(-) (limited to 'include') diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index b235f446ee50..1592650b2c92 100644 --- a/drivers/clocksource/bcm2835_timer.c +++ b/drivers/clocksource/bcm2835_timer.c @@ -31,7 +31,6 @@ struct bcm2835_timer { void __iomem *compare; int match_mask; struct clock_event_device evt; - struct irqaction act; }; static void __iomem *system_clock __read_mostly; @@ -113,12 +112,9 @@ static int __init bcm2835_timer_init(struct device_node *node) timer->evt.features = CLOCK_EVT_FEAT_ONESHOT; timer->evt.set_next_event = bcm2835_time_set_next_event; timer->evt.cpumask = cpumask_of(0); - timer->act.name = node->name; - timer->act.flags = IRQF_TIMER | IRQF_SHARED; - timer->act.dev_id = timer; - timer->act.handler = bcm2835_time_interrupt; - ret = setup_irq(irq, &timer->act); + ret = request_irq(irq, bcm2835_time_interrupt, IRQF_TIMER | IRQF_SHARED, + node->name, timer); if (ret) { pr_err("Can't set up timer IRQ\n"); goto err_timer_free; diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c index 5c40be9880f5..a50ab5c2154f 100644 --- a/drivers/clocksource/bcm_kona_timer.c +++ b/drivers/clocksource/bcm_kona_timer.c @@ -160,12 +160,6 @@ static irqreturn_t kona_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction kona_timer_irq = { - .name = "Kona Timer Tick", - .flags = IRQF_TIMER, - .handler = kona_timer_interrupt, -}; - static int __init kona_timer_init(struct device_node *node) { u32 freq; @@ -192,7 +186,9 @@ static int __init kona_timer_init(struct device_node *node) kona_timer_disable_and_clear(timers.tmr_regs); kona_timer_clockevents_init(); - setup_irq(timers.tmr_irq, &kona_timer_irq); + if (request_irq(timers.tmr_irq, kona_timer_interrupt, IRQF_TIMER, + "Kona Timer Tick", NULL)) + pr_err("%s: request_irq() failed\n", "Kona Timer Tick"); kona_timer_set_next_event((arch_timer_rate / HZ), NULL); return 0; diff --git a/drivers/clocksource/dw_apb_timer.c b/drivers/clocksource/dw_apb_timer.c index 654766538f93..b207a77b0831 100644 --- a/drivers/clocksource/dw_apb_timer.c +++ b/drivers/clocksource/dw_apb_timer.c @@ -270,15 +270,10 @@ dw_apb_clockevent_init(int cpu, const char *name, unsigned rating, dw_ced->ced.rating = rating; dw_ced->ced.name = name; - dw_ced->irqaction.name = dw_ced->ced.name; - dw_ced->irqaction.handler = dw_apb_clockevent_irq; - dw_ced->irqaction.dev_id = &dw_ced->ced; - dw_ced->irqaction.irq = irq; - dw_ced->irqaction.flags = IRQF_TIMER | IRQF_IRQPOLL | - IRQF_NOBALANCING; - dw_ced->eoi = apbt_eoi; - err = setup_irq(irq, &dw_ced->irqaction); + err = request_irq(irq, dw_apb_clockevent_irq, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dw_ced->ced.name, &dw_ced->ced); if (err) { pr_err("failed to request timer irq\n"); kfree(dw_ced); diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index a267fe31ef13..fabad79baafc 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -329,19 +329,15 @@ static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mct_comp_event_irq = { - .name = "mct_comp_irq", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = exynos4_mct_comp_isr, - .dev_id = &mct_comp_device, -}; - static int exynos4_clockevent_init(void) { mct_comp_device.cpumask = cpumask_of(0); clockevents_config_and_register(&mct_comp_device, clk_rate, 0xf, 0xffffffff); - setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq); + if (request_irq(mct_irqs[MCT_G0_IRQ], exynos4_mct_comp_isr, + IRQF_TIMER | IRQF_IRQPOLL, "mct_comp_irq", + &mct_comp_device)) + pr_err("%s: request_irq() failed\n", "mct_comp_irq"); return 0; } diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c index f6ddae30933f..bc96a4cbf26c 100644 --- a/drivers/clocksource/mxs_timer.c +++ b/drivers/clocksource/mxs_timer.c @@ -117,13 +117,6 @@ static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mxs_timer_irq = { - .name = "MXS Timer Tick", - .dev_id = &mxs_clockevent_device, - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = mxs_timer_interrupt, -}; - static void mxs_irq_clear(char *state) { /* Disable interrupt in timer module */ @@ -277,6 +270,7 @@ static int __init mxs_timer_init(struct device_node *np) if (irq <= 0) return -EINVAL; - return setup_irq(irq, &mxs_timer_irq); + return request_irq(irq, mxs_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "MXS Timer Tick", &mxs_clockevent_device); } TIMER_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init); diff --git a/drivers/clocksource/nomadik-mtu.c b/drivers/clocksource/nomadik-mtu.c index 3f7fa8c01367..f49a631d8f58 100644 --- a/drivers/clocksource/nomadik-mtu.c +++ b/drivers/clocksource/nomadik-mtu.c @@ -181,13 +181,6 @@ static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction nmdk_timer_irq = { - .name = "Nomadik Timer Tick", - .flags = IRQF_TIMER, - .handler = nmdk_timer_interrupt, - .dev_id = &nmdk_clkevt, -}; - static int __init nmdk_timer_init(void __iomem *base, int irq, struct clk *pclk, struct clk *clk) { @@ -232,7 +225,9 @@ static int __init nmdk_timer_init(void __iomem *base, int irq, sched_clock_register(nomadik_read_sched_clock, 32, rate); /* Timer 1 is used for events, register irq and clockevents */ - setup_irq(irq, &nmdk_timer_irq); + if (request_irq(irq, nmdk_timer_interrupt, IRQF_TIMER, + "Nomadik Timer Tick", &nmdk_clkevt)) + pr_err("%s: request_irq() failed\n", "Nomadik Timer Tick"); nmdk_clkevt.cpumask = cpumask_of(0); nmdk_clkevt.irq = irq; clockevents_config_and_register(&nmdk_clkevt, rate, 2, 0xffffffffU); diff --git a/drivers/clocksource/samsung_pwm_timer.c b/drivers/clocksource/samsung_pwm_timer.c index dae1b2b5a0c5..f760229d0c7f 100644 --- a/drivers/clocksource/samsung_pwm_timer.c +++ b/drivers/clocksource/samsung_pwm_timer.c @@ -256,13 +256,6 @@ static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction samsung_clock_event_irq = { - .name = "samsung_time_irq", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = samsung_clock_event_isr, - .dev_id = &time_event_device, -}; - static void __init samsung_clockevent_init(void) { unsigned long pclk; @@ -282,7 +275,10 @@ static void __init samsung_clockevent_init(void) clock_rate, 1, pwm.tcnt_max); irq_number = pwm.irq[pwm.event_id]; - setup_irq(irq_number, &samsung_clock_event_irq); + if (request_irq(irq_number, samsung_clock_event_isr, + IRQF_TIMER | IRQF_IRQPOLL, "samsung_time_irq", + &time_event_device)) + pr_err("%s: request_irq() failed\n", "samsung_time_irq"); if (pwm.variant.has_tint_cstat) { u32 mask = (1 << pwm.event_id); diff --git a/drivers/clocksource/timer-atlas7.c b/drivers/clocksource/timer-atlas7.c index 93c3ac6d72bd..c21c91c2bc56 100644 --- a/drivers/clocksource/timer-atlas7.c +++ b/drivers/clocksource/timer-atlas7.c @@ -159,29 +159,23 @@ static struct clocksource sirfsoc_clocksource = { .resume = sirfsoc_clocksource_resume, }; -static struct irqaction sirfsoc_timer_irq = { - .name = "sirfsoc_timer0", - .flags = IRQF_TIMER | IRQF_NOBALANCING, - .handler = sirfsoc_timer_interrupt, -}; - -static struct irqaction sirfsoc_timer1_irq = { - .name = "sirfsoc_timer1", - .flags = IRQF_TIMER | IRQF_NOBALANCING, - .handler = sirfsoc_timer_interrupt, -}; +static unsigned int sirfsoc_timer_irq, sirfsoc_timer1_irq; static int sirfsoc_local_timer_starting_cpu(unsigned int cpu) { struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu); - struct irqaction *action; - - if (cpu == 0) - action = &sirfsoc_timer_irq; - else - action = &sirfsoc_timer1_irq; + unsigned int irq; + const char *name; + + if (cpu == 0) { + irq = sirfsoc_timer_irq; + name = "sirfsoc_timer0"; + } else { + irq = sirfsoc_timer1_irq; + name = "sirfsoc_timer1"; + } - ce->irq = action->irq; + ce->irq = irq; ce->name = "local_timer"; ce->features = CLOCK_EVT_FEAT_ONESHOT; ce->rating = 200; @@ -196,9 +190,9 @@ static int sirfsoc_local_timer_starting_cpu(unsigned int cpu) ce->min_delta_ticks = 2; ce->cpumask = cpumask_of(cpu); - action->dev_id = ce; - BUG_ON(setup_irq(ce->irq, action)); - irq_force_affinity(action->irq, cpumask_of(cpu)); + BUG_ON(request_irq(ce->irq, sirfsoc_timer_interrupt, + IRQF_TIMER | IRQF_NOBALANCING, name, ce)); + irq_force_affinity(ce->irq, cpumask_of(cpu)); clockevents_register_device(ce); return 0; @@ -206,12 +200,14 @@ static int sirfsoc_local_timer_starting_cpu(unsigned int cpu) static int sirfsoc_local_timer_dying_cpu(unsigned int cpu) { + struct clock_event_device *ce = per_cpu_ptr(sirfsoc_clockevent, cpu); + sirfsoc_timer_count_disable(1); if (cpu == 0) - remove_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq); + free_irq(sirfsoc_timer_irq, ce); else - remove_irq(sirfsoc_timer1_irq.irq, &sirfsoc_timer1_irq); + free_irq(sirfsoc_timer1_irq, ce); return 0; } @@ -268,14 +264,14 @@ static int __init sirfsoc_of_timer_init(struct device_node *np) return -ENXIO; } - sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); - if (!sirfsoc_timer_irq.irq) { + sirfsoc_timer_irq = irq_of_parse_and_map(np, 0); + if (!sirfsoc_timer_irq) { pr_err("No irq passed for timer0 via DT\n"); return -EINVAL; } - sirfsoc_timer1_irq.irq = irq_of_parse_and_map(np, 1); - if (!sirfsoc_timer1_irq.irq) { + sirfsoc_timer1_irq = irq_of_parse_and_map(np, 1); + if (!sirfsoc_timer1_irq) { pr_err("No irq passed for timer1 via DT\n"); return -EINVAL; } diff --git a/drivers/clocksource/timer-cs5535.c b/drivers/clocksource/timer-cs5535.c index 8f6bc536bef2..51ea0509fb25 100644 --- a/drivers/clocksource/timer-cs5535.c +++ b/drivers/clocksource/timer-cs5535.c @@ -131,12 +131,6 @@ static irqreturn_t mfgpt_tick(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction mfgptirq = { - .handler = mfgpt_tick, - .flags = IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED, - .name = DRV_NAME, -}; - static int __init cs5535_mfgpt_init(void) { struct cs5535_mfgpt_timer *timer; @@ -158,7 +152,9 @@ static int __init cs5535_mfgpt_init(void) } /* And register it with the kernel */ - ret = setup_irq(timer_irq, &mfgptirq); + ret = request_irq(timer_irq, mfgpt_tick, + IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED, + DRV_NAME, NULL); if (ret) { printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n"); goto err_irq; diff --git a/drivers/clocksource/timer-efm32.c b/drivers/clocksource/timer-efm32.c index 5a22cb079ad3..441a4b916841 100644 --- a/drivers/clocksource/timer-efm32.c +++ b/drivers/clocksource/timer-efm32.c @@ -119,13 +119,6 @@ static struct efm32_clock_event_ddata clock_event_ddata = { }, }; -static struct irqaction efm32_clock_event_irq = { - .name = "efm32 clockevent", - .flags = IRQF_TIMER, - .handler = efm32_clock_event_handler, - .dev_id = &clock_event_ddata, -}; - static int __init efm32_clocksource_init(struct device_node *np) { struct clk *clk; @@ -230,7 +223,8 @@ static int __init efm32_clockevent_init(struct device_node *np) DIV_ROUND_CLOSEST(rate, 1024), 0xf, 0xffff); - ret = setup_irq(irq, &efm32_clock_event_irq); + ret = request_irq(irq, efm32_clock_event_handler, IRQF_TIMER, + "efm32 clockevent", &clock_event_ddata); if (ret) { pr_err("Failed setup irq\n"); goto err_setup_irq; diff --git a/drivers/clocksource/timer-fsl-ftm.c b/drivers/clocksource/timer-fsl-ftm.c index a9d9a3ca5996..12a2ed7cfaff 100644 --- a/drivers/clocksource/timer-fsl-ftm.c +++ b/drivers/clocksource/timer-fsl-ftm.c @@ -176,13 +176,6 @@ static struct clock_event_device ftm_clockevent = { .rating = 300, }; -static struct irqaction ftm_timer_irq = { - .name = "Freescale ftm timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = ftm_evt_interrupt, - .dev_id = &ftm_clockevent, -}; - static int __init ftm_clockevent_init(unsigned long freq, int irq) { int err; @@ -192,7 +185,8 @@ static int __init ftm_clockevent_init(unsigned long freq, int irq) ftm_reset_counter(priv->clkevt_base); - err = setup_irq(irq, &ftm_timer_irq); + err = request_irq(irq, ftm_evt_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "Freescale ftm timer", &ftm_clockevent); if (err) { pr_err("ftm: setup irq failed: %d\n", err); return err; diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c index 706c0d0ff56c..7b2c70f2f353 100644 --- a/drivers/clocksource/timer-imx-gpt.c +++ b/drivers/clocksource/timer-imx-gpt.c @@ -67,7 +67,6 @@ struct imx_timer { struct clk *clk_ipg; const struct imx_gpt_data *gpt; struct clock_event_device ced; - struct irqaction act; }; struct imx_gpt_data { @@ -273,7 +272,6 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id) static int __init mxc_clockevent_init(struct imx_timer *imxtm) { struct clock_event_device *ced = &imxtm->ced; - struct irqaction *act = &imxtm->act; ced->name = "mxc_timer1"; ced->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_DYNIRQ; @@ -287,12 +285,8 @@ static int __init mxc_clockevent_init(struct imx_timer *imxtm) clockevents_config_and_register(ced, clk_get_rate(imxtm->clk_per), 0xff, 0xfffffffe); - act->name = "i.MX Timer Tick"; - act->flags = IRQF_TIMER | IRQF_IRQPOLL; - act->handler = mxc_timer_interrupt; - act->dev_id = ced; - - return setup_irq(imxtm->irq, act); + return request_irq(imxtm->irq, mxc_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "i.MX Timer Tick", ced); } static void imx1_gpt_setup_tctl(struct imx_timer *imxtm) diff --git a/drivers/clocksource/timer-integrator-ap.c b/drivers/clocksource/timer-integrator-ap.c index c90a69c7b5fa..b0fcbaac58b0 100644 --- a/drivers/clocksource/timer-integrator-ap.c +++ b/drivers/clocksource/timer-integrator-ap.c @@ -123,13 +123,6 @@ static struct clock_event_device integrator_clockevent = { .rating = 300, }; -static struct irqaction integrator_timer_irq = { - .name = "timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = integrator_timer_interrupt, - .dev_id = &integrator_clockevent, -}; - static int integrator_clockevent_init(unsigned long inrate, void __iomem *base, int irq) { @@ -149,7 +142,9 @@ static int integrator_clockevent_init(unsigned long inrate, timer_reload = rate / HZ; writel(ctrl, clkevt_base + TIMER_CTRL); - ret = setup_irq(irq, &integrator_timer_irq); + ret = request_irq(irq, integrator_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "timer", + &integrator_clockevent); if (ret) return ret; diff --git a/drivers/clocksource/timer-meson6.c b/drivers/clocksource/timer-meson6.c index 9e8b467c71da..99f5510a2b56 100644 --- a/drivers/clocksource/timer-meson6.c +++ b/drivers/clocksource/timer-meson6.c @@ -150,13 +150,6 @@ static irqreturn_t meson6_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction meson6_timer_irq = { - .name = "meson6_timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = meson6_timer_interrupt, - .dev_id = &meson6_clockevent, -}; - static int __init meson6_timer_init(struct device_node *node) { u32 val; @@ -194,7 +187,9 @@ static int __init meson6_timer_init(struct device_node *node) /* Stop the timer A */ meson6_clkevt_time_stop(); - ret = setup_irq(irq, &meson6_timer_irq); + ret = request_irq(irq, meson6_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "meson6_timer", + &meson6_clockevent); if (ret) { pr_warn("failed to setup irq %d\n", irq); return ret; diff --git a/drivers/clocksource/timer-orion.c b/drivers/clocksource/timer-orion.c index 7d487107e3cd..d01ff4181867 100644 --- a/drivers/clocksource/timer-orion.c +++ b/drivers/clocksource/timer-orion.c @@ -114,12 +114,6 @@ static irqreturn_t orion_clkevt_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction orion_clkevt_irq = { - .name = "orion_event", - .flags = IRQF_TIMER, - .handler = orion_clkevt_irq_handler, -}; - static int __init orion_timer_init(struct device_node *np) { unsigned long rate; @@ -172,7 +166,8 @@ static int __init orion_timer_init(struct device_node *np) sched_clock_register(orion_read_sched_clock, 32, rate); /* setup timer1 as clockevent timer */ - ret = setup_irq(irq, &orion_clkevt_irq); + ret = request_irq(irq, orion_clkevt_irq_handler, IRQF_TIMER, + "orion_event", NULL); if (ret) { pr_err("%pOFn: unable to setup irq\n", np); return ret; diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c index d4a9dcf5fba2..c5d469342a9d 100644 --- a/drivers/clocksource/timer-prima2.c +++ b/drivers/clocksource/timer-prima2.c @@ -165,14 +165,6 @@ static struct clocksource sirfsoc_clocksource = { .resume = sirfsoc_clocksource_resume, }; -static struct irqaction sirfsoc_timer_irq = { - .name = "sirfsoc_timer0", - .flags = IRQF_TIMER, - .irq = 0, - .handler = sirfsoc_timer_interrupt, - .dev_id = &sirfsoc_clockevent, -}; - /* Overwrite weak default sched_clock with more precise one */ static u64 notrace sirfsoc_read_sched_clock(void) { @@ -190,6 +182,7 @@ static void __init sirfsoc_clockevent_init(void) static int __init sirfsoc_prima2_timer_init(struct device_node *np) { unsigned long rate; + unsigned int irq; struct clk *clk; int ret; @@ -218,7 +211,7 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np) return -ENXIO; } - sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0); + irq = irq_of_parse_and_map(np, 0); writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1, sirfsoc_timer_base + SIRFSOC_TIMER_DIV); @@ -234,7 +227,8 @@ static int __init sirfsoc_prima2_timer_init(struct device_node *np) sched_clock_register(sirfsoc_read_sched_clock, 64, PRIMA2_CLOCK_FREQ); - ret = setup_irq(sirfsoc_timer_irq.irq, &sirfsoc_timer_irq); + ret = request_irq(irq, sirfsoc_timer_interrupt, IRQF_TIMER, + "sirfsoc_timer0", &sirfsoc_clockevent); if (ret) { pr_err("Failed to setup irq\n"); return ret; diff --git a/drivers/clocksource/timer-pxa.c b/drivers/clocksource/timer-pxa.c index 913a5d354a1f..7ad0e5adb2ff 100644 --- a/drivers/clocksource/timer-pxa.c +++ b/drivers/clocksource/timer-pxa.c @@ -143,13 +143,6 @@ static struct clock_event_device ckevt_pxa_osmr0 = { .resume = pxa_timer_resume, }; -static struct irqaction pxa_ost0_irq = { - .name = "ost0", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = pxa_ost0_interrupt, - .dev_id = &ckevt_pxa_osmr0, -}; - static int __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate) { int ret; @@ -161,7 +154,8 @@ static int __init pxa_timer_common_init(int irq, unsigned long clock_tick_rate) ckevt_pxa_osmr0.cpumask = cpumask_of(0); - ret = setup_irq(irq, &pxa_ost0_irq); + ret = request_irq(irq, pxa_ost0_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "ost0", &ckevt_pxa_osmr0); if (ret) { pr_err("Failed to setup irq\n"); return ret; diff --git a/drivers/clocksource/timer-sp804.c b/drivers/clocksource/timer-sp804.c index 9c841980eed1..5cd0abf9b396 100644 --- a/drivers/clocksource/timer-sp804.c +++ b/drivers/clocksource/timer-sp804.c @@ -168,13 +168,6 @@ static struct clock_event_device sp804_clockevent = { .rating = 300, }; -static struct irqaction sp804_timer_irq = { - .name = "timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = sp804_timer_interrupt, - .dev_id = &sp804_clockevent, -}; - int __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct clk *clk, const char *name) { struct clock_event_device *evt = &sp804_clockevent; @@ -200,7 +193,9 @@ int __init __sp804_clockevents_init(void __iomem *base, unsigned int irq, struct writel(0, base + TIMER_CTRL); - setup_irq(irq, &sp804_timer_irq); + if (request_irq(irq, sp804_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "timer", &sp804_clockevent)) + pr_err("%s: request_irq() failed\n", "timer"); clockevents_config_and_register(evt, rate, 0xf, 0xffffffff); return 0; diff --git a/drivers/clocksource/timer-u300.c b/drivers/clocksource/timer-u300.c index 32adc3057dda..37cba8dfd45f 100644 --- a/drivers/clocksource/timer-u300.c +++ b/drivers/clocksource/timer-u300.c @@ -330,12 +330,6 @@ static irqreturn_t u300_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction u300_timer_irq = { - .name = "U300 Timer Tick", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = u300_timer_interrupt, -}; - /* * Override the global weak sched_clock symbol with this * local implementation which uses the clocksource to get some @@ -420,7 +414,8 @@ static int __init u300_timer_init_of(struct device_node *np) u300_timer_base + U300_TIMER_APP_RGPT1); /* Set up the IRQ handler */ - ret = setup_irq(irq, &u300_timer_irq); + ret = request_irq(irq, u300_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "U300 Timer Tick", NULL); if (ret) return ret; diff --git a/drivers/clocksource/timer-vf-pit.c b/drivers/clocksource/timer-vf-pit.c index fef0bb4e0c8c..7ad4a8b008c2 100644 --- a/drivers/clocksource/timer-vf-pit.c +++ b/drivers/clocksource/timer-vf-pit.c @@ -123,19 +123,13 @@ static struct clock_event_device clockevent_pit = { .rating = 300, }; -static struct irqaction pit_timer_irq = { - .name = "VF pit timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = pit_timer_interrupt, - .dev_id = &clockevent_pit, -}; - static int __init pit_clockevent_init(unsigned long rate, int irq) { __raw_writel(0, clkevt_base + PITTCTRL); __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); - BUG_ON(setup_irq(irq, &pit_timer_irq)); + BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, + "VF pit timer", &clockevent_pit); clockevent_pit.cpumask = cpumask_of(0); clockevent_pit.irq = irq; diff --git a/drivers/clocksource/timer-vt8500.c b/drivers/clocksource/timer-vt8500.c index bb424bcefbb3..a469b1b5f972 100644 --- a/drivers/clocksource/timer-vt8500.c +++ b/drivers/clocksource/timer-vt8500.c @@ -101,13 +101,6 @@ static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction irq = { - .name = "vt8500_timer", - .flags = IRQF_TIMER | IRQF_IRQPOLL, - .handler = vt8500_timer_interrupt, - .dev_id = &clockevent, -}; - static int __init vt8500_timer_init(struct device_node *np) { int timer_irq, ret; @@ -139,7 +132,9 @@ static int __init vt8500_timer_init(struct device_node *np) clockevent.cpumask = cpumask_of(0); - ret = setup_irq(timer_irq, &irq); + ret = request_irq(timer_irq, vt8500_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, "vt8500_timer", + &clockevent); if (ret) { pr_err("%s: setup_irq failed for %s\n", __func__, clockevent.name); diff --git a/drivers/clocksource/timer-zevio.c b/drivers/clocksource/timer-zevio.c index c0041561f1be..ecaa3568841c 100644 --- a/drivers/clocksource/timer-zevio.c +++ b/drivers/clocksource/timer-zevio.c @@ -53,7 +53,6 @@ struct zevio_timer { struct clk *clk; struct clock_event_device clkevt; - struct irqaction clkevt_irq; char clocksource_name[64]; char clockevent_name[64]; @@ -172,12 +171,12 @@ static int __init zevio_timer_add(struct device_node *node) /* Interrupt to occur when timer value matches 0 */ writel(0, timer->base + IO_MATCH(TIMER_MATCH)); - timer->clkevt_irq.name = timer->clockevent_name; - timer->clkevt_irq.handler = zevio_timer_interrupt; - timer->clkevt_irq.dev_id = timer; - timer->clkevt_irq.flags = IRQF_TIMER | IRQF_IRQPOLL; - - setup_irq(irqnr, &timer->clkevt_irq); + if (request_irq(irqnr, zevio_timer_interrupt, + IRQF_TIMER | IRQF_IRQPOLL, + timer->clockevent_name, timer)) { + pr_err("%s: request_irq() failed\n", + timer->clockevent_name); + } clockevents_config_and_register(&timer->clkevt, clk_get_rate(timer->clk), 0x0001, 0xffff); diff --git a/include/linux/dw_apb_timer.h b/include/linux/dw_apb_timer.h index 14f072edbca5..82ebf9223948 100644 --- a/include/linux/dw_apb_timer.h +++ b/include/linux/dw_apb_timer.h @@ -25,7 +25,6 @@ struct dw_apb_timer { struct dw_apb_clock_event_device { struct clock_event_device ced; struct dw_apb_timer timer; - struct irqaction irqaction; void (*eoi)(struct dw_apb_timer *); }; -- cgit v1.2.3 From 06ee7a950b6a342cd79590e7243bdda850141967 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Dec 2019 21:07:52 -0600 Subject: ARM: OMAP2+: pm33xx-core: Add cpuidle_ops for am335x/am437x am335x and am437x can now make use of the generic cpuidle-arm driver. This requires that we define init and suspend ops to be passed set as the cpuidle ops for the SoC. These ops are invoked directly at the last stage of the cpuidle-arm driver in order to allow low level platform code to run and bring the CPU the rest of the way into it's desired idle state. It is required that the CPUIDLE_METHOD_OF_DECLARE be called from code that is built in so define these ops in pm33xx-core where the always built-in portion of the PM code for these SoCs lives. Additionally, although an soc_suspend function is already exposed by the pm33xx platform code, it contains additional operations needed for full SoC suspend beyond what is needed for a relatively simple CPU suspend needed during cpuidle. To get around this introduce cpu_suspend ops to be used by the am335x and am437x PM driver for the last stage of cpuidle path. Acked-by: Santosh Shilimkar Signed-off-by: Dave Gerlach Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/pm33xx-core.c | 117 ++++++++++++++++++++++++++++++++++- include/linux/platform_data/pm33xx.h | 3 + 2 files changed, 119 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c index 7461b0346549..b36654186c79 100644 --- a/arch/arm/mach-omap2/pm33xx-core.c +++ b/arch/arm/mach-omap2/pm33xx-core.c @@ -6,11 +6,14 @@ * Dave Gerlach */ +#include +#include +#include #include #include #include -#include #include +#include #include #include #include @@ -35,6 +38,14 @@ static struct clockdomain *gfx_l4ls_clkdm; static void __iomem *scu_base; static struct omap_hwmod *rtc_oh; +static int (*idle_fn)(u32 wfi_flags); + +struct amx3_idle_state { + int wfi_flags; +}; + +static struct amx3_idle_state *idle_states; + static int am43xx_map_scu(void) { scu_base = ioremap(scu_a9_get_base(), SZ_256); @@ -201,6 +212,43 @@ static int am43xx_suspend(unsigned int state, int (*fn)(unsigned long), return ret; } +static int am33xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args) +{ + int ret = 0; + + if (omap_irq_pending() || need_resched()) + return ret; + + ret = cpu_suspend(args, fn); + + return ret; +} + +static int am43xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args) +{ + int ret = 0; + + if (!scu_base) + return 0; + + scu_power_mode(scu_base, SCU_PM_DORMANT); + ret = cpu_suspend(args, fn); + scu_power_mode(scu_base, SCU_PM_NORMAL); + + return ret; +} + +static void amx3_begin_suspend(void) +{ + cpu_idle_poll_ctrl(true); +} + +static void amx3_finish_suspend(void) +{ + cpu_idle_poll_ctrl(false); +} + + static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void) { if (soc_is_am33xx()) @@ -254,6 +302,9 @@ static void am43xx_prepare_rtc_resume(void) static struct am33xx_pm_platform_data am33xx_ops = { .init = am33xx_suspend_init, .soc_suspend = am33xx_suspend, + .cpu_suspend = am33xx_cpu_suspend, + .begin_suspend = amx3_begin_suspend, + .finish_suspend = amx3_finish_suspend, .get_sram_addrs = amx3_get_sram_addrs, .save_context = am33xx_save_context, .restore_context = am33xx_restore_context, @@ -266,6 +317,9 @@ static struct am33xx_pm_platform_data am33xx_ops = { static struct am33xx_pm_platform_data am43xx_ops = { .init = am43xx_suspend_init, .soc_suspend = am43xx_suspend, + .cpu_suspend = am43xx_cpu_suspend, + .begin_suspend = amx3_begin_suspend, + .finish_suspend = amx3_finish_suspend, .get_sram_addrs = amx3_get_sram_addrs, .save_context = am43xx_save_context, .restore_context = am43xx_restore_context, @@ -301,3 +355,64 @@ int __init amx3_common_pm_init(void) return 0; } + +static int __init amx3_idle_init(struct device_node *cpu_node, int cpu) +{ + struct device_node *state_node; + struct amx3_idle_state states[CPUIDLE_STATE_MAX]; + int i; + int state_count = 1; + + for (i = 0; ; i++) { + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + if (!state_node) + break; + + if (!of_device_is_available(state_node)) + continue; + + if (i == CPUIDLE_STATE_MAX) { + pr_warn("%s: cpuidle states reached max possible\n", + __func__); + break; + } + + states[state_count].wfi_flags = 0; + + if (of_property_read_bool(state_node, "ti,idle-wkup-m3")) + states[state_count].wfi_flags |= WFI_FLAG_WAKE_M3 | + WFI_FLAG_FLUSH_CACHE; + + state_count++; + } + + idle_states = kcalloc(state_count, sizeof(*idle_states), GFP_KERNEL); + if (!idle_states) + return -ENOMEM; + + for (i = 1; i < state_count; i++) + idle_states[i].wfi_flags = states[i].wfi_flags; + + return 0; +} + +static int amx3_idle_enter(unsigned long index) +{ + struct amx3_idle_state *idle_state = &idle_states[index]; + + if (!idle_state) + return -EINVAL; + + if (idle_fn) + idle_fn(idle_state->wfi_flags); + + return 0; +} + +static struct cpuidle_ops amx3_cpuidle_ops __initdata = { + .init = amx3_idle_init, + .suspend = amx3_idle_enter, +}; + +CPUIDLE_METHOD_OF_DECLARE(pm33xx_idle, "ti,am3352", &amx3_cpuidle_ops); +CPUIDLE_METHOD_OF_DECLARE(pm43xx_idle, "ti,am4372", &amx3_cpuidle_ops); diff --git a/include/linux/platform_data/pm33xx.h b/include/linux/platform_data/pm33xx.h index dd5971937a64..8e59f2db2adc 100644 --- a/include/linux/platform_data/pm33xx.h +++ b/include/linux/platform_data/pm33xx.h @@ -49,6 +49,9 @@ struct am33xx_pm_platform_data { int (*init)(void); int (*soc_suspend)(unsigned int state, int (*fn)(unsigned long), unsigned long args); + int (*cpu_suspend)(int (*fn)(unsigned long), unsigned long args); + void (*begin_suspend)(void); + void (*finish_suspend)(void); struct am33xx_pm_sram_addr *(*get_sram_addrs)(void); void __iomem *(*get_rtc_base_addr)(void); void (*save_context)(void); -- cgit v1.2.3 From 65880ab160838e0764138894ef4450abdbed4af5 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Thu, 12 Dec 2019 21:07:53 -0600 Subject: ARM: OMAP2+: pm33xx-core: Extend platform_data ops for cpuidle In order for am335x and am437x to properly enter deeper c-states in cpuidle they must always call into the sleep33/43xx suspend code and also sometimes invoke the wkup_m3_ipc driver. These are both controlled by the pm33xx module so we must provide a method for the platform code to call back into the module when it is available as the core cpuidle ops that are invoked by the cpuidle-arm driver must remain as built in. Extend the init platform op to take an idle function as an argument so that we can use this to call into the pm33xx module for c-states that need it. Also add a deinit op so we can unregister this idle function from the PM core when the pm33xx module gets unloaded. Acked-by: Santosh Shilimkar Signed-off-by: Dave Gerlach Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/pm33xx-core.c | 20 +++++++++++++++----- drivers/soc/ti/pm33xx.c | 2 +- include/linux/platform_data/pm33xx.h | 3 ++- 3 files changed, 18 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c index b36654186c79..5455fc98c60e 100644 --- a/arch/arm/mach-omap2/pm33xx-core.c +++ b/arch/arm/mach-omap2/pm33xx-core.c @@ -79,7 +79,7 @@ static int am43xx_check_off_mode_enable(void) return 0; } -static int amx3_common_init(void) +static int amx3_common_init(int (*idle)(u32 wfi_flags)) { gfx_pwrdm = pwrdm_lookup("gfx_pwrdm"); per_pwrdm = pwrdm_lookup("per_pwrdm"); @@ -99,10 +99,12 @@ static int amx3_common_init(void) else omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF); + idle_fn = idle; + return 0; } -static int am33xx_suspend_init(void) +static int am33xx_suspend_init(int (*idle)(u32 wfi_flags)) { int ret; @@ -113,12 +115,12 @@ static int am33xx_suspend_init(void) return -ENODEV; } - ret = amx3_common_init(); + ret = amx3_common_init(idle); return ret; } -static int am43xx_suspend_init(void) +static int am43xx_suspend_init(int (*idle)(u32 wfi_flags)) { int ret = 0; @@ -128,11 +130,17 @@ static int am43xx_suspend_init(void) return ret; } - ret = amx3_common_init(); + ret = amx3_common_init(idle); return ret; } +static int amx3_suspend_deinit(void) +{ + idle_fn = NULL; + return 0; +} + static void amx3_pre_suspend_common(void) { omap_set_pwrdm_state(gfx_pwrdm, PWRDM_POWER_OFF); @@ -301,6 +309,7 @@ static void am43xx_prepare_rtc_resume(void) static struct am33xx_pm_platform_data am33xx_ops = { .init = am33xx_suspend_init, + .deinit = amx3_suspend_deinit, .soc_suspend = am33xx_suspend, .cpu_suspend = am33xx_cpu_suspend, .begin_suspend = amx3_begin_suspend, @@ -316,6 +325,7 @@ static struct am33xx_pm_platform_data am33xx_ops = { static struct am33xx_pm_platform_data am43xx_ops = { .init = am43xx_suspend_init, + .deinit = amx3_suspend_deinit, .soc_suspend = am43xx_suspend, .cpu_suspend = am43xx_cpu_suspend, .begin_suspend = amx3_begin_suspend, diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index ccc6d53fe788..19bdcaca1f21 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -503,7 +503,7 @@ static int am33xx_pm_probe(struct platform_device *pdev) suspend_wfi_flags |= WFI_FLAG_WAKE_M3; #endif /* CONFIG_SUSPEND */ - ret = pm_ops->init(); + ret = pm_ops->init(NULL); if (ret) { dev_err(dev, "Unable to call core pm init!\n"); ret = -ENODEV; diff --git a/include/linux/platform_data/pm33xx.h b/include/linux/platform_data/pm33xx.h index 8e59f2db2adc..644af1d89cfa 100644 --- a/include/linux/platform_data/pm33xx.h +++ b/include/linux/platform_data/pm33xx.h @@ -46,7 +46,8 @@ struct am33xx_pm_sram_addr { }; struct am33xx_pm_platform_data { - int (*init)(void); + int (*init)(int (*idle)(u32 wfi_flags)); + int (*deinit)(void); int (*soc_suspend)(unsigned int state, int (*fn)(unsigned long), unsigned long args); int (*cpu_suspend)(int (*fn)(unsigned long), unsigned long args); -- cgit v1.2.3 From 29b40f105ec8d555984c1f72dc9133b122e51903 Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Mon, 30 Sep 2019 04:19:18 -0400 Subject: KVM: s390: protvirt: Add initial vm and cpu lifecycle handling This contains 3 main changes: 1. changes in SIE control block handling for secure guests 2. helper functions for create/destroy/unpack secure guests 3. KVM_S390_PV_COMMAND ioctl to allow userspace dealing with secure machines Signed-off-by: Janosch Frank Reviewed-by: David Hildenbrand Reviewed-by: Cornelia Huck [borntraeger@de.ibm.com: patch merging, splitting, fixing] Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 24 +++- arch/s390/include/asm/uv.h | 69 ++++++++++ arch/s390/kvm/Makefile | 2 +- arch/s390/kvm/kvm-s390.c | 214 ++++++++++++++++++++++++++++++- arch/s390/kvm/kvm-s390.h | 33 +++++ arch/s390/kvm/pv.c | 266 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 31 +++++ 7 files changed, 635 insertions(+), 4 deletions(-) create mode 100644 arch/s390/kvm/pv.c (limited to 'include') diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index d058289385a5..1aa2382fe363 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -160,7 +160,13 @@ struct kvm_s390_sie_block { __u8 reserved08[4]; /* 0x0008 */ #define PROG_IN_SIE (1<<0) __u32 prog0c; /* 0x000c */ - __u8 reserved10[16]; /* 0x0010 */ + union { + __u8 reserved10[16]; /* 0x0010 */ + struct { + __u64 pv_handle_cpu; + __u64 pv_handle_config; + }; + }; #define PROG_BLOCK_SIE (1<<0) #define PROG_REQUEST (1<<1) atomic_t prog20; /* 0x0020 */ @@ -233,7 +239,7 @@ struct kvm_s390_sie_block { #define ECB3_RI 0x01 __u8 ecb3; /* 0x0063 */ __u32 scaol; /* 0x0064 */ - __u8 reserved68; /* 0x0068 */ + __u8 sdf; /* 0x0068 */ __u8 epdx; /* 0x0069 */ __u8 reserved6a[2]; /* 0x006a */ __u32 todpr; /* 0x006c */ @@ -645,6 +651,11 @@ struct kvm_guestdbg_info_arch { unsigned long last_bp; }; +struct kvm_s390_pv_vcpu { + u64 handle; + unsigned long stor_base; +}; + struct kvm_vcpu_arch { struct kvm_s390_sie_block *sie_block; /* if vsie is active, currently executed shadow sie control block */ @@ -673,6 +684,7 @@ struct kvm_vcpu_arch { __u64 cputm_start; bool gs_enabled; bool skey_enabled; + struct kvm_s390_pv_vcpu pv; }; struct kvm_vm_stat { @@ -843,6 +855,13 @@ struct kvm_s390_gisa_interrupt { DECLARE_BITMAP(kicked_mask, KVM_MAX_VCPUS); }; +struct kvm_s390_pv { + u64 handle; + u64 guest_len; + unsigned long stor_base; + void *stor_var; +}; + struct kvm_arch{ void *sca; int use_esca; @@ -878,6 +897,7 @@ struct kvm_arch{ DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS); DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS); struct kvm_s390_gisa_interrupt gisa_int; + struct kvm_s390_pv pv; }; #define KVM_HVA_ERR_BAD (-1UL) diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index d7aa91c89f6c..91ef26983bfd 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -23,11 +23,19 @@ #define UVC_RC_INV_STATE 0x0003 #define UVC_RC_INV_LEN 0x0005 #define UVC_RC_NO_RESUME 0x0007 +#define UVC_RC_NEED_DESTROY 0x8000 #define UVC_CMD_QUI 0x0001 #define UVC_CMD_INIT_UV 0x000f +#define UVC_CMD_CREATE_SEC_CONF 0x0100 +#define UVC_CMD_DESTROY_SEC_CONF 0x0101 +#define UVC_CMD_CREATE_SEC_CPU 0x0120 +#define UVC_CMD_DESTROY_SEC_CPU 0x0121 #define UVC_CMD_CONV_TO_SEC_STOR 0x0200 #define UVC_CMD_CONV_FROM_SEC_STOR 0x0201 +#define UVC_CMD_SET_SEC_CONF_PARAMS 0x0300 +#define UVC_CMD_UNPACK_IMG 0x0301 +#define UVC_CMD_VERIFY_IMG 0x0302 #define UVC_CMD_PIN_PAGE_SHARED 0x0341 #define UVC_CMD_UNPIN_PAGE_SHARED 0x0342 #define UVC_CMD_SET_SHARED_ACCESS 0x1000 @@ -37,10 +45,17 @@ enum uv_cmds_inst { BIT_UVC_CMD_QUI = 0, BIT_UVC_CMD_INIT_UV = 1, + BIT_UVC_CMD_CREATE_SEC_CONF = 2, + BIT_UVC_CMD_DESTROY_SEC_CONF = 3, + BIT_UVC_CMD_CREATE_SEC_CPU = 4, + BIT_UVC_CMD_DESTROY_SEC_CPU = 5, BIT_UVC_CMD_CONV_TO_SEC_STOR = 6, BIT_UVC_CMD_CONV_FROM_SEC_STOR = 7, BIT_UVC_CMD_SET_SHARED_ACCESS = 8, BIT_UVC_CMD_REMOVE_SHARED_ACCESS = 9, + BIT_UVC_CMD_SET_SEC_PARMS = 11, + BIT_UVC_CMD_UNPACK_IMG = 13, + BIT_UVC_CMD_VERIFY_IMG = 14, BIT_UVC_CMD_PIN_PAGE_SHARED = 21, BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, }; @@ -52,6 +67,7 @@ struct uv_cb_header { u16 rrc; /* Return Reason Code */ } __packed __aligned(8); +/* Query Ultravisor Information */ struct uv_cb_qui { struct uv_cb_header header; u64 reserved08; @@ -71,6 +87,7 @@ struct uv_cb_qui { u8 reserveda0[200 - 160]; } __packed __aligned(8); +/* Initialize Ultravisor */ struct uv_cb_init { struct uv_cb_header header; u64 reserved08[2]; @@ -79,6 +96,35 @@ struct uv_cb_init { u64 reserved28[4]; } __packed __aligned(8); +/* Create Guest Configuration */ +struct uv_cb_cgc { + struct uv_cb_header header; + u64 reserved08[2]; + u64 guest_handle; + u64 conf_base_stor_origin; + u64 conf_virt_stor_origin; + u64 reserved30; + u64 guest_stor_origin; + u64 guest_stor_len; + u64 guest_sca; + u64 guest_asce; + u64 reserved58[5]; +} __packed __aligned(8); + +/* Create Secure CPU */ +struct uv_cb_csc { + struct uv_cb_header header; + u64 reserved08[2]; + u64 cpu_handle; + u64 guest_handle; + u64 stor_origin; + u8 reserved30[6]; + u16 num; + u64 state_origin; + u64 reserved40[4]; +} __packed __aligned(8); + +/* Convert to Secure */ struct uv_cb_cts { struct uv_cb_header header; u64 reserved08[2]; @@ -86,12 +132,34 @@ struct uv_cb_cts { u64 gaddr; } __packed __aligned(8); +/* Convert from Secure / Pin Page Shared */ struct uv_cb_cfs { struct uv_cb_header header; u64 reserved08[2]; u64 paddr; } __packed __aligned(8); +/* Set Secure Config Parameter */ +struct uv_cb_ssc { + struct uv_cb_header header; + u64 reserved08[2]; + u64 guest_handle; + u64 sec_header_origin; + u32 sec_header_len; + u32 reserved2c; + u64 reserved30[4]; +} __packed __aligned(8); + +/* Unpack */ +struct uv_cb_unp { + struct uv_cb_header header; + u64 reserved08[2]; + u64 guest_handle; + u64 gaddr; + u64 tweak[2]; + u64 reserved38[3]; +} __packed __aligned(8); + /* * A common UV call struct for calls that take no payload * Examples: @@ -105,6 +173,7 @@ struct uv_cb_nodata { u64 reserved20[4]; } __packed __aligned(8); +/* Set Shared Access */ struct uv_cb_share { struct uv_cb_header header; u64 reserved08[3]; diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index 05ee90a5ea08..12decca22e7c 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile @@ -9,6 +9,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqch ccflags-y := -Ivirt/kvm -Iarch/s390/kvm kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o -kvm-objs += diag.o gaccess.o guestdbg.o vsie.o +kvm-objs += diag.o gaccess.o guestdbg.o vsie.o pv.o obj-$(CONFIG_KVM) += kvm.o diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 7e4a982bfea3..87258bebb955 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "kvm-s390.h" #include "gaccess.h" @@ -234,8 +235,10 @@ int kvm_arch_check_processor_compat(void) return 0; } +/* forward declarations */ static void kvm_gmap_notifier(struct gmap *gmap, unsigned long start, unsigned long end); +static int sca_switch_to_extended(struct kvm *kvm); static void kvm_clock_sync_scb(struct kvm_s390_sie_block *scb, u64 delta) { @@ -2165,6 +2168,160 @@ out: return r; } +static int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rcp, u16 *rrcp) +{ + struct kvm_vcpu *vcpu; + u16 rc, rrc; + int ret = 0; + int i; + + /* + * We ignore failures and try to destroy as many CPUs as possible. + * At the same time we must not free the assigned resources when + * this fails, as the ultravisor has still access to that memory. + * So kvm_s390_pv_destroy_cpu can leave a "wanted" memory leak + * behind. + * We want to return the first failure rc and rrc, though. + */ + kvm_for_each_vcpu(i, vcpu, kvm) { + mutex_lock(&vcpu->mutex); + if (kvm_s390_pv_destroy_cpu(vcpu, &rc, &rrc) && !ret) { + *rcp = rc; + *rrcp = rrc; + ret = -EIO; + } + mutex_unlock(&vcpu->mutex); + } + return ret; +} + +static int kvm_s390_cpus_to_pv(struct kvm *kvm, u16 *rc, u16 *rrc) +{ + int i, r = 0; + u16 dummy; + + struct kvm_vcpu *vcpu; + + kvm_for_each_vcpu(i, vcpu, kvm) { + mutex_lock(&vcpu->mutex); + r = kvm_s390_pv_create_cpu(vcpu, rc, rrc); + mutex_unlock(&vcpu->mutex); + if (r) + break; + } + if (r) + kvm_s390_cpus_from_pv(kvm, &dummy, &dummy); + return r; +} + +static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd) +{ + int r = 0; + u16 dummy; + void __user *argp = (void __user *)cmd->data; + + switch (cmd->cmd) { + case KVM_PV_ENABLE: { + r = -EINVAL; + if (kvm_s390_pv_is_protected(kvm)) + break; + + /* + * FMT 4 SIE needs esca. As we never switch back to bsca from + * esca, we need no cleanup in the error cases below + */ + r = sca_switch_to_extended(kvm); + if (r) + break; + + r = kvm_s390_pv_init_vm(kvm, &cmd->rc, &cmd->rrc); + if (r) + break; + + r = kvm_s390_cpus_to_pv(kvm, &cmd->rc, &cmd->rrc); + if (r) + kvm_s390_pv_deinit_vm(kvm, &dummy, &dummy); + break; + } + case KVM_PV_DISABLE: { + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = kvm_s390_cpus_from_pv(kvm, &cmd->rc, &cmd->rrc); + /* + * If a CPU could not be destroyed, destroy VM will also fail. + * There is no point in trying to destroy it. Instead return + * the rc and rrc from the first CPU that failed destroying. + */ + if (r) + break; + r = kvm_s390_pv_deinit_vm(kvm, &cmd->rc, &cmd->rrc); + break; + } + case KVM_PV_SET_SEC_PARMS: { + struct kvm_s390_pv_sec_parm parms = {}; + void *hdr; + + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = -EFAULT; + if (copy_from_user(&parms, argp, sizeof(parms))) + break; + + /* Currently restricted to 8KB */ + r = -EINVAL; + if (parms.length > PAGE_SIZE * 2) + break; + + r = -ENOMEM; + hdr = vmalloc(parms.length); + if (!hdr) + break; + + r = -EFAULT; + if (!copy_from_user(hdr, (void __user *)parms.origin, + parms.length)) + r = kvm_s390_pv_set_sec_parms(kvm, hdr, parms.length, + &cmd->rc, &cmd->rrc); + + vfree(hdr); + break; + } + case KVM_PV_UNPACK: { + struct kvm_s390_pv_unp unp = {}; + + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = -EFAULT; + if (copy_from_user(&unp, argp, sizeof(unp))) + break; + + r = kvm_s390_pv_unpack(kvm, unp.addr, unp.size, unp.tweak, + &cmd->rc, &cmd->rrc); + break; + } + case KVM_PV_VERIFY: { + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm), + UVC_CMD_VERIFY_IMG, &cmd->rc, &cmd->rrc); + KVM_UV_EVENT(kvm, 3, "PROTVIRT VERIFY: rc %x rrc %x", cmd->rc, + cmd->rrc); + break; + } + default: + r = -ENOTTY; + } + return r; +} + long kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -2262,6 +2419,31 @@ long kvm_arch_vm_ioctl(struct file *filp, mutex_unlock(&kvm->slots_lock); break; } + case KVM_S390_PV_COMMAND: { + struct kvm_pv_cmd args; + + r = 0; + if (!is_prot_virt_host()) { + r = -EINVAL; + break; + } + if (copy_from_user(&args, argp, sizeof(args))) { + r = -EFAULT; + break; + } + if (args.flags) { + r = -EINVAL; + break; + } + mutex_lock(&kvm->lock); + r = kvm_s390_handle_pv(kvm, &args); + mutex_unlock(&kvm->lock); + if (copy_to_user(argp, &args, sizeof(args))) { + r = -EFAULT; + break; + } + break; + } default: r = -ENOTTY; } @@ -2525,6 +2707,8 @@ out_err: void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) { + u16 rc, rrc; + VCPU_EVENT(vcpu, 3, "%s", "free cpu"); trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id); kvm_s390_clear_local_irqs(vcpu); @@ -2537,6 +2721,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) if (vcpu->kvm->arch.use_cmma) kvm_s390_vcpu_unsetup_cmma(vcpu); + /* We can not hold the vcpu mutex here, we are already dying */ + if (kvm_s390_pv_cpu_get_handle(vcpu)) + kvm_s390_pv_destroy_cpu(vcpu, &rc, &rrc); free_page((unsigned long)(vcpu->arch.sie_block)); } @@ -2558,10 +2745,20 @@ static void kvm_free_vcpus(struct kvm *kvm) void kvm_arch_destroy_vm(struct kvm *kvm) { + u16 rc, rrc; + kvm_free_vcpus(kvm); sca_dispose(kvm); - debug_unregister(kvm->arch.dbf); kvm_s390_gisa_destroy(kvm); + /* + * We are already at the end of life and kvm->lock is not taken. + * This is ok as the file descriptor is closed by now and nobody + * can mess with the pv state. To avoid lockdep_assert_held from + * complaining we do not use kvm_s390_pv_is_protected. + */ + if (kvm_s390_pv_get_handle(kvm)) + kvm_s390_pv_deinit_vm(kvm, &rc, &rrc); + debug_unregister(kvm->arch.dbf); free_page((unsigned long)kvm->arch.sie_page2); if (!kvm_is_ucontrol(kvm)) gmap_remove(kvm->arch.gmap); @@ -2657,6 +2854,9 @@ static int sca_switch_to_extended(struct kvm *kvm) unsigned int vcpu_idx; u32 scaol, scaoh; + if (kvm->arch.use_esca) + return 0; + new_sca = alloc_pages_exact(sizeof(*new_sca), GFP_KERNEL|__GFP_ZERO); if (!new_sca) return -ENOMEM; @@ -2908,6 +3108,7 @@ static void kvm_s390_vcpu_setup_model(struct kvm_vcpu *vcpu) static int kvm_s390_vcpu_setup(struct kvm_vcpu *vcpu) { int rc = 0; + u16 uvrc, uvrrc; atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH | CPUSTAT_SM | @@ -2975,6 +3176,14 @@ static int kvm_s390_vcpu_setup(struct kvm_vcpu *vcpu) kvm_s390_vcpu_crypto_setup(vcpu); + mutex_lock(&vcpu->kvm->lock); + if (kvm_s390_pv_is_protected(vcpu->kvm)) { + rc = kvm_s390_pv_create_cpu(vcpu, &uvrc, &uvrrc); + if (rc) + kvm_s390_vcpu_unsetup_cmma(vcpu); + } + mutex_unlock(&vcpu->kvm->lock); + return rc; } @@ -4540,6 +4749,9 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, if (mem->guest_phys_addr + mem->memory_size > kvm->arch.mem_limit) return -EINVAL; + /* When we are protected, we should not change the memory slots */ + if (kvm_s390_pv_get_handle(kvm)) + return -EINVAL; return 0; } diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index be55b4b99bd3..13e6986596ed 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -207,6 +208,38 @@ static inline int kvm_s390_user_cpu_state_ctrl(struct kvm *kvm) return kvm->arch.user_cpu_state_ctrl != 0; } +/* implemented in pv.c */ +int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc); +int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc); +int kvm_s390_pv_deinit_vm(struct kvm *kvm, u16 *rc, u16 *rrc); +int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc); +int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length, u16 *rc, + u16 *rrc); +int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size, + unsigned long tweak, u16 *rc, u16 *rrc); + +static inline u64 kvm_s390_pv_get_handle(struct kvm *kvm) +{ + return kvm->arch.pv.handle; +} + +static inline u64 kvm_s390_pv_cpu_get_handle(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.pv.handle; +} + +static inline bool kvm_s390_pv_is_protected(struct kvm *kvm) +{ + lockdep_assert_held(&kvm->lock); + return !!kvm_s390_pv_get_handle(kvm); +} + +static inline bool kvm_s390_pv_cpu_is_protected(struct kvm_vcpu *vcpu) +{ + lockdep_assert_held(&vcpu->mutex); + return !!kvm_s390_pv_cpu_get_handle(vcpu); +} + /* implemented in interrupt.c */ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu); void kvm_s390_vcpu_wakeup(struct kvm_vcpu *vcpu); diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c new file mode 100644 index 000000000000..e9e020475f4a --- /dev/null +++ b/arch/s390/kvm/pv.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hosting Protected Virtual Machines + * + * Copyright IBM Corp. 2019, 2020 + * Author(s): Janosch Frank + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "kvm-s390.h" + +int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) +{ + int cc = 0; + + if (kvm_s390_pv_cpu_get_handle(vcpu)) { + cc = uv_cmd_nodata(kvm_s390_pv_cpu_get_handle(vcpu), + UVC_CMD_DESTROY_SEC_CPU, rc, rrc); + + KVM_UV_EVENT(vcpu->kvm, 3, + "PROTVIRT DESTROY VCPU %d: rc %x rrc %x", + vcpu->vcpu_id, *rc, *rrc); + WARN_ONCE(cc, "protvirt destroy cpu failed rc %x rrc %x", + *rc, *rrc); + } + /* Intended memory leak for something that should never happen. */ + if (!cc) + free_pages(vcpu->arch.pv.stor_base, + get_order(uv_info.guest_cpu_stor_len)); + vcpu->arch.sie_block->pv_handle_cpu = 0; + vcpu->arch.sie_block->pv_handle_config = 0; + memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv)); + vcpu->arch.sie_block->sdf = 0; + kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); + + return cc ? EIO : 0; +} + +int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) +{ + struct uv_cb_csc uvcb = { + .header.cmd = UVC_CMD_CREATE_SEC_CPU, + .header.len = sizeof(uvcb), + }; + int cc; + + if (kvm_s390_pv_cpu_get_handle(vcpu)) + return -EINVAL; + + vcpu->arch.pv.stor_base = __get_free_pages(GFP_KERNEL, + get_order(uv_info.guest_cpu_stor_len)); + if (!vcpu->arch.pv.stor_base) + return -ENOMEM; + + /* Input */ + uvcb.guest_handle = kvm_s390_pv_get_handle(vcpu->kvm); + uvcb.num = vcpu->arch.sie_block->icpua; + uvcb.state_origin = (u64)vcpu->arch.sie_block; + uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base; + + cc = uv_call(0, (u64)&uvcb); + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + KVM_UV_EVENT(vcpu->kvm, 3, + "PROTVIRT CREATE VCPU: cpu %d handle %llx rc %x rrc %x", + vcpu->vcpu_id, uvcb.cpu_handle, uvcb.header.rc, + uvcb.header.rrc); + + if (cc) { + u16 dummy; + + kvm_s390_pv_destroy_cpu(vcpu, &dummy, &dummy); + return -EIO; + } + + /* Output */ + vcpu->arch.pv.handle = uvcb.cpu_handle; + vcpu->arch.sie_block->pv_handle_cpu = uvcb.cpu_handle; + vcpu->arch.sie_block->pv_handle_config = kvm_s390_pv_get_handle(vcpu->kvm); + vcpu->arch.sie_block->sdf = 2; + kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); + return 0; +} + +/* only free resources when the destroy was successful */ +static void kvm_s390_pv_dealloc_vm(struct kvm *kvm) +{ + vfree(kvm->arch.pv.stor_var); + free_pages(kvm->arch.pv.stor_base, + get_order(uv_info.guest_base_stor_len)); + memset(&kvm->arch.pv, 0, sizeof(kvm->arch.pv)); +} + +static int kvm_s390_pv_alloc_vm(struct kvm *kvm) +{ + unsigned long base = uv_info.guest_base_stor_len; + unsigned long virt = uv_info.guest_virt_var_stor_len; + unsigned long npages = 0, vlen = 0; + struct kvm_memory_slot *memslot; + + kvm->arch.pv.stor_var = NULL; + kvm->arch.pv.stor_base = __get_free_pages(GFP_KERNEL, get_order(base)); + if (!kvm->arch.pv.stor_base) + return -ENOMEM; + + /* + * Calculate current guest storage for allocation of the + * variable storage, which is based on the length in MB. + * + * Slots are sorted by GFN + */ + mutex_lock(&kvm->slots_lock); + memslot = kvm_memslots(kvm)->memslots; + npages = memslot->base_gfn + memslot->npages; + mutex_unlock(&kvm->slots_lock); + + kvm->arch.pv.guest_len = npages * PAGE_SIZE; + + /* Allocate variable storage */ + vlen = ALIGN(virt * ((npages * PAGE_SIZE) / HPAGE_SIZE), PAGE_SIZE); + vlen += uv_info.guest_virt_base_stor_len; + kvm->arch.pv.stor_var = vzalloc(vlen); + if (!kvm->arch.pv.stor_var) + goto out_err; + return 0; + +out_err: + kvm_s390_pv_dealloc_vm(kvm); + return -ENOMEM; +} + +/* this should not fail, but if it does, we must not free the donated memory */ +int kvm_s390_pv_deinit_vm(struct kvm *kvm, u16 *rc, u16 *rrc) +{ + int cc; + + cc = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm), + UVC_CMD_DESTROY_SEC_CONF, rc, rrc); + WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); + atomic_set(&kvm->mm->context.is_protected, 0); + KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY VM: rc %x rrc %x", *rc, *rrc); + WARN_ONCE(cc, "protvirt destroy vm failed rc %x rrc %x", *rc, *rrc); + /* Inteded memory leak on "impossible" error */ + if (!cc) + kvm_s390_pv_dealloc_vm(kvm); + return cc ? -EIO : 0; +} + +int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc) +{ + struct uv_cb_cgc uvcb = { + .header.cmd = UVC_CMD_CREATE_SEC_CONF, + .header.len = sizeof(uvcb) + }; + int cc, ret; + u16 dummy; + + ret = kvm_s390_pv_alloc_vm(kvm); + if (ret) + return ret; + + /* Inputs */ + uvcb.guest_stor_origin = 0; /* MSO is 0 for KVM */ + uvcb.guest_stor_len = kvm->arch.pv.guest_len; + uvcb.guest_asce = kvm->arch.gmap->asce; + uvcb.guest_sca = (unsigned long)kvm->arch.sca; + uvcb.conf_base_stor_origin = (u64)kvm->arch.pv.stor_base; + uvcb.conf_virt_stor_origin = (u64)kvm->arch.pv.stor_var; + + cc = uv_call(0, (u64)&uvcb); + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + KVM_UV_EVENT(kvm, 3, "PROTVIRT CREATE VM: handle %llx len %llx rc %x rrc %x", + uvcb.guest_handle, uvcb.guest_stor_len, *rc, *rrc); + + /* Outputs */ + kvm->arch.pv.handle = uvcb.guest_handle; + + if (cc) { + if (uvcb.header.rc & UVC_RC_NEED_DESTROY) + kvm_s390_pv_deinit_vm(kvm, &dummy, &dummy); + else + kvm_s390_pv_dealloc_vm(kvm); + return -EIO; + } + kvm->arch.gmap->guest_handle = uvcb.guest_handle; + atomic_set(&kvm->mm->context.is_protected, 1); + return 0; +} + +int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length, u16 *rc, + u16 *rrc) +{ + struct uv_cb_ssc uvcb = { + .header.cmd = UVC_CMD_SET_SEC_CONF_PARAMS, + .header.len = sizeof(uvcb), + .sec_header_origin = (u64)hdr, + .sec_header_len = length, + .guest_handle = kvm_s390_pv_get_handle(kvm), + }; + int cc = uv_call(0, (u64)&uvcb); + + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + KVM_UV_EVENT(kvm, 3, "PROTVIRT VM SET PARMS: rc %x rrc %x", + *rc, *rrc); + return cc ? -EINVAL : 0; +} + +static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak, + u64 offset, u16 *rc, u16 *rrc) +{ + struct uv_cb_unp uvcb = { + .header.cmd = UVC_CMD_UNPACK_IMG, + .header.len = sizeof(uvcb), + .guest_handle = kvm_s390_pv_get_handle(kvm), + .gaddr = addr, + .tweak[0] = tweak, + .tweak[1] = offset, + }; + int ret = gmap_make_secure(kvm->arch.gmap, addr, &uvcb); + + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + + if (ret && ret != -EAGAIN) + KVM_UV_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx with rc %x rrc %x", + uvcb.gaddr, *rc, *rrc); + return ret; +} + +int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size, + unsigned long tweak, u16 *rc, u16 *rrc) +{ + u64 offset = 0; + int ret = 0; + + if (addr & ~PAGE_MASK || !size || size & ~PAGE_MASK) + return -EINVAL; + + KVM_UV_EVENT(kvm, 3, "PROTVIRT VM UNPACK: start addr %lx size %lx", + addr, size); + + while (offset < size) { + ret = unpack_one(kvm, addr, tweak, offset, rc, rrc); + if (ret == -EAGAIN) { + cond_resched(); + if (fatal_signal_pending(current)) + break; + continue; + } + if (ret) + break; + addr += PAGE_SIZE; + offset += PAGE_SIZE; + } + if (!ret) + KVM_UV_EVENT(kvm, 3, "%s", "PROTVIRT VM UNPACK: successful"); + return ret; +} diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 4b95f9a31a2f..ad69817f7792 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1478,6 +1478,37 @@ struct kvm_enc_region { #define KVM_S390_NORMAL_RESET _IO(KVMIO, 0xc3) #define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4) +struct kvm_s390_pv_sec_parm { + __u64 origin; + __u64 length; +}; + +struct kvm_s390_pv_unp { + __u64 addr; + __u64 size; + __u64 tweak; +}; + +enum pv_cmd_id { + KVM_PV_ENABLE, + KVM_PV_DISABLE, + KVM_PV_SET_SEC_PARMS, + KVM_PV_UNPACK, + KVM_PV_VERIFY, +}; + +struct kvm_pv_cmd { + __u32 cmd; /* Command to be executed */ + __u16 rc; /* Ultravisor return code */ + __u16 rrc; /* Ultravisor return reason code */ + __u64 data; /* Data or address */ + __u32 flags; /* flags for future extensions. Must be 0 for now */ + __u32 reserved[3]; +}; + +/* Available with KVM_CAP_S390_PROTECTED */ +#define KVM_S390_PV_COMMAND _IOWR(KVMIO, 0xc5, struct kvm_pv_cmd) + /* Secure Encrypted Virtualization command */ enum sev_cmd_id { /* Guest initialization commands */ -- cgit v1.2.3 From 19e1227768863a1469797c13ef8fea1af7beac2c Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Tue, 2 Apr 2019 09:21:06 +0200 Subject: KVM: S390: protvirt: Introduce instruction data area bounce buffer Now that we can't access guest memory anymore, we have a dedicated satellite block that's a bounce buffer for instruction data. We re-use the memop interface to copy the instruction data to / from userspace. This lets us re-use a lot of QEMU code which used that interface to make logical guest memory accesses which are not possible anymore in protected mode anyway. Signed-off-by: Janosch Frank Reviewed-by: Thomas Huth Reviewed-by: David Hildenbrand [borntraeger@de.ibm.com: patch merging, splitting, fixing] Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/kvm_host.h | 11 ++++++- arch/s390/kvm/kvm-s390.c | 66 +++++++++++++++++++++++++++++++++++----- arch/s390/kvm/pv.c | 16 ++++++++++ include/uapi/linux/kvm.h | 9 ++++-- 4 files changed, 91 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 4fcbb055a565..aa945b101fff 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -127,6 +127,12 @@ struct mcck_volatile_info { #define CR14_INITIAL_MASK (CR14_UNUSED_32 | CR14_UNUSED_33 | \ CR14_EXTERNAL_DAMAGE_SUBMASK) +#define SIDAD_SIZE_MASK 0xff +#define sida_origin(sie_block) \ + ((sie_block)->sidad & PAGE_MASK) +#define sida_size(sie_block) \ + ((((sie_block)->sidad & SIDAD_SIZE_MASK) + 1) * PAGE_SIZE) + #define CPUSTAT_STOPPED 0x80000000 #define CPUSTAT_WAIT 0x10000000 #define CPUSTAT_ECALL_PEND 0x08000000 @@ -315,7 +321,10 @@ struct kvm_s390_sie_block { #define CRYCB_FORMAT2 0x00000003 __u32 crycbd; /* 0x00fc */ __u64 gcr[16]; /* 0x0100 */ - __u64 gbea; /* 0x0180 */ + union { + __u64 gbea; /* 0x0180 */ + __u64 sidad; + }; __u8 reserved188[8]; /* 0x0188 */ __u64 sdnxo; /* 0x0190 */ __u8 reserved198[8]; /* 0x0198 */ diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index bd62312fdc0e..efbbcd2948a3 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -4495,12 +4495,40 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, return r; } +static long kvm_s390_guest_sida_op(struct kvm_vcpu *vcpu, + struct kvm_s390_mem_op *mop) +{ + void __user *uaddr = (void __user *)mop->buf; + int r = 0; + + if (mop->flags || !mop->size) + return -EINVAL; + if (mop->size + mop->sida_offset < mop->size) + return -EINVAL; + if (mop->size + mop->sida_offset > sida_size(vcpu->arch.sie_block)) + return -E2BIG; + + switch (mop->op) { + case KVM_S390_MEMOP_SIDA_READ: + if (copy_to_user(uaddr, (void *)(sida_origin(vcpu->arch.sie_block) + + mop->sida_offset), mop->size)) + r = -EFAULT; + + break; + case KVM_S390_MEMOP_SIDA_WRITE: + if (copy_from_user((void *)(sida_origin(vcpu->arch.sie_block) + + mop->sida_offset), uaddr, mop->size)) + r = -EFAULT; + break; + } + return r; +} static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, struct kvm_s390_mem_op *mop) { void __user *uaddr = (void __user *)mop->buf; void *tmpbuf = NULL; - int r, srcu_idx; + int r = 0; const u64 supported_flags = KVM_S390_MEMOP_F_INJECT_EXCEPTION | KVM_S390_MEMOP_F_CHECK_ONLY; @@ -4510,14 +4538,15 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, if (mop->size > MEM_OP_MAX_SIZE) return -E2BIG; + if (kvm_s390_pv_cpu_is_protected(vcpu)) + return -EINVAL; + if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) { tmpbuf = vmalloc(mop->size); if (!tmpbuf) return -ENOMEM; } - srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); - switch (mop->op) { case KVM_S390_MEMOP_LOGICAL_READ: if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) { @@ -4543,12 +4572,8 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, } r = write_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size); break; - default: - r = -EINVAL; } - srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); - if (r > 0 && (mop->flags & KVM_S390_MEMOP_F_INJECT_EXCEPTION) != 0) kvm_s390_inject_prog_irq(vcpu, &vcpu->arch.pgm); @@ -4556,6 +4581,31 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu, return r; } +static long kvm_s390_guest_memsida_op(struct kvm_vcpu *vcpu, + struct kvm_s390_mem_op *mop) +{ + int r, srcu_idx; + + srcu_idx = srcu_read_lock(&vcpu->kvm->srcu); + + switch (mop->op) { + case KVM_S390_MEMOP_LOGICAL_READ: + case KVM_S390_MEMOP_LOGICAL_WRITE: + r = kvm_s390_guest_mem_op(vcpu, mop); + break; + case KVM_S390_MEMOP_SIDA_READ: + case KVM_S390_MEMOP_SIDA_WRITE: + /* we are locked against sida going away by the vcpu->mutex */ + r = kvm_s390_guest_sida_op(vcpu, mop); + break; + default: + r = -EINVAL; + } + + srcu_read_unlock(&vcpu->kvm->srcu, srcu_idx); + return r; +} + long kvm_arch_vcpu_async_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -4686,7 +4736,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, struct kvm_s390_mem_op mem_op; if (copy_from_user(&mem_op, argp, sizeof(mem_op)) == 0) - r = kvm_s390_guest_mem_op(vcpu, &mem_op); + r = kvm_s390_guest_memsida_op(vcpu, &mem_op); else r = -EFAULT; break; diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index 9840ee49e572..13a41533eacf 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -33,10 +33,18 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) if (!cc) free_pages(vcpu->arch.pv.stor_base, get_order(uv_info.guest_cpu_stor_len)); + + free_page(sida_origin(vcpu->arch.sie_block)); vcpu->arch.sie_block->pv_handle_cpu = 0; vcpu->arch.sie_block->pv_handle_config = 0; memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv)); vcpu->arch.sie_block->sdf = 0; + /* + * The sidad field (for sdf == 2) is now the gbea field (for sdf == 0). + * Use the reset value of gbea to avoid leaking the kernel pointer of + * the just freed sida. + */ + vcpu->arch.sie_block->gbea = 1; kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); return cc ? EIO : 0; @@ -64,6 +72,14 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) uvcb.state_origin = (u64)vcpu->arch.sie_block; uvcb.stor_origin = (u64)vcpu->arch.pv.stor_base; + /* Alloc Secure Instruction Data Area Designation */ + vcpu->arch.sie_block->sidad = __get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!vcpu->arch.sie_block->sidad) { + free_pages(vcpu->arch.pv.stor_base, + get_order(uv_info.guest_cpu_stor_len)); + return -ENOMEM; + } + cc = uv_call(0, (u64)&uvcb); *rc = uvcb.header.rc; *rrc = uvcb.header.rrc; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index ad69817f7792..f4cac1c09e97 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -474,12 +474,17 @@ struct kvm_s390_mem_op { __u32 size; /* amount of bytes */ __u32 op; /* type of operation */ __u64 buf; /* buffer in userspace */ - __u8 ar; /* the access register number */ - __u8 reserved[31]; /* should be set to 0 */ + union { + __u8 ar; /* the access register number */ + __u32 sida_offset; /* offset into the sida */ + __u8 reserved[32]; /* should be set to 0 */ + }; }; /* types for kvm_s390_mem_op->op */ #define KVM_S390_MEMOP_LOGICAL_READ 0 #define KVM_S390_MEMOP_LOGICAL_WRITE 1 +#define KVM_S390_MEMOP_SIDA_READ 2 +#define KVM_S390_MEMOP_SIDA_WRITE 3 /* flags for kvm_s390_mem_op->flags */ #define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0) #define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1) -- cgit v1.2.3 From e0d2773d487c2a41c99d9e256d51cc0a859aa9ab Mon Sep 17 00:00:00 2001 From: Janosch Frank Date: Thu, 9 May 2019 13:07:21 +0200 Subject: KVM: s390: protvirt: UV calls in support of diag308 0, 1 diag 308 subcode 0 and 1 require several KVM and Ultravisor interactions. Specific to these "soft" reboots are * The "unshare all" UVC * The "prepare for reset" UVC Signed-off-by: Janosch Frank Acked-by: David Hildenbrand Reviewed-by: Cornelia Huck [borntraeger@de.ibm.com: patch merging, splitting, fixing] Signed-off-by: Christian Borntraeger --- arch/s390/include/asm/uv.h | 4 ++++ arch/s390/kvm/kvm-s390.c | 22 ++++++++++++++++++++++ include/uapi/linux/kvm.h | 2 ++ 3 files changed, 28 insertions(+) (limited to 'include') diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index 91ef26983bfd..f149c29ddb84 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -36,6 +36,8 @@ #define UVC_CMD_SET_SEC_CONF_PARAMS 0x0300 #define UVC_CMD_UNPACK_IMG 0x0301 #define UVC_CMD_VERIFY_IMG 0x0302 +#define UVC_CMD_PREPARE_RESET 0x0320 +#define UVC_CMD_SET_UNSHARE_ALL 0x0340 #define UVC_CMD_PIN_PAGE_SHARED 0x0341 #define UVC_CMD_UNPIN_PAGE_SHARED 0x0342 #define UVC_CMD_SET_SHARED_ACCESS 0x1000 @@ -56,6 +58,8 @@ enum uv_cmds_inst { BIT_UVC_CMD_SET_SEC_PARMS = 11, BIT_UVC_CMD_UNPACK_IMG = 13, BIT_UVC_CMD_VERIFY_IMG = 14, + BIT_UVC_CMD_PREPARE_RESET = 18, + BIT_UVC_CMD_UNSHARE_ALL = 20, BIT_UVC_CMD_PIN_PAGE_SHARED = 21, BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, }; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index abe295077d00..16531b251eab 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -2328,6 +2328,28 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd) cmd->rrc); break; } + case KVM_PV_PREP_RESET: { + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm), + UVC_CMD_PREPARE_RESET, &cmd->rc, &cmd->rrc); + KVM_UV_EVENT(kvm, 3, "PROTVIRT PREP RESET: rc %x rrc %x", + cmd->rc, cmd->rrc); + break; + } + case KVM_PV_UNSHARE_ALL: { + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm), + UVC_CMD_SET_UNSHARE_ALL, &cmd->rc, &cmd->rrc); + KVM_UV_EVENT(kvm, 3, "PROTVIRT UNSHARE: rc %x rrc %x", + cmd->rc, cmd->rrc); + break; + } default: r = -ENOTTY; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index f4cac1c09e97..2c354ba3a610 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1500,6 +1500,8 @@ enum pv_cmd_id { KVM_PV_SET_SEC_PARMS, KVM_PV_UNPACK, KVM_PV_VERIFY, + KVM_PV_PREP_RESET, + KVM_PV_UNSHARE_ALL, }; struct kvm_pv_cmd { -- cgit v1.2.3 From 13da9ae1cdbf1ec4ea36b7612e606681c27cca13 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Tue, 18 Feb 2020 15:08:07 -0500 Subject: KVM: s390: protvirt: introduce and enable KVM_CAP_S390_PROTECTED Now that everything is in place, we can announce the feature. Signed-off-by: Christian Borntraeger Reviewed-by: Cornelia Huck Reviewed-by: David Hildenbrand --- arch/s390/kvm/kvm-s390.c | 3 +++ include/uapi/linux/kvm.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index bb060064cce0..f4cd436ba979 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -574,6 +574,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_BPB: r = test_facility(82); break; + case KVM_CAP_S390_PROTECTED: + r = is_prot_virt_host(); + break; default: r = 0; } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 2c354ba3a610..2ab168d6d2a8 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1015,6 +1015,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ARM_NISV_TO_USER 177 #define KVM_CAP_ARM_INJECT_EXT_DABT 178 #define KVM_CAP_S390_VCPU_RESETS 179 +#define KVM_CAP_S390_PROTECTED 180 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3 From 91a208f2185ad4855ff03c342d0b7e4f5fc6f5df Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 26 Feb 2020 10:23:41 +0000 Subject: net: phylink: propagate resolved link config via mac_link_up() Propagate the resolved link parameters via the mac_link_up() call for MACs that do not automatically track their PCS state. We propagate the link parameters via function arguments so that inappropriate members of struct phylink_link_state can't be accessed, and creating a new structure just for this adds needless complexity to the API. Tested-by: Andre Przywara Tested-by: Alexandre Belloni Tested-by: Vladimir Oltean Signed-off-by: Russell King Signed-off-by: David S. Miller --- Documentation/networking/sfp-phylink.rst | 17 +++++-- drivers/net/ethernet/cadence/macb_main.c | 7 ++- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 7 ++- drivers/net/ethernet/marvell/mvneta.c | 8 ++-- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 19 +++++--- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 7 +-- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +- drivers/net/ethernet/xilinx/xilinx_axienet_main.c | 7 +-- drivers/net/phy/phylink.c | 9 +++- include/linux/phylink.h | 57 +++++++++++++++++------ net/dsa/port.c | 4 +- 11 files changed, 105 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/Documentation/networking/sfp-phylink.rst b/Documentation/networking/sfp-phylink.rst index d753a309f9d1..8d7af28cd835 100644 --- a/Documentation/networking/sfp-phylink.rst +++ b/Documentation/networking/sfp-phylink.rst @@ -74,10 +74,13 @@ phylib to the sfp/phylink support. Please send patches to improve this documentation. 1. Optionally split the network driver's phylib update function into - three parts dealing with link-down, link-up and reconfiguring the - MAC settings. This can be done as a separate preparation commit. + two parts dealing with link-down and link-up. This can be done as + a separate preparation commit. - An example of this preparation can be found in git commit fc548b991fb0. + An older example of this preparation can be found in git commit + fc548b991fb0, although this was splitting into three parts; the + link-up part now includes configuring the MAC for the link settings. + Please see :c:func:`mac_link_up` for more information on this. 2. Replace:: @@ -207,6 +210,14 @@ this documentation. using. This is particularly important for in-band negotiation methods such as 1000base-X and SGMII. + The :c:func:`mac_link_up` method is used to inform the MAC that the + link has come up. The call includes the negotiation mode and interface + for reference only. The finalised link parameters are also supplied + (speed, duplex and flow control/pause enablement settings) which + should be used to configure the MAC when the MAC and PCS are not + tightly integrated, or when the settings are not coming from in-band + negotiation. + The :c:func:`mac_config` method is used to update the MAC with the requested state, and must avoid unnecessarily taking the link down when making changes to the MAC configuration. This means the diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 2c28da1737fe..7ab0bef5e1bd 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -626,8 +626,11 @@ static void macb_mac_link_down(struct phylink_config *config, unsigned int mode, netif_tx_stop_all_queues(ndev); } -static void macb_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void macb_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct net_device *ndev = to_net_dev(config->dev); struct macb *bp = netdev_priv(ndev); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index 84233e467ed1..3a75c5b58f95 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -154,8 +154,11 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode, netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err); } -static void dpaa2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void dpaa2_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); struct dpmac_link_state *dpmac_state = &mac->state; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 1c391f63a26f..9af3f8d5b289 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -3965,9 +3965,11 @@ static void mvneta_mac_link_down(struct phylink_config *config, mvneta_set_eee(pp, false); } -static void mvneta_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) +static void mvneta_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct net_device *ndev = to_net_dev(config->dev); struct mvneta_port *pp = netdev_priv(ndev); diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 72133cbe55d4..ed8042d97e29 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -58,8 +58,11 @@ static struct { */ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state); -static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy); +static void mvpp2_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause); /* Queue modes */ #define MVPP2_QDIST_SINGLE_MODE 0 @@ -3473,8 +3476,9 @@ static void mvpp2_start_dev(struct mvpp2_port *port) .interface = port->phy_interface, }; mvpp2_mac_config(&port->phylink_config, MLO_AN_INBAND, &state); - mvpp2_mac_link_up(&port->phylink_config, MLO_AN_INBAND, - port->phy_interface, NULL); + mvpp2_mac_link_up(&port->phylink_config, NULL, + MLO_AN_INBAND, port->phy_interface, + SPEED_UNKNOWN, DUPLEX_UNKNOWN, false, false); } netif_tx_start_all_queues(port->dev); @@ -5141,8 +5145,11 @@ static void mvpp2_mac_config(struct phylink_config *config, unsigned int mode, mvpp2_port_enable(port); } -static void mvpp2_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, struct phy_device *phy) +static void mvpp2_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct net_device *dev = to_net_dev(config->dev); struct mvpp2_port *port = netdev_priv(dev); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 8c6cfd15481c..8d28f90acfe7 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -412,9 +412,10 @@ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } -static void mtk_mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) +static void mtk_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause) { struct mtk_mac *mac = container_of(config, struct mtk_mac, phylink_config); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 37920b4da091..e039e715dcee 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -950,8 +950,10 @@ static void stmmac_mac_link_down(struct phylink_config *config, } static void stmmac_mac_link_up(struct phylink_config *config, + struct phy_device *phy, unsigned int mode, phy_interface_t interface, - struct phy_device *phy) + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 20746b801959..197740781157 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1486,9 +1486,10 @@ static void axienet_mac_link_down(struct phylink_config *config, } static void axienet_mac_link_up(struct phylink_config *config, - unsigned int mode, - phy_interface_t interface, - struct phy_device *phy) + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) { /* nothing meaningful to do */ } diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index 2899fbe699ab..b4367fab7899 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -480,8 +480,11 @@ static void phylink_mac_link_up(struct phylink *pl, struct net_device *ndev = pl->netdev; pl->cur_interface = link_state.interface; - pl->ops->mac_link_up(pl->config, pl->cur_link_an_mode, - pl->cur_interface, pl->phydev); + pl->ops->mac_link_up(pl->config, pl->phydev, + pl->cur_link_an_mode, pl->cur_interface, + link_state.speed, link_state.duplex, + !!(link_state.pause & MLO_PAUSE_TX), + !!(link_state.pause & MLO_PAUSE_RX)); if (ndev) netif_carrier_on(ndev); @@ -547,6 +550,8 @@ static void phylink_resolve(struct work_struct *w) link_state.pause = pl->phy_state.pause; phylink_apply_manual_flow(pl, &link_state); phylink_mac_config(pl, &link_state); + } else { + phylink_apply_manual_flow(pl, &link_state); } break; } diff --git a/include/linux/phylink.h b/include/linux/phylink.h index 812357c03df4..2180eb1aa254 100644 --- a/include/linux/phylink.h +++ b/include/linux/phylink.h @@ -91,9 +91,10 @@ struct phylink_mac_ops { void (*mac_an_restart)(struct phylink_config *config); void (*mac_link_down)(struct phylink_config *config, unsigned int mode, phy_interface_t interface); - void (*mac_link_up)(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy); + void (*mac_link_up)(struct phylink_config *config, + struct phy_device *phy, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause); }; #if 0 /* For kernel-doc purposes only. */ @@ -152,6 +153,9 @@ void mac_pcs_get_state(struct phylink_config *config, * guaranteed to be correct, and so any mac_config() implementation must * never reference these fields. * + * (this requires a rewrite - please refer to mac_link_up() for situations + * where the PCS and MAC are not tightly integrated.) + * * In all negotiation modes, as defined by @mode, @state->pause indicates the * pause settings which should be applied as follows. If %MLO_PAUSE_AN is not * set, %MLO_PAUSE_TX and %MLO_PAUSE_RX indicate whether the MAC should send @@ -162,12 +166,20 @@ void mac_pcs_get_state(struct phylink_config *config, * The action performed depends on the currently selected mode: * * %MLO_AN_FIXED, %MLO_AN_PHY: - * Configure the specified @state->speed and @state->duplex over a link - * specified by @state->interface. @state->advertising may be used, but - * is not required. Pause modes as above. Other members of @state must - * be ignored. + * Configure for non-inband negotiation mode, where the link settings + * are completely communicated via mac_link_up(). The physical link + * protocol from the MAC is specified by @state->interface. + * + * @state->advertising may be used, but is not required. + * + * Older drivers (prior to the mac_link_up() change) may use @state->speed, + * @state->duplex and @state->pause to configure the MAC, but this is + * deprecated; such drivers should be converted to use mac_link_up(). * - * Valid state members: interface, speed, duplex, pause, advertising. + * Other members of @state must be ignored. + * + * Valid state members: interface, advertising. + * Deprecated state members: speed, duplex, pause. * * %MLO_AN_INBAND: * place the link in an inband negotiation mode (such as 802.3z @@ -228,19 +240,34 @@ void mac_link_down(struct phylink_config *config, unsigned int mode, /** * mac_link_up() - allow the link to come up * @config: a pointer to a &struct phylink_config. + * @phy: any attached phy * @mode: link autonegotiation mode * @interface: link &typedef phy_interface_t mode - * @phy: any attached phy + * @speed: link speed + * @duplex: link duplex + * @tx_pause: link transmit pause enablement status + * @rx_pause: link receive pause enablement status * - * If @mode is not an in-band negotiation mode (as defined by - * phylink_autoneg_inband()), allow the link to come up. If @phy - * is non-%NULL, configure Energy Efficient Ethernet by calling + * Configure the MAC for an established link. + * + * @speed, @duplex, @tx_pause and @rx_pause indicate the finalised link + * settings, and should be used to configure the MAC block appropriately + * where these settings are not automatically conveyed from the PCS block, + * or if in-band negotiation (as defined by phylink_autoneg_inband(@mode)) + * is disabled. + * + * Note that when 802.3z in-band negotiation is in use, it is possible + * that the user wishes to override the pause settings, and this should + * be allowed when considering the implementation of this method. + * + * If in-band negotiation mode is disabled, allow the link to come up. If + * @phy is non-%NULL, configure Energy Efficient Ethernet by calling * phy_init_eee() and perform appropriate MAC configuration for EEE. * Interface type selection must be done in mac_config(). */ -void mac_link_up(struct phylink_config *config, unsigned int mode, - phy_interface_t interface, - struct phy_device *phy); +void mac_link_up(struct phylink_config *config, struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, bool tx_pause, bool rx_pause); #endif struct phylink *phylink_create(struct phylink_config *, struct fwnode_handle *, diff --git a/net/dsa/port.c b/net/dsa/port.c index 774facb8d547..b2f5262b35cf 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -489,9 +489,11 @@ static void dsa_port_phylink_mac_link_down(struct phylink_config *config, } static void dsa_port_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct dsa_port *dp = container_of(config, struct dsa_port, pl_config); struct dsa_switch *ds = dp->ds; -- cgit v1.2.3 From 5b502a7b2992008a1fd5962ba032771b03c4e840 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 26 Feb 2020 10:23:46 +0000 Subject: net: dsa: propagate resolved link config via mac_link_up() Propagate the resolved link configuration down via DSA's phylink_mac_link_up() operation to allow split PCS/MAC to work. Tested-by: Vladimir Oltean Signed-off-by: Russell King Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 4 +++- drivers/net/dsa/b53/b53_priv.h | 4 +++- drivers/net/dsa/bcm_sf2.c | 4 +++- drivers/net/dsa/lantiq_gswip.c | 4 +++- drivers/net/dsa/mt7530.c | 4 +++- drivers/net/dsa/mv88e6xxx/chip.c | 4 +++- drivers/net/dsa/ocelot/felix.c | 4 +++- drivers/net/dsa/qca/ar9331.c | 4 +++- drivers/net/dsa/sja1105/sja1105_main.c | 4 +++- include/net/dsa.h | 4 +++- net/dsa/port.c | 3 ++- 11 files changed, 32 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 1a69286daa8d..ceafce446317 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1289,7 +1289,9 @@ EXPORT_SYMBOL(b53_phylink_mac_link_down); void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct b53_device *dev = ds->priv; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 3c30f3a7eb29..3d42318bc3f1 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -338,7 +338,9 @@ void b53_phylink_mac_link_down(struct dsa_switch *ds, int port, void b53_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev); + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause); int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering); int b53_vlan_prepare(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index d1955543acd1..2daca9f0b8ca 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -649,7 +649,9 @@ static void bcm_sf2_sw_mac_link_down(struct dsa_switch *ds, int port, static void bcm_sf2_sw_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->dev->ports[port].eee; diff --git a/drivers/net/dsa/lantiq_gswip.c b/drivers/net/dsa/lantiq_gswip.c index 0369c22fe3e1..cf6fa8fede33 100644 --- a/drivers/net/dsa/lantiq_gswip.c +++ b/drivers/net/dsa/lantiq_gswip.c @@ -1517,7 +1517,9 @@ static void gswip_phylink_mac_link_down(struct dsa_switch *ds, int port, static void gswip_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct gswip_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 022466ca1c19..86818ab3bb07 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -1482,7 +1482,9 @@ static void mt7530_phylink_mac_link_down(struct dsa_switch *ds, int port, static void mt7530_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct mt7530_priv *priv = ds->priv; diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 705c118f6fdd..ebaaf21d7953 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -655,7 +655,9 @@ static void mv88e6xxx_mac_link_down(struct dsa_switch *ds, int port, static void mv88e6xxx_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { if (mode == MLO_AN_FIXED) mv88e6xxx_mac_link_force(ds, port, LINK_FORCED_UP); diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 35124ef7e75b..7e66821b05b4 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -263,7 +263,9 @@ static void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, static void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int link_an_mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct ocelot *ocelot = ds->priv; struct ocelot_port *ocelot_port = ocelot->ports[port]; diff --git a/drivers/net/dsa/qca/ar9331.c b/drivers/net/dsa/qca/ar9331.c index de25f99e995a..7c86056b9401 100644 --- a/drivers/net/dsa/qca/ar9331.c +++ b/drivers/net/dsa/qca/ar9331.c @@ -458,7 +458,9 @@ static void ar9331_sw_phylink_mac_link_down(struct dsa_switch *ds, int port, static void ar9331_sw_phylink_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { struct ar9331_sw_priv *priv = (struct ar9331_sw_priv *)ds->priv; struct regmap *regmap = priv->regmap; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 03ba6d25f7fe..c27cc7b37440 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -786,7 +786,9 @@ static void sja1105_mac_link_down(struct dsa_switch *ds, int port, static void sja1105_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { sja1105_inhibit_tx(ds->priv, BIT(port), false); } diff --git a/include/net/dsa.h b/include/net/dsa.h index 63495e3443ac..7d3d84f0ef42 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -420,7 +420,9 @@ struct dsa_switch_ops { void (*phylink_mac_link_up)(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev); + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause); void (*phylink_fixed_state)(struct dsa_switch *ds, int port, struct phylink_link_state *state); /* diff --git a/net/dsa/port.c b/net/dsa/port.c index b2f5262b35cf..d4450a454249 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -504,7 +504,8 @@ static void dsa_port_phylink_mac_link_up(struct phylink_config *config, return; } - ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev); + ds->ops->phylink_mac_link_up(ds, dp->index, mode, interface, phydev, + speed, duplex, tx_pause, rx_pause); } const struct phylink_mac_ops dsa_port_phylink_mac_ops = { -- cgit v1.2.3 From da60fbe7ef942d64b4d80e99cc9b0205db5964b9 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Feb 2020 15:20:00 -0600 Subject: NFC: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- drivers/nfc/fdp/fdp.c | 2 +- drivers/nfc/st21nfca/dep.c | 4 ++-- include/net/nfc/nci.h | 14 +++++++------- include/net/nfc/nfc.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/nfc/fdp/fdp.c b/drivers/nfc/fdp/fdp.c index 0cc9ac856fe2..7c0b1e6b80ff 100644 --- a/drivers/nfc/fdp/fdp.c +++ b/drivers/nfc/fdp/fdp.c @@ -76,7 +76,7 @@ static u8 nci_core_get_config_otp_ram_version[5] = { struct nci_core_get_config_rsp { u8 status; u8 count; - u8 data[0]; + u8 data[]; }; static int fdp_nci_create_conn(struct nci_dev *ndev) diff --git a/drivers/nfc/st21nfca/dep.c b/drivers/nfc/st21nfca/dep.c index 60acdfd1cb8c..a1d69f9b2d4a 100644 --- a/drivers/nfc/st21nfca/dep.c +++ b/drivers/nfc/st21nfca/dep.c @@ -66,7 +66,7 @@ struct st21nfca_atr_req { u8 bsi; u8 bri; u8 ppi; - u8 gbi[0]; + u8 gbi[]; } __packed; struct st21nfca_atr_res { @@ -79,7 +79,7 @@ struct st21nfca_atr_res { u8 bri; u8 to; u8 ppi; - u8 gbi[0]; + u8 gbi[]; } __packed; struct st21nfca_psl_req { diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index 6ab5a83f597c..0550e0380b8d 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -244,13 +244,13 @@ struct dest_spec_params { struct core_conn_create_dest_spec_params { __u8 type; __u8 length; - __u8 value[0]; + __u8 value[]; } __packed; struct nci_core_conn_create_cmd { __u8 destination_type; __u8 number_destination_params; - struct core_conn_create_dest_spec_params params[0]; + struct core_conn_create_dest_spec_params params[]; } __packed; #define NCI_OP_CORE_CONN_CLOSE_CMD nci_opcode_pack(NCI_GID_CORE, 0x05) @@ -321,7 +321,7 @@ struct nci_core_init_rsp_1 { __u8 status; __le32 nfcc_features; __u8 num_supported_rf_interfaces; - __u8 supported_rf_interfaces[0]; /* variable size array */ + __u8 supported_rf_interfaces[]; /* variable size array */ /* continuted in nci_core_init_rsp_2 */ } __packed; @@ -338,7 +338,7 @@ struct nci_core_init_rsp_2 { struct nci_core_set_config_rsp { __u8 status; __u8 num_params; - __u8 params_id[0]; /* variable size array */ + __u8 params_id[]; /* variable size array */ } __packed; #define NCI_OP_CORE_CONN_CREATE_RSP nci_opcode_pack(NCI_GID_CORE, 0x04) @@ -501,18 +501,18 @@ struct nci_rf_nfcee_action_ntf { __u8 nfcee_id; __u8 trigger; __u8 supported_data_length; - __u8 supported_data[0]; + __u8 supported_data[]; } __packed; #define NCI_OP_NFCEE_DISCOVER_NTF nci_opcode_pack(NCI_GID_NFCEE_MGMT, 0x00) struct nci_nfcee_supported_protocol { __u8 num_protocol; - __u8 supported_protocol[0]; + __u8 supported_protocol[]; } __packed; struct nci_nfcee_information_tlv { __u8 num_tlv; - __u8 information_tlv[0]; + __u8 information_tlv[]; } __packed; struct nci_nfcee_discover_ntf { diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 5d277d68fd8d..2cd3a261bcbc 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -146,7 +146,7 @@ struct nfc_evt_transaction { u32 aid_len; u8 aid[NFC_MAX_AID_LENGTH]; u8 params_len; - u8 params[0]; + u8 params[]; } __packed; struct nfc_genl_data { -- cgit v1.2.3 From 7cbeb2e1bf0c0972d7ff09af1f37e77bfac680ce Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 11 Feb 2020 14:22:51 -0300 Subject: media: smiapp: Move definitions under driver directory include/media/i2c/smiapp.h was meant to serve systems where the sensor is enumerated through platform data. That's no longer necessary, hopefully not even in out-of-tree use cases. Move the definitions to the appropriate headers. Signed-off-by: Sakari Ailus Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/smiapp/smiapp-reg.h | 4 +++ drivers/media/i2c/smiapp/smiapp.h | 44 +++++++++++++++++++++++- include/media/i2c/smiapp.h | 63 ----------------------------------- 3 files changed, 47 insertions(+), 64 deletions(-) delete mode 100644 include/media/i2c/smiapp.h (limited to 'include') diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index 43505cd0616e..e6f96309786f 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -35,6 +35,10 @@ #define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE BIT(0) #define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE BIT(1) +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 +#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 + #define SMIAPP_DPHY_CTRL_AUTOMATIC 0 /* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ #define SMIAPP_DPHY_CTRL_UI 1 diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index 4837d80dc453..6f469934f9e3 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -14,7 +14,6 @@ #include #include #include -#include #include "smiapp-pll.h" #include "smiapp-reg.h" @@ -42,6 +41,49 @@ #define SMIAPP_COLOUR_COMPONENTS 4 +#define SMIAPP_NAME "smiapp" + +#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ +#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ + +/* + * Sometimes due to board layout considerations the camera module can be + * mounted rotated. The typical rotation used is 180 degrees which can be + * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. + * FIXME: rotation also changes the bayer pattern. + */ +enum smiapp_module_board_orient { + SMIAPP_MODULE_BOARD_ORIENT_0 = 0, + SMIAPP_MODULE_BOARD_ORIENT_180, +}; + +struct smiapp_flash_strobe_parms { + u8 mode; + u32 strobe_width_high_us; + u16 strobe_delay; + u16 stobe_start_point; + u8 trigger; +}; + +struct smiapp_hwconfig { + /* + * Change the cci address if i2c_addr_alt is set. + * Both default and alternate cci addr need to be present + */ + unsigned short i2c_addr_dfl; /* Default i2c addr */ + unsigned short i2c_addr_alt; /* Alternate i2c addr */ + + uint32_t ext_clk; /* sensor external clk */ + + unsigned int lanes; /* Number of CSI-2 lanes */ + uint32_t csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ + uint64_t *op_sys_clock; + + enum smiapp_module_board_orient module_board_orient; + + struct smiapp_flash_strobe_parms *strobe_setup; +}; + #include "smiapp-limits.h" struct smiapp_quirk; diff --git a/include/media/i2c/smiapp.h b/include/media/i2c/smiapp.h deleted file mode 100644 index 80f8251d87a3..000000000000 --- a/include/media/i2c/smiapp.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * include/media/i2c/smiapp.h - * - * Generic driver for SMIA/SMIA++ compliant camera modules - * - * Copyright (C) 2011--2012 Nokia Corporation - * Contact: Sakari Ailus - */ - -#ifndef __SMIAPP_H_ -#define __SMIAPP_H_ - -#include - -#define SMIAPP_NAME "smiapp" - -#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ -#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ - -#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 -#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 -#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 - -/* - * Sometimes due to board layout considerations the camera module can be - * mounted rotated. The typical rotation used is 180 degrees which can be - * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. - * FIXME: rotation also changes the bayer pattern. - */ -enum smiapp_module_board_orient { - SMIAPP_MODULE_BOARD_ORIENT_0 = 0, - SMIAPP_MODULE_BOARD_ORIENT_180, -}; - -struct smiapp_flash_strobe_parms { - u8 mode; - u32 strobe_width_high_us; - u16 strobe_delay; - u16 stobe_start_point; - u8 trigger; -}; - -struct smiapp_hwconfig { - /* - * Change the cci address if i2c_addr_alt is set. - * Both default and alternate cci addr need to be present - */ - unsigned short i2c_addr_dfl; /* Default i2c addr */ - unsigned short i2c_addr_alt; /* Alternate i2c addr */ - - uint32_t ext_clk; /* sensor external clk */ - - unsigned int lanes; /* Number of CSI-2 lanes */ - uint32_t csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ - uint64_t *op_sys_clock; - - enum smiapp_module_board_orient module_board_orient; - - struct smiapp_flash_strobe_parms *strobe_setup; -}; - -#endif /* __SMIAPP_H_ */ -- cgit v1.2.3 From d7f10df86202273155a9d8f8553bc2ad28e0dd46 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Feb 2020 18:17:44 -0600 Subject: bpf: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Daniel Borkmann Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200227001744.GA3317@embeddedor --- include/linux/bpf-cgroup.h | 2 +- include/linux/bpf.h | 2 +- include/uapi/linux/bpf.h | 2 +- kernel/bpf/bpf_struct_ops.c | 2 +- kernel/bpf/hashtab.c | 2 +- kernel/bpf/lpm_trie.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index a11d5b7dbbf3..a7cd5c7a2509 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -36,7 +36,7 @@ struct bpf_cgroup_storage_map; struct bpf_storage_buffer { struct rcu_head rcu; - char data[0]; + char data[]; }; struct bpf_cgroup_storage { diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 1acd5bf70350..9aa33b8f3d55 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -859,7 +859,7 @@ struct bpf_prog_array_item { struct bpf_prog_array { struct rcu_head rcu; - struct bpf_prog_array_item items[0]; + struct bpf_prog_array_item items[]; }; struct bpf_prog_array *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 906e9f2752db..8e98ced0963b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -73,7 +73,7 @@ struct bpf_insn { /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ - __u8 data[0]; /* Arbitrary size */ + __u8 data[]; /* Arbitrary size */ }; struct bpf_cgroup_storage_key { diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index 042f95534f86..c498f0fffb40 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -23,7 +23,7 @@ enum bpf_struct_ops_state { struct bpf_struct_ops_value { BPF_STRUCT_OPS_COMMON_VALUE; - char data[0] ____cacheline_aligned_in_smp; + char data[] ____cacheline_aligned_in_smp; }; struct bpf_struct_ops_map { diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 53d9483fee10..d541c8486c95 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -118,7 +118,7 @@ struct htab_elem { struct bpf_lru_node lru_node; }; u32 hash; - char key[0] __aligned(8); + char key[] __aligned(8); }; static inline bool htab_is_prealloc(const struct bpf_htab *htab) diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index 3b3c420bc8ed..65c236cf341e 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -25,7 +25,7 @@ struct lpm_trie_node { struct lpm_trie_node __rcu *child[2]; u32 prefixlen; u32 flags; - u8 data[0]; + u8 data[]; }; struct lpm_trie { -- cgit v1.2.3 From 057a0fb649f3bfcd3048e22e15fa613026076119 Mon Sep 17 00:00:00 2001 From: Kalyani Akula Date: Mon, 17 Feb 2020 15:56:41 +0530 Subject: firmware: xilinx: Add ZynqMP aes API for AES functionality Add ZynqMP firmware AES API to perform encryption/decryption of given data. Signed-off-by: Kalyani Akula Acked-by: Michal Simek Signed-off-by: Herbert Xu --- drivers/firmware/xilinx/zynqmp.c | 25 +++++++++++++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 2 ++ 2 files changed, 27 insertions(+) (limited to 'include') diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index ecc339d846de..1dea880b2397 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -707,6 +707,30 @@ static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, qos, ack, NULL); } +/** + * zynqmp_pm_aes - Access AES hardware to encrypt/decrypt the data using + * AES-GCM core. + * @address: Address of the AesParams structure. + * @out: Returned output value + * + * Return: Returns status, either success or error code. + */ +static int zynqmp_pm_aes_engine(const u64 address, u32 *out) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!out) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_SECURE_AES, upper_32_bits(address), + lower_32_bits(address), + 0, 0, ret_payload); + *out = ret_payload[1]; + + return ret; +} + static const struct zynqmp_eemi_ops eemi_ops = { .get_api_version = zynqmp_pm_get_api_version, .get_chipid = zynqmp_pm_get_chipid, @@ -730,6 +754,7 @@ static const struct zynqmp_eemi_ops eemi_ops = { .set_requirement = zynqmp_pm_set_requirement, .fpga_load = zynqmp_pm_fpga_load, .fpga_get_status = zynqmp_pm_fpga_get_status, + .aes = zynqmp_pm_aes_engine, }; /** diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 2cd12ebd6826..8acace459351 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -82,6 +82,7 @@ enum pm_api_id { PM_CLOCK_GETRATE, PM_CLOCK_SETPARENT, PM_CLOCK_GETPARENT, + PM_SECURE_AES = 47, PM_FEATURE_CHECK = 63, PM_API_MAX, }; @@ -315,6 +316,7 @@ struct zynqmp_eemi_ops { const u32 capabilities, const u32 qos, const enum zynqmp_pm_request_ack ack); + int (*aes)(const u64 address, u32 *out); }; int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, -- cgit v1.2.3 From 96e326878fa5e2727d14e9a23644119374619010 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Tue, 14 Jan 2020 17:30:41 +0200 Subject: net/mlx5e: Eswitch, Use per vport tables for mirroring When using port mirroring, we forward the traffic to another table and use that table to forward to the mirrored vport. Since the hardware loses the values of reg c, and in particular reg c0, we fail the match on the input vport which previously existed in reg c0. To overcome this situation, we use a set of per vport tables, positioned at the lowest priority, and forward traffic to those tables. Since these tables are per vport, we can avoid matching on reg c0. Fixes: c01cfd0f1115 ("net/mlx5: E-Switch, Add match on vport metadata for rule in fast path") Signed-off-by: Eli Cohen Reviewed-by: Mark Bloch Reviewed-by: Paul Blakey Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 10 + .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 206 ++++++++++++++++++++- .../mellanox/mlx5/core/eswitch_offloads_chains.c | 11 +- drivers/net/ethernet/mellanox/mlx5/core/fs_core.c | 11 ++ include/linux/mlx5/fs.h | 1 + 5 files changed, 221 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 4472710ccc9c..479d2458f872 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -49,6 +49,7 @@ /* The index of the last real chain (FT) + 1 as chain zero is valid as well */ #define FDB_NUM_CHAINS (FDB_FT_CHAIN + 1) +#define ESW_OFFLOADS_NUM_GROUPS 4 #define FDB_TC_MAX_PRIO 16 #define FDB_TC_LEVELS_PER_PRIO 2 @@ -183,6 +184,12 @@ struct mlx5_eswitch_fdb { int vlan_push_pop_refcount; struct mlx5_esw_chains_priv *esw_chains_priv; + struct { + DECLARE_HASHTABLE(table, 8); + /* Protects vports.table */ + struct mutex lock; + } vports; + } offloads; }; u32 flags; @@ -623,6 +630,9 @@ void esw_vport_destroy_offloads_acl_tables(struct mlx5_eswitch *esw, struct mlx5_vport *vport); +int mlx5_esw_vport_tbl_get(struct mlx5_eswitch *esw); +void mlx5_esw_vport_tbl_put(struct mlx5_eswitch *esw); + #else /* CONFIG_MLX5_ESWITCH */ /* eswitch API stubs */ static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 1a57b2bd74b8..9a72c719d8f5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -50,6 +50,179 @@ #define MLX5_ESW_MISS_FLOWS (2) #define UPLINK_REP_INDEX 0 +/* Per vport tables */ + +#define MLX5_ESW_VPORT_TABLE_SIZE 128 + +/* This struct is used as a key to the hash table and we need it to be packed + * so hash result is consistent + */ +struct mlx5_vport_key { + u32 chain; + u16 prio; + u16 vport; + u16 vhca_id; +} __packed; + +struct mlx5_vport_table { + struct hlist_node hlist; + struct mlx5_flow_table *fdb; + u32 num_rules; + struct mlx5_vport_key key; +}; + +static struct mlx5_flow_table * +esw_vport_tbl_create(struct mlx5_eswitch *esw, struct mlx5_flow_namespace *ns) +{ + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5_flow_table *fdb; + + ft_attr.autogroup.max_num_groups = ESW_OFFLOADS_NUM_GROUPS; + ft_attr.max_fte = MLX5_ESW_VPORT_TABLE_SIZE; + ft_attr.prio = FDB_PER_VPORT; + fdb = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); + if (IS_ERR(fdb)) { + esw_warn(esw->dev, "Failed to create per vport FDB Table err %ld\n", + PTR_ERR(fdb)); + } + + return fdb; +} + +static u32 flow_attr_to_vport_key(struct mlx5_eswitch *esw, + struct mlx5_esw_flow_attr *attr, + struct mlx5_vport_key *key) +{ + key->vport = attr->in_rep->vport; + key->chain = attr->chain; + key->prio = attr->prio; + key->vhca_id = MLX5_CAP_GEN(esw->dev, vhca_id); + return jhash(key, sizeof(*key), 0); +} + +/* caller must hold vports.lock */ +static struct mlx5_vport_table * +esw_vport_tbl_lookup(struct mlx5_eswitch *esw, struct mlx5_vport_key *skey, u32 key) +{ + struct mlx5_vport_table *e; + + hash_for_each_possible(esw->fdb_table.offloads.vports.table, e, hlist, key) + if (!memcmp(&e->key, skey, sizeof(*skey))) + return e; + + return NULL; +} + +static void +esw_vport_tbl_put(struct mlx5_eswitch *esw, struct mlx5_esw_flow_attr *attr) +{ + struct mlx5_vport_table *e; + struct mlx5_vport_key key; + u32 hkey; + + mutex_lock(&esw->fdb_table.offloads.vports.lock); + hkey = flow_attr_to_vport_key(esw, attr, &key); + e = esw_vport_tbl_lookup(esw, &key, hkey); + if (!e || --e->num_rules) + goto out; + + hash_del(&e->hlist); + mlx5_destroy_flow_table(e->fdb); + kfree(e); +out: + mutex_unlock(&esw->fdb_table.offloads.vports.lock); +} + +static struct mlx5_flow_table * +esw_vport_tbl_get(struct mlx5_eswitch *esw, struct mlx5_esw_flow_attr *attr) +{ + struct mlx5_core_dev *dev = esw->dev; + struct mlx5_flow_namespace *ns; + struct mlx5_flow_table *fdb; + struct mlx5_vport_table *e; + struct mlx5_vport_key skey; + u32 hkey; + + mutex_lock(&esw->fdb_table.offloads.vports.lock); + hkey = flow_attr_to_vport_key(esw, attr, &skey); + e = esw_vport_tbl_lookup(esw, &skey, hkey); + if (e) { + e->num_rules++; + goto out; + } + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (!e) { + fdb = ERR_PTR(-ENOMEM); + goto err_alloc; + } + + ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); + if (!ns) { + esw_warn(dev, "Failed to get FDB namespace\n"); + fdb = ERR_PTR(-ENOENT); + goto err_ns; + } + + fdb = esw_vport_tbl_create(esw, ns); + if (IS_ERR(fdb)) + goto err_ns; + + e->fdb = fdb; + e->num_rules = 1; + e->key = skey; + hash_add(esw->fdb_table.offloads.vports.table, &e->hlist, hkey); +out: + mutex_unlock(&esw->fdb_table.offloads.vports.lock); + return e->fdb; + +err_ns: + kfree(e); +err_alloc: + mutex_unlock(&esw->fdb_table.offloads.vports.lock); + return fdb; +} + +int mlx5_esw_vport_tbl_get(struct mlx5_eswitch *esw) +{ + struct mlx5_esw_flow_attr attr = {}; + struct mlx5_eswitch_rep rep = {}; + struct mlx5_flow_table *fdb; + struct mlx5_vport *vport; + int i; + + attr.prio = 1; + attr.in_rep = &rep; + mlx5_esw_for_all_vports(esw, i, vport) { + attr.in_rep->vport = vport->vport; + fdb = esw_vport_tbl_get(esw, &attr); + if (!fdb) + goto out; + } + return 0; + +out: + mlx5_esw_vport_tbl_put(esw); + return PTR_ERR(fdb); +} + +void mlx5_esw_vport_tbl_put(struct mlx5_eswitch *esw) +{ + struct mlx5_esw_flow_attr attr = {}; + struct mlx5_eswitch_rep rep = {}; + struct mlx5_vport *vport; + int i; + + attr.prio = 1; + attr.in_rep = &rep; + mlx5_esw_for_all_vports(esw, i, vport) { + attr.in_rep->vport = vport->vport; + esw_vport_tbl_put(esw, &attr); + } +} + +/* End: Per vport tables */ + static struct mlx5_eswitch_rep *mlx5_eswitch_get_rep(struct mlx5_eswitch *esw, u16 vport_num) { @@ -191,8 +364,6 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, i++; } - mlx5_eswitch_set_rule_source_port(esw, spec, attr); - if (attr->outer_match_level != MLX5_MATCH_NONE) spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; if (attr->inner_match_level != MLX5_MATCH_NONE) @@ -201,8 +372,13 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, if (flow_act.action & MLX5_FLOW_CONTEXT_ACTION_MOD_HDR) flow_act.modify_hdr = attr->modify_hdr; - fdb = mlx5_esw_chains_get_table(esw, attr->chain, attr->prio, - !!split); + if (split) { + fdb = esw_vport_tbl_get(esw, attr); + } else { + fdb = mlx5_esw_chains_get_table(esw, attr->chain, attr->prio, + 0); + mlx5_eswitch_set_rule_source_port(esw, spec, attr); + } if (IS_ERR(fdb)) { rule = ERR_CAST(fdb); goto err_esw_get; @@ -221,7 +397,10 @@ mlx5_eswitch_add_offloaded_rule(struct mlx5_eswitch *esw, return rule; err_add_rule: - mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, !!split); + if (split) + esw_vport_tbl_put(esw, attr); + else + mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 0); err_esw_get: if (!(attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH) && attr->dest_chain) mlx5_esw_chains_put_table(esw, attr->dest_chain, 1, 0); @@ -247,7 +426,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw, goto err_get_fast; } - fwd_fdb = mlx5_esw_chains_get_table(esw, attr->chain, attr->prio, 1); + fwd_fdb = esw_vport_tbl_get(esw, attr); if (IS_ERR(fwd_fdb)) { rule = ERR_CAST(fwd_fdb); goto err_get_fwd; @@ -285,7 +464,7 @@ mlx5_eswitch_add_fwd_rule(struct mlx5_eswitch *esw, return rule; add_err: - mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 1); + esw_vport_tbl_put(esw, attr); err_get_fwd: mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 0); err_get_fast: @@ -312,11 +491,14 @@ __mlx5_eswitch_del_rule(struct mlx5_eswitch *esw, atomic64_dec(&esw->offloads.num_flows); if (fwd_rule) { - mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 1); + esw_vport_tbl_put(esw, attr); mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, 0); } else { - mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, - !!split); + if (split) + esw_vport_tbl_put(esw, attr); + else + mlx5_esw_chains_put_table(esw, attr->chain, attr->prio, + 0); if (attr->dest_chain) mlx5_esw_chains_put_table(esw, attr->dest_chain, 1, 0); } @@ -1923,6 +2105,9 @@ static int esw_offloads_steering_init(struct mlx5_eswitch *esw) if (err) goto create_fg_err; + mutex_init(&esw->fdb_table.offloads.vports.lock); + hash_init(esw->fdb_table.offloads.vports.table); + return 0; create_fg_err: @@ -1939,6 +2124,7 @@ create_fdb_err: static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) { + mutex_destroy(&esw->fdb_table.offloads.vports.lock); esw_destroy_vport_rx_group(esw); esw_destroy_offloads_table(esw); esw_destroy_offloads_fdb_tables(esw); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c index 4276194b633f..883c9e6ff0b2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads_chains.c @@ -21,8 +21,6 @@ #define fdb_ignore_flow_level_supported(esw) \ (MLX5_CAP_ESW_FLOWTABLE_FDB((esw)->dev, ignore_flow_level)) -#define ESW_OFFLOADS_NUM_GROUPS 4 - /* Firmware currently has 4 pool of 4 sizes that it supports (ESW_POOLS), * and a virtual memory region of 16M (ESW_SIZE), this region is duplicated * for each flow table pool. We can allocate up to 16M of each pool, @@ -704,12 +702,9 @@ mlx5_esw_chains_open(struct mlx5_eswitch *esw) /* Open level 1 for split rules now if prios isn't supported */ if (!mlx5_esw_chains_prios_supported(esw)) { - ft = mlx5_esw_chains_get_table(esw, 0, 1, 1); - - if (IS_ERR(ft)) { - err = PTR_ERR(ft); + err = mlx5_esw_vport_tbl_get(esw); + if (err) goto level_1_err; - } } return 0; @@ -725,7 +720,7 @@ static void mlx5_esw_chains_close(struct mlx5_eswitch *esw) { if (!mlx5_esw_chains_prios_supported(esw)) - mlx5_esw_chains_put_table(esw, 0, 1, 1); + mlx5_esw_vport_tbl_put(esw); mlx5_esw_chains_put_table(esw, 0, 1, 0); mlx5_esw_chains_put_table(esw, mlx5_esw_chains_get_ft_chain(esw), 1, 0); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index 9dc24241dc91..5826fd43d530 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -2700,6 +2700,17 @@ static int init_fdb_root_ns(struct mlx5_flow_steering *steering) goto out_err; } + /* We put this priority last, knowing that nothing will get here + * unless explicitly forwarded to. This is possible because the + * slow path tables have catch all rules and nothing gets passed + * those tables. + */ + maj_prio = fs_create_prio(&steering->fdb_root_ns->ns, FDB_PER_VPORT, 1); + if (IS_ERR(maj_prio)) { + err = PTR_ERR(maj_prio); + goto out_err; + } + set_prio_attrs(steering->fdb_root_ns); return 0; diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index 4cae16016b2b..a5cf5c76f348 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -84,6 +84,7 @@ enum { FDB_TC_OFFLOAD, FDB_FT_OFFLOAD, FDB_SLOW_PATH, + FDB_PER_VPORT, }; struct mlx5_pkt_reformat; -- cgit v1.2.3 From 5682d393b40e1fe7426a7b8c3471f05262f42010 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Feb 2020 15:04:09 -0800 Subject: inet_diag: Refactor inet_sk_diag_fill(), dump(), and dump_one() In a latter patch, there is a need to update "cb->min_dump_alloc" in inet_sk_diag_fill() as it learns the diffierent bpf_sk_storages stored in a sk while dumping all sk(s) (e.g. tcp_hashinfo). The inet_sk_diag_fill() currently does not take the "cb" as an argument. One of the reason is inet_sk_diag_fill() is used by both dump_one() and dump() (which belong to the "struct inet_diag_handler". The dump_one() interface does not pass the "cb" along. This patch is to make dump_one() pass a "cb". The "cb" is created in inet_diag_cmd_exact(). The "nlh" and "in_skb" are stored in "cb" as the dump() interface does. The total number of args in inet_sk_diag_fill() is also cut from 10 to 7 and that helps many callers to pass fewer args. In particular, "struct user_namespace *user_ns", "u32 pid", and "u32 seq" can be replaced by accessing "cb->nlh" and "cb->skb". A similar argument reduction is also made to inet_twsk_diag_fill() and inet_req_diag_fill(). inet_csk_diag_dump() and inet_csk_diag_fill() are also removed. They are mostly equivalent to inet_sk_diag_fill(). Their repeated usages are very limited. Thus, inet_sk_diag_fill() is directly used in those occasions. Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200225230409.1975173-1-kafai@fb.com --- include/linux/inet_diag.h | 12 ++--- net/dccp/diag.c | 5 +- net/ipv4/inet_diag.c | 116 ++++++++++++++++++---------------------------- net/ipv4/raw_diag.c | 18 +++---- net/ipv4/tcp_diag.c | 4 +- net/ipv4/udp_diag.c | 26 +++++------ net/sctp/diag.c | 5 +- 7 files changed, 73 insertions(+), 113 deletions(-) (limited to 'include') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index 39faaaf843e1..6b157ce07d74 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -18,8 +18,7 @@ struct inet_diag_handler { const struct inet_diag_req_v2 *r, struct nlattr *bc); - int (*dump_one)(struct sk_buff *in_skb, - const struct nlmsghdr *nlh, + int (*dump_one)(struct netlink_callback *cb, const struct inet_diag_req_v2 *req); void (*idiag_get_info)(struct sock *sk, @@ -42,16 +41,15 @@ struct inet_diag_handler { struct inet_connection_sock; int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, - struct sk_buff *skb, const struct inet_diag_req_v2 *req, - struct user_namespace *user_ns, - u32 pid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh, bool net_admin); + struct sk_buff *skb, struct netlink_callback *cb, + const struct inet_diag_req_v2 *req, + u16 nlmsg_flags, bool net_admin); void inet_diag_dump_icsk(struct inet_hashinfo *h, struct sk_buff *skb, struct netlink_callback *cb, const struct inet_diag_req_v2 *r, struct nlattr *bc); int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, - struct sk_buff *in_skb, const struct nlmsghdr *nlh, + struct netlink_callback *cb, const struct inet_diag_req_v2 *req); struct sock *inet_diag_find_one_icsk(struct net *net, diff --git a/net/dccp/diag.c b/net/dccp/diag.c index 73ef73a218ff..8f1e2a653f6d 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -51,11 +51,10 @@ static void dccp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, inet_diag_dump_icsk(&dccp_hashinfo, skb, cb, r, bc); } -static int dccp_diag_dump_one(struct sk_buff *in_skb, - const struct nlmsghdr *nlh, +static int dccp_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { - return inet_diag_dump_one_icsk(&dccp_hashinfo, in_skb, nlh, req); + return inet_diag_dump_one_icsk(&dccp_hashinfo, cb, req); } static const struct inet_diag_handler dccp_diag_handler = { diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index f11e997e517b..d2ecff3195ba 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -157,11 +157,9 @@ errout: EXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill); int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, - struct sk_buff *skb, const struct inet_diag_req_v2 *req, - struct user_namespace *user_ns, - u32 portid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh, - bool net_admin) + struct sk_buff *skb, struct netlink_callback *cb, + const struct inet_diag_req_v2 *req, + u16 nlmsg_flags, bool net_admin) { const struct tcp_congestion_ops *ca_ops; const struct inet_diag_handler *handler; @@ -174,8 +172,8 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, handler = inet_diag_table[req->sdiag_protocol]; BUG_ON(!handler); - nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), - nlmsg_flags); + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags); if (!nlh) return -EMSGSIZE; @@ -187,7 +185,9 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, r->idiag_timer = 0; r->idiag_retrans = 0; - if (inet_diag_msg_attrs_fill(sk, skb, r, ext, user_ns, net_admin)) + if (inet_diag_msg_attrs_fill(sk, skb, r, ext, + sk_user_ns(NETLINK_CB(cb->skb).sk), + net_admin)) goto errout; if (ext & (1 << (INET_DIAG_MEMINFO - 1))) { @@ -312,30 +312,19 @@ errout: } EXPORT_SYMBOL_GPL(inet_sk_diag_fill); -static int inet_csk_diag_fill(struct sock *sk, - struct sk_buff *skb, - const struct inet_diag_req_v2 *req, - struct user_namespace *user_ns, - u32 portid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh, - bool net_admin) -{ - return inet_sk_diag_fill(sk, inet_csk(sk), skb, req, user_ns, - portid, seq, nlmsg_flags, unlh, net_admin); -} - static int inet_twsk_diag_fill(struct sock *sk, struct sk_buff *skb, - u32 portid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh) + struct netlink_callback *cb, + u16 nlmsg_flags) { struct inet_timewait_sock *tw = inet_twsk(sk); struct inet_diag_msg *r; struct nlmsghdr *nlh; long tmo; - nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), - nlmsg_flags); + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, + sizeof(*r), nlmsg_flags); if (!nlh) return -EMSGSIZE; @@ -359,16 +348,16 @@ static int inet_twsk_diag_fill(struct sock *sk, } static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb, - u32 portid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh, bool net_admin) + struct netlink_callback *cb, + u16 nlmsg_flags, bool net_admin) { struct request_sock *reqsk = inet_reqsk(sk); struct inet_diag_msg *r; struct nlmsghdr *nlh; long tmo; - nlh = nlmsg_put(skb, portid, seq, unlh->nlmsg_type, sizeof(*r), - nlmsg_flags); + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, + cb->nlh->nlmsg_type, sizeof(*r), nlmsg_flags); if (!nlh) return -EMSGSIZE; @@ -397,21 +386,18 @@ static int inet_req_diag_fill(struct sock *sk, struct sk_buff *skb, } static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, + struct netlink_callback *cb, const struct inet_diag_req_v2 *r, - struct user_namespace *user_ns, - u32 portid, u32 seq, u16 nlmsg_flags, - const struct nlmsghdr *unlh, bool net_admin) + u16 nlmsg_flags, bool net_admin) { if (sk->sk_state == TCP_TIME_WAIT) - return inet_twsk_diag_fill(sk, skb, portid, seq, - nlmsg_flags, unlh); + return inet_twsk_diag_fill(sk, skb, cb, nlmsg_flags); if (sk->sk_state == TCP_NEW_SYN_RECV) - return inet_req_diag_fill(sk, skb, portid, seq, - nlmsg_flags, unlh, net_admin); + return inet_req_diag_fill(sk, skb, cb, nlmsg_flags, net_admin); - return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, - nlmsg_flags, unlh, net_admin); + return inet_sk_diag_fill(sk, inet_csk(sk), skb, cb, r, nlmsg_flags, + net_admin); } struct sock *inet_diag_find_one_icsk(struct net *net, @@ -459,10 +445,10 @@ struct sock *inet_diag_find_one_icsk(struct net *net, EXPORT_SYMBOL_GPL(inet_diag_find_one_icsk); int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, - struct sk_buff *in_skb, - const struct nlmsghdr *nlh, + struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { + struct sk_buff *in_skb = cb->skb; bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN); struct net *net = sock_net(in_skb->sk); struct sk_buff *rep; @@ -479,10 +465,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, goto out; } - err = sk_diag_fill(sk, rep, req, - sk_user_ns(NETLINK_CB(in_skb).sk), - NETLINK_CB(in_skb).portid, - nlh->nlmsg_seq, 0, nlh, net_admin); + err = sk_diag_fill(sk, rep, cb, req, 0, net_admin); if (err < 0) { WARN_ON(err == -EMSGSIZE); nlmsg_free(rep); @@ -509,14 +492,19 @@ static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb, int err; handler = inet_diag_lock_handler(req->sdiag_protocol); - if (IS_ERR(handler)) + if (IS_ERR(handler)) { err = PTR_ERR(handler); - else if (cmd == SOCK_DIAG_BY_FAMILY) - err = handler->dump_one(in_skb, nlh, req); - else if (cmd == SOCK_DESTROY && handler->destroy) + } else if (cmd == SOCK_DIAG_BY_FAMILY) { + struct netlink_callback cb = { + .nlh = nlh, + .skb = in_skb, + }; + err = handler->dump_one(&cb, req); + } else if (cmd == SOCK_DESTROY && handler->destroy) { err = handler->destroy(in_skb, req); - else + } else { err = -EOPNOTSUPP; + } inet_diag_unlock_handler(handler); return err; @@ -847,23 +835,6 @@ static int inet_diag_bc_audit(const struct nlattr *attr, return len == 0 ? 0 : -EINVAL; } -static int inet_csk_diag_dump(struct sock *sk, - struct sk_buff *skb, - struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, - const struct nlattr *bc, - bool net_admin) -{ - if (!inet_diag_bc_sk(bc, sk)) - return 0; - - return inet_csk_diag_fill(sk, skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh, - net_admin); -} - static void twsk_build_assert(void) { BUILD_BUG_ON(offsetof(struct inet_timewait_sock, tw_family) != @@ -935,8 +906,12 @@ void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, r->id.idiag_sport) goto next_listen; - if (inet_csk_diag_dump(sk, skb, cb, r, - bc, net_admin) < 0) { + if (!inet_diag_bc_sk(bc, sk)) + goto next_listen; + + if (inet_sk_diag_fill(sk, inet_csk(sk), skb, + cb, r, NLM_F_MULTI, + net_admin) < 0) { spin_unlock(&ilb->lock); goto done; } @@ -1014,11 +989,8 @@ next_normal: res = 0; for (idx = 0; idx < accum; idx++) { if (res >= 0) { - res = sk_diag_fill(sk_arr[idx], skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - cb->nlh, net_admin); + res = sk_diag_fill(sk_arr[idx], skb, cb, r, + NLM_F_MULTI, net_admin); if (res < 0) num = num_arr[idx]; } diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index e35736b99300..a2933eeabd91 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -87,15 +87,16 @@ out_unlock: return sk ? sk : ERR_PTR(-ENOENT); } -static int raw_diag_dump_one(struct sk_buff *in_skb, - const struct nlmsghdr *nlh, +static int raw_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *r) { - struct net *net = sock_net(in_skb->sk); + struct sk_buff *in_skb = cb->skb; struct sk_buff *rep; struct sock *sk; + struct net *net; int err; + net = sock_net(in_skb->sk); sk = raw_sock_get(net, r); if (IS_ERR(sk)) return PTR_ERR(sk); @@ -108,10 +109,7 @@ static int raw_diag_dump_one(struct sk_buff *in_skb, return -ENOMEM; } - err = inet_sk_diag_fill(sk, NULL, rep, r, - sk_user_ns(NETLINK_CB(in_skb).sk), - NETLINK_CB(in_skb).portid, - nlh->nlmsg_seq, 0, nlh, + err = inet_sk_diag_fill(sk, NULL, rep, cb, r, 0, netlink_net_capable(in_skb, CAP_NET_ADMIN)); sock_put(sk); @@ -136,11 +134,7 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, if (!inet_diag_bc_sk(bc, sk)) return 0; - return inet_sk_diag_fill(sk, NULL, skb, r, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, - cb->nlh, net_admin); + return inet_sk_diag_fill(sk, NULL, skb, cb, r, NLM_F_MULTI, net_admin); } static void raw_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index 0d08f9e2d8d0..bcd3a26efff1 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -184,10 +184,10 @@ static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r, bc); } -static int tcp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, +static int tcp_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { - return inet_diag_dump_one_icsk(&tcp_hashinfo, in_skb, nlh, req); + return inet_diag_dump_one_icsk(&tcp_hashinfo, cb, req); } #ifdef CONFIG_INET_DIAG_DESTROY diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 910555a4d9fe..7d65a6a5cd51 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -21,16 +21,15 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, if (!inet_diag_bc_sk(bc, sk)) return 0; - return inet_sk_diag_fill(sk, NULL, skb, req, - sk_user_ns(NETLINK_CB(cb->skb).sk), - NETLINK_CB(cb->skb).portid, - cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh, net_admin); + return inet_sk_diag_fill(sk, NULL, skb, cb, req, NLM_F_MULTI, + net_admin); } -static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, - const struct nlmsghdr *nlh, +static int udp_dump_one(struct udp_table *tbl, + struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { + struct sk_buff *in_skb = cb->skb; int err = -EINVAL; struct sock *sk = NULL; struct sk_buff *rep; @@ -70,11 +69,8 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, if (!rep) goto out; - err = inet_sk_diag_fill(sk, NULL, rep, req, - sk_user_ns(NETLINK_CB(in_skb).sk), - NETLINK_CB(in_skb).portid, - nlh->nlmsg_seq, 0, nlh, - netlink_net_capable(in_skb, CAP_NET_ADMIN)); + err = inet_sk_diag_fill(sk, NULL, rep, cb, req, 0, + netlink_net_capable(in_skb, CAP_NET_ADMIN)); if (err < 0) { WARN_ON(err == -EMSGSIZE); kfree_skb(rep); @@ -151,10 +147,10 @@ static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, udp_dump(&udp_table, skb, cb, r, bc); } -static int udp_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, +static int udp_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { - return udp_dump_one(&udp_table, in_skb, nlh, req); + return udp_dump_one(&udp_table, cb, req); } static void udp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, @@ -255,10 +251,10 @@ static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, udp_dump(&udplite_table, skb, cb, r, bc); } -static int udplite_diag_dump_one(struct sk_buff *in_skb, const struct nlmsghdr *nlh, +static int udplite_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { - return udp_dump_one(&udplite_table, in_skb, nlh, req); + return udp_dump_one(&udplite_table, cb, req); } static const struct inet_diag_handler udplite_diag_handler = { diff --git a/net/sctp/diag.c b/net/sctp/diag.c index 8a15146faaeb..bed6436cd0af 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -432,11 +432,12 @@ static void sctp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, sctp_get_sctp_info(sk, infox->asoc, infox->sctpinfo); } -static int sctp_diag_dump_one(struct sk_buff *in_skb, - const struct nlmsghdr *nlh, +static int sctp_diag_dump_one(struct netlink_callback *cb, const struct inet_diag_req_v2 *req) { + struct sk_buff *in_skb = cb->skb; struct net *net = sock_net(in_skb->sk); + const struct nlmsghdr *nlh = cb->nlh; union sctp_addr laddr, paddr; struct sctp_comm_param commp = { .skb = in_skb, -- cgit v1.2.3 From 0df6d32842b9a5f97a29ea90c8adc5cfac38341d Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Feb 2020 15:04:15 -0800 Subject: inet_diag: Move the INET_DIAG_REQ_BYTECODE nlattr to cb->data The INET_DIAG_REQ_BYTECODE nlattr is currently re-found every time when the "dump()" is re-started. In a latter patch, it will also need to parse the new INET_DIAG_REQ_SK_BPF_STORAGES nlattr to learn the map_fds. Thus, this patch takes this chance to store the parsed nlattr in cb->data during the "start" time of a dump. By doing this, the "bc" argument also becomes unnecessary and is removed. Also, the two copies of the INET_DIAG_REQ_BYTECODE parsing-audit logic between compat/current version can be consolidated to one. Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200225230415.1975555-1-kafai@fb.com --- include/linux/inet_diag.h | 11 ++-- include/uapi/linux/inet_diag.h | 3 +- net/dccp/diag.c | 4 +- net/ipv4/inet_diag.c | 117 ++++++++++++++++++++++++----------------- net/ipv4/raw_diag.c | 6 ++- net/ipv4/tcp_diag.c | 4 +- net/ipv4/udp_diag.c | 15 +++--- net/sctp/diag.c | 2 +- 8 files changed, 98 insertions(+), 64 deletions(-) (limited to 'include') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index 6b157ce07d74..1bb94cac265f 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -15,8 +15,7 @@ struct netlink_callback; struct inet_diag_handler { void (*dump)(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, - struct nlattr *bc); + const struct inet_diag_req_v2 *r); int (*dump_one)(struct netlink_callback *cb, const struct inet_diag_req_v2 *req); @@ -39,6 +38,11 @@ struct inet_diag_handler { __u16 idiag_info_size; }; +struct inet_diag_dump_data { + struct nlattr *req_nlas[__INET_DIAG_REQ_MAX]; +#define inet_diag_nla_bc req_nlas[INET_DIAG_REQ_BYTECODE] +}; + struct inet_connection_sock; int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, struct sk_buff *skb, struct netlink_callback *cb, @@ -46,8 +50,7 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, u16 nlmsg_flags, bool net_admin); void inet_diag_dump_icsk(struct inet_hashinfo *h, struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, - struct nlattr *bc); + const struct inet_diag_req_v2 *r); int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct netlink_callback *cb, const struct inet_diag_req_v2 *req); diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index a1ff345b3f33..bab9a9f8da12 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -64,9 +64,10 @@ struct inet_diag_req_raw { enum { INET_DIAG_REQ_NONE, INET_DIAG_REQ_BYTECODE, + __INET_DIAG_REQ_MAX, }; -#define INET_DIAG_REQ_MAX INET_DIAG_REQ_BYTECODE +#define INET_DIAG_REQ_MAX (__INET_DIAG_REQ_MAX - 1) /* Bytecode is sequence of 4 byte commands followed by variable arguments. * All the commands identified by "code" are conditional jumps forward: diff --git a/net/dccp/diag.c b/net/dccp/diag.c index 8f1e2a653f6d..8a82c5a2c5a8 100644 --- a/net/dccp/diag.c +++ b/net/dccp/diag.c @@ -46,9 +46,9 @@ static void dccp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, } static void dccp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { - inet_diag_dump_icsk(&dccp_hashinfo, skb, cb, r, bc); + inet_diag_dump_icsk(&dccp_hashinfo, skb, cb, r); } static int dccp_diag_dump_one(struct netlink_callback *cb, diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index d2ecff3195ba..4bce8a477699 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -495,9 +495,11 @@ static int inet_diag_cmd_exact(int cmd, struct sk_buff *in_skb, if (IS_ERR(handler)) { err = PTR_ERR(handler); } else if (cmd == SOCK_DIAG_BY_FAMILY) { + struct inet_diag_dump_data empty_dump_data = {}; struct netlink_callback cb = { .nlh = nlh, .skb = in_skb, + .data = &empty_dump_data, }; err = handler->dump_one(&cb, req); } else if (cmd == SOCK_DESTROY && handler->destroy) { @@ -863,14 +865,17 @@ static void twsk_build_assert(void) void inet_diag_dump_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); + struct inet_diag_dump_data *cb_data = cb->data; struct net *net = sock_net(skb->sk); u32 idiag_states = r->idiag_states; int i, num, s_i, s_num; + struct nlattr *bc; struct sock *sk; + bc = cb_data->inet_diag_nla_bc; if (idiag_states & TCPF_SYN_RECV) idiag_states |= TCPF_NEW_SYN_RECV; s_i = cb->args[1]; @@ -1014,15 +1019,14 @@ out: EXPORT_SYMBOL_GPL(inet_diag_dump_icsk); static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, - struct nlattr *bc) + const struct inet_diag_req_v2 *r) { const struct inet_diag_handler *handler; int err = 0; handler = inet_diag_lock_handler(r->sdiag_protocol); if (!IS_ERR(handler)) - handler->dump(skb, cb, r, bc); + handler->dump(skb, cb, r); else err = PTR_ERR(handler); inet_diag_unlock_handler(handler); @@ -1032,13 +1036,57 @@ static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, static int inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) { - int hdrlen = sizeof(struct inet_diag_req_v2); - struct nlattr *bc = NULL; + return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh)); +} + +static int __inet_diag_dump_start(struct netlink_callback *cb, int hdrlen) +{ + const struct nlmsghdr *nlh = cb->nlh; + struct inet_diag_dump_data *cb_data; + struct sk_buff *skb = cb->skb; + struct nlattr *nla; + int rem, err; + + cb_data = kzalloc(sizeof(*cb_data), GFP_KERNEL); + if (!cb_data) + return -ENOMEM; + + nla_for_each_attr(nla, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), rem) { + int type = nla_type(nla); + + if (type < __INET_DIAG_REQ_MAX) + cb_data->req_nlas[type] = nla; + } + + nla = cb_data->inet_diag_nla_bc; + if (nla) { + err = inet_diag_bc_audit(nla, skb); + if (err) { + kfree(cb_data); + return err; + } + } + + cb->data = cb_data; + return 0; +} + +static int inet_diag_dump_start(struct netlink_callback *cb) +{ + return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req_v2)); +} + +static int inet_diag_dump_start_compat(struct netlink_callback *cb) +{ + return __inet_diag_dump_start(cb, sizeof(struct inet_diag_req)); +} - if (nlmsg_attrlen(cb->nlh, hdrlen)) - bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE); +static int inet_diag_dump_done(struct netlink_callback *cb) +{ + kfree(cb->data); - return __inet_diag_dump(skb, cb, nlmsg_data(cb->nlh), bc); + return 0; } static int inet_diag_type2proto(int type) @@ -1057,9 +1105,7 @@ static int inet_diag_dump_compat(struct sk_buff *skb, struct netlink_callback *cb) { struct inet_diag_req *rc = nlmsg_data(cb->nlh); - int hdrlen = sizeof(struct inet_diag_req); struct inet_diag_req_v2 req; - struct nlattr *bc = NULL; req.sdiag_family = AF_UNSPEC; /* compatibility */ req.sdiag_protocol = inet_diag_type2proto(cb->nlh->nlmsg_type); @@ -1067,10 +1113,7 @@ static int inet_diag_dump_compat(struct sk_buff *skb, req.idiag_states = rc->idiag_states; req.id = rc->id; - if (nlmsg_attrlen(cb->nlh, hdrlen)) - bc = nlmsg_find_attr(cb->nlh, hdrlen, INET_DIAG_REQ_BYTECODE); - - return __inet_diag_dump(skb, cb, &req, bc); + return __inet_diag_dump(skb, cb, &req); } static int inet_diag_get_exact_compat(struct sk_buff *in_skb, @@ -1098,22 +1141,12 @@ static int inet_diag_rcv_msg_compat(struct sk_buff *skb, struct nlmsghdr *nlh) return -EINVAL; if (nlh->nlmsg_flags & NLM_F_DUMP) { - if (nlmsg_attrlen(nlh, hdrlen)) { - struct nlattr *attr; - int err; - - attr = nlmsg_find_attr(nlh, hdrlen, - INET_DIAG_REQ_BYTECODE); - err = inet_diag_bc_audit(attr, skb); - if (err) - return err; - } - { - struct netlink_dump_control c = { - .dump = inet_diag_dump_compat, - }; - return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); - } + struct netlink_dump_control c = { + .start = inet_diag_dump_start_compat, + .done = inet_diag_dump_done, + .dump = inet_diag_dump_compat, + }; + return netlink_dump_start(net->diag_nlsk, skb, nlh, &c); } return inet_diag_get_exact_compat(skb, nlh); @@ -1129,22 +1162,12 @@ static int inet_diag_handler_cmd(struct sk_buff *skb, struct nlmsghdr *h) if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY && h->nlmsg_flags & NLM_F_DUMP) { - if (nlmsg_attrlen(h, hdrlen)) { - struct nlattr *attr; - int err; - - attr = nlmsg_find_attr(h, hdrlen, - INET_DIAG_REQ_BYTECODE); - err = inet_diag_bc_audit(attr, skb); - if (err) - return err; - } - { - struct netlink_dump_control c = { - .dump = inet_diag_dump, - }; - return netlink_dump_start(net->diag_nlsk, skb, h, &c); - } + struct netlink_dump_control c = { + .start = inet_diag_dump_start, + .done = inet_diag_dump_done, + .dump = inet_diag_dump, + }; + return netlink_dump_start(net->diag_nlsk, skb, h, &c); } return inet_diag_cmd_exact(h->nlmsg_type, skb, h, nlmsg_data(h)); diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index a2933eeabd91..d19cce39be1b 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -138,17 +138,21 @@ static int sk_diag_dump(struct sock *sk, struct sk_buff *skb, } static void raw_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); struct raw_hashinfo *hashinfo = raw_get_hashinfo(r); struct net *net = sock_net(skb->sk); + struct inet_diag_dump_data *cb_data; int num, s_num, slot, s_slot; struct sock *sk = NULL; + struct nlattr *bc; if (IS_ERR(hashinfo)) return; + cb_data = cb->data; + bc = cb_data->inet_diag_nla_bc; s_slot = cb->args[0]; num = s_num = cb->args[1]; diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index bcd3a26efff1..75a1c985f49a 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -179,9 +179,9 @@ static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin) } static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { - inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r, bc); + inet_diag_dump_icsk(&tcp_hashinfo, skb, cb, r); } static int tcp_diag_dump_one(struct netlink_callback *cb, diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 7d65a6a5cd51..93884696abdd 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -89,12 +89,16 @@ out_nosk: static void udp_dump(struct udp_table *table, struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { bool net_admin = netlink_net_capable(cb->skb, CAP_NET_ADMIN); struct net *net = sock_net(skb->sk); + struct inet_diag_dump_data *cb_data; int num, s_num, slot, s_slot; + struct nlattr *bc; + cb_data = cb->data; + bc = cb_data->inet_diag_nla_bc; s_slot = cb->args[0]; num = s_num = cb->args[1]; @@ -142,9 +146,9 @@ done: } static void udp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { - udp_dump(&udp_table, skb, cb, r, bc); + udp_dump(&udp_table, skb, cb, r); } static int udp_diag_dump_one(struct netlink_callback *cb, @@ -245,10 +249,9 @@ static const struct inet_diag_handler udp_diag_handler = { }; static void udplite_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, - struct nlattr *bc) + const struct inet_diag_req_v2 *r) { - udp_dump(&udplite_table, skb, cb, r, bc); + udp_dump(&udplite_table, skb, cb, r); } static int udplite_diag_dump_one(struct netlink_callback *cb, diff --git a/net/sctp/diag.c b/net/sctp/diag.c index bed6436cd0af..69743a6aaf6f 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -471,7 +471,7 @@ static int sctp_diag_dump_one(struct netlink_callback *cb, } static void sctp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, - const struct inet_diag_req_v2 *r, struct nlattr *bc) + const struct inet_diag_req_v2 *r) { u32 idiag_states = r->idiag_states; struct net *net = sock_net(skb->sk); -- cgit v1.2.3 From 1ed4d92458a969e71e7914550b6f0c730c14d84e Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Feb 2020 15:04:21 -0800 Subject: bpf: INET_DIAG support in bpf_sk_storage This patch adds INET_DIAG support to bpf_sk_storage. 1. Although this series adds bpf_sk_storage diag capability to inet sk, bpf_sk_storage is in general applicable to all fullsock. Hence, the bpf_sk_storage logic will operate on SK_DIAG_* nlattr. The caller will pass in its specific nesting nlattr (e.g. INET_DIAG_*) as the argument. 2. The request will be like: INET_DIAG_REQ_SK_BPF_STORAGES (nla_nest) (defined in latter patch) SK_DIAG_BPF_STORAGE_REQ_MAP_FD (nla_put_u32) SK_DIAG_BPF_STORAGE_REQ_MAP_FD (nla_put_u32) ...... Considering there could have multiple bpf_sk_storages in a sk, instead of reusing INET_DIAG_INFO ("ss -i"), the user can select some specific bpf_sk_storage to dump by specifying an array of SK_DIAG_BPF_STORAGE_REQ_MAP_FD. If no SK_DIAG_BPF_STORAGE_REQ_MAP_FD is specified (i.e. an empty INET_DIAG_REQ_SK_BPF_STORAGES), it will dump all bpf_sk_storages of a sk. 3. The reply will be like: INET_DIAG_BPF_SK_STORAGES (nla_nest) (defined in latter patch) SK_DIAG_BPF_STORAGE (nla_nest) SK_DIAG_BPF_STORAGE_MAP_ID (nla_put_u32) SK_DIAG_BPF_STORAGE_MAP_VALUE (nla_reserve_64bit) SK_DIAG_BPF_STORAGE (nla_nest) SK_DIAG_BPF_STORAGE_MAP_ID (nla_put_u32) SK_DIAG_BPF_STORAGE_MAP_VALUE (nla_reserve_64bit) ...... 4. Unlike other INET_DIAG info of a sk which is pretty static, the size required to dump the bpf_sk_storage(s) of a sk is dynamic as the system adding more bpf_sk_storage_map. It is hard to set a static min_dump_alloc size. Hence, this series learns it at the runtime and adjust the cb->min_dump_alloc as it iterates all sk(s) of a system. The "unsigned int *res_diag_size" in bpf_sk_storage_diag_put() is for this purpose. The next patch will update the cb->min_dump_alloc as it iterates the sk(s). Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200225230421.1975729-1-kafai@fb.com --- include/linux/bpf.h | 1 + include/net/bpf_sk_storage.h | 27 ++++ include/uapi/linux/sock_diag.h | 26 ++++ kernel/bpf/syscall.c | 15 +++ net/core/bpf_sk_storage.c | 283 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 346 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 9aa33b8f3d55..6015a4daf118 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1023,6 +1023,7 @@ void __bpf_free_used_maps(struct bpf_prog_aux *aux, void bpf_prog_free_id(struct bpf_prog *prog, bool do_idr_lock); void bpf_map_free_id(struct bpf_map *map, bool do_idr_lock); +struct bpf_map *bpf_map_get(u32 ufd); struct bpf_map *bpf_map_get_with_uref(u32 ufd); struct bpf_map *__bpf_map_get(struct fd f); void bpf_map_inc(struct bpf_map *map); diff --git a/include/net/bpf_sk_storage.h b/include/net/bpf_sk_storage.h index 8e4f831d2e52..5036c94c0503 100644 --- a/include/net/bpf_sk_storage.h +++ b/include/net/bpf_sk_storage.h @@ -10,14 +10,41 @@ void bpf_sk_storage_free(struct sock *sk); extern const struct bpf_func_proto bpf_sk_storage_get_proto; extern const struct bpf_func_proto bpf_sk_storage_delete_proto; +struct bpf_sk_storage_diag; +struct sk_buff; +struct nlattr; +struct sock; + #ifdef CONFIG_BPF_SYSCALL int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk); +struct bpf_sk_storage_diag * +bpf_sk_storage_diag_alloc(const struct nlattr *nla_stgs); +void bpf_sk_storage_diag_free(struct bpf_sk_storage_diag *diag); +int bpf_sk_storage_diag_put(struct bpf_sk_storage_diag *diag, + struct sock *sk, struct sk_buff *skb, + int stg_array_type, + unsigned int *res_diag_size); #else static inline int bpf_sk_storage_clone(const struct sock *sk, struct sock *newsk) { return 0; } +static inline struct bpf_sk_storage_diag * +bpf_sk_storage_diag_alloc(const struct nlattr *nla) +{ + return NULL; +} +static inline void bpf_sk_storage_diag_free(struct bpf_sk_storage_diag *diag) +{ +} +static inline int bpf_sk_storage_diag_put(struct bpf_sk_storage_diag *diag, + struct sock *sk, struct sk_buff *skb, + int stg_array_type, + unsigned int *res_diag_size) +{ + return 0; +} #endif #endif /* _BPF_SK_STORAGE_H */ diff --git a/include/uapi/linux/sock_diag.h b/include/uapi/linux/sock_diag.h index e5925009a652..5f74a5f6091d 100644 --- a/include/uapi/linux/sock_diag.h +++ b/include/uapi/linux/sock_diag.h @@ -36,4 +36,30 @@ enum sknetlink_groups { }; #define SKNLGRP_MAX (__SKNLGRP_MAX - 1) +enum { + SK_DIAG_BPF_STORAGE_REQ_NONE, + SK_DIAG_BPF_STORAGE_REQ_MAP_FD, + __SK_DIAG_BPF_STORAGE_REQ_MAX, +}; + +#define SK_DIAG_BPF_STORAGE_REQ_MAX (__SK_DIAG_BPF_STORAGE_REQ_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_REP_NONE, + SK_DIAG_BPF_STORAGE, + __SK_DIAG_BPF_STORAGE_REP_MAX, +}; + +#define SK_DIAB_BPF_STORAGE_REP_MAX (__SK_DIAG_BPF_STORAGE_REP_MAX - 1) + +enum { + SK_DIAG_BPF_STORAGE_NONE, + SK_DIAG_BPF_STORAGE_PAD, + SK_DIAG_BPF_STORAGE_MAP_ID, + SK_DIAG_BPF_STORAGE_MAP_VALUE, + __SK_DIAG_BPF_STORAGE_MAX, +}; + +#define SK_DIAG_BPF_STORAGE_MAX (__SK_DIAG_BPF_STORAGE_MAX - 1) + #endif /* _UAPI__SOCK_DIAG_H__ */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index a79743a89815..c536c65256ad 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -902,6 +902,21 @@ void bpf_map_inc_with_uref(struct bpf_map *map) } EXPORT_SYMBOL_GPL(bpf_map_inc_with_uref); +struct bpf_map *bpf_map_get(u32 ufd) +{ + struct fd f = fdget(ufd); + struct bpf_map *map; + + map = __bpf_map_get(f); + if (IS_ERR(map)) + return map; + + bpf_map_inc(map); + fdput(f); + + return map; +} + struct bpf_map *bpf_map_get_with_uref(u32 ufd) { struct fd f = fdget(ufd); diff --git a/net/core/bpf_sk_storage.c b/net/core/bpf_sk_storage.c index 3ab23f698221..3415a4896c59 100644 --- a/net/core/bpf_sk_storage.c +++ b/net/core/bpf_sk_storage.c @@ -8,6 +8,7 @@ #include #include #include +#include #include static atomic_t cache_idx; @@ -606,6 +607,14 @@ static void bpf_sk_storage_map_free(struct bpf_map *map) kfree(map); } +/* U16_MAX is much more than enough for sk local storage + * considering a tcp_sock is ~2k. + */ +#define MAX_VALUE_SIZE \ + min_t(u32, \ + (KMALLOC_MAX_SIZE - MAX_BPF_STACK - sizeof(struct bpf_sk_storage_elem)), \ + (U16_MAX - sizeof(struct bpf_sk_storage_elem))) + static int bpf_sk_storage_map_alloc_check(union bpf_attr *attr) { if (attr->map_flags & ~SK_STORAGE_CREATE_FLAG_MASK || @@ -619,12 +628,7 @@ static int bpf_sk_storage_map_alloc_check(union bpf_attr *attr) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (attr->value_size >= KMALLOC_MAX_SIZE - - MAX_BPF_STACK - sizeof(struct bpf_sk_storage_elem) || - /* U16_MAX is much more than enough for sk local storage - * considering a tcp_sock is ~2k. - */ - attr->value_size > U16_MAX - sizeof(struct bpf_sk_storage_elem)) + if (attr->value_size > MAX_VALUE_SIZE) return -E2BIG; return 0; @@ -910,3 +914,270 @@ const struct bpf_func_proto bpf_sk_storage_delete_proto = { .arg1_type = ARG_CONST_MAP_PTR, .arg2_type = ARG_PTR_TO_SOCKET, }; + +struct bpf_sk_storage_diag { + u32 nr_maps; + struct bpf_map *maps[]; +}; + +/* The reply will be like: + * INET_DIAG_BPF_SK_STORAGES (nla_nest) + * SK_DIAG_BPF_STORAGE (nla_nest) + * SK_DIAG_BPF_STORAGE_MAP_ID (nla_put_u32) + * SK_DIAG_BPF_STORAGE_MAP_VALUE (nla_reserve_64bit) + * SK_DIAG_BPF_STORAGE (nla_nest) + * SK_DIAG_BPF_STORAGE_MAP_ID (nla_put_u32) + * SK_DIAG_BPF_STORAGE_MAP_VALUE (nla_reserve_64bit) + * .... + */ +static int nla_value_size(u32 value_size) +{ + /* SK_DIAG_BPF_STORAGE (nla_nest) + * SK_DIAG_BPF_STORAGE_MAP_ID (nla_put_u32) + * SK_DIAG_BPF_STORAGE_MAP_VALUE (nla_reserve_64bit) + */ + return nla_total_size(0) + nla_total_size(sizeof(u32)) + + nla_total_size_64bit(value_size); +} + +void bpf_sk_storage_diag_free(struct bpf_sk_storage_diag *diag) +{ + u32 i; + + if (!diag) + return; + + for (i = 0; i < diag->nr_maps; i++) + bpf_map_put(diag->maps[i]); + + kfree(diag); +} +EXPORT_SYMBOL_GPL(bpf_sk_storage_diag_free); + +static bool diag_check_dup(const struct bpf_sk_storage_diag *diag, + const struct bpf_map *map) +{ + u32 i; + + for (i = 0; i < diag->nr_maps; i++) { + if (diag->maps[i] == map) + return true; + } + + return false; +} + +struct bpf_sk_storage_diag * +bpf_sk_storage_diag_alloc(const struct nlattr *nla_stgs) +{ + struct bpf_sk_storage_diag *diag; + struct nlattr *nla; + u32 nr_maps = 0; + int rem, err; + + /* bpf_sk_storage_map is currently limited to CAP_SYS_ADMIN as + * the map_alloc_check() side also does. + */ + if (!capable(CAP_SYS_ADMIN)) + return ERR_PTR(-EPERM); + + nla_for_each_nested(nla, nla_stgs, rem) { + if (nla_type(nla) == SK_DIAG_BPF_STORAGE_REQ_MAP_FD) + nr_maps++; + } + + diag = kzalloc(sizeof(*diag) + sizeof(diag->maps[0]) * nr_maps, + GFP_KERNEL); + if (!diag) + return ERR_PTR(-ENOMEM); + + nla_for_each_nested(nla, nla_stgs, rem) { + struct bpf_map *map; + int map_fd; + + if (nla_type(nla) != SK_DIAG_BPF_STORAGE_REQ_MAP_FD) + continue; + + map_fd = nla_get_u32(nla); + map = bpf_map_get(map_fd); + if (IS_ERR(map)) { + err = PTR_ERR(map); + goto err_free; + } + if (map->map_type != BPF_MAP_TYPE_SK_STORAGE) { + bpf_map_put(map); + err = -EINVAL; + goto err_free; + } + if (diag_check_dup(diag, map)) { + bpf_map_put(map); + err = -EEXIST; + goto err_free; + } + diag->maps[diag->nr_maps++] = map; + } + + return diag; + +err_free: + bpf_sk_storage_diag_free(diag); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(bpf_sk_storage_diag_alloc); + +static int diag_get(struct bpf_sk_storage_data *sdata, struct sk_buff *skb) +{ + struct nlattr *nla_stg, *nla_value; + struct bpf_sk_storage_map *smap; + + /* It cannot exceed max nlattr's payload */ + BUILD_BUG_ON(U16_MAX - NLA_HDRLEN < MAX_VALUE_SIZE); + + nla_stg = nla_nest_start(skb, SK_DIAG_BPF_STORAGE); + if (!nla_stg) + return -EMSGSIZE; + + smap = rcu_dereference(sdata->smap); + if (nla_put_u32(skb, SK_DIAG_BPF_STORAGE_MAP_ID, smap->map.id)) + goto errout; + + nla_value = nla_reserve_64bit(skb, SK_DIAG_BPF_STORAGE_MAP_VALUE, + smap->map.value_size, + SK_DIAG_BPF_STORAGE_PAD); + if (!nla_value) + goto errout; + + if (map_value_has_spin_lock(&smap->map)) + copy_map_value_locked(&smap->map, nla_data(nla_value), + sdata->data, true); + else + copy_map_value(&smap->map, nla_data(nla_value), sdata->data); + + nla_nest_end(skb, nla_stg); + return 0; + +errout: + nla_nest_cancel(skb, nla_stg); + return -EMSGSIZE; +} + +static int bpf_sk_storage_diag_put_all(struct sock *sk, struct sk_buff *skb, + int stg_array_type, + unsigned int *res_diag_size) +{ + /* stg_array_type (e.g. INET_DIAG_BPF_SK_STORAGES) */ + unsigned int diag_size = nla_total_size(0); + struct bpf_sk_storage *sk_storage; + struct bpf_sk_storage_elem *selem; + struct bpf_sk_storage_map *smap; + struct nlattr *nla_stgs; + unsigned int saved_len; + int err = 0; + + rcu_read_lock(); + + sk_storage = rcu_dereference(sk->sk_bpf_storage); + if (!sk_storage || hlist_empty(&sk_storage->list)) { + rcu_read_unlock(); + return 0; + } + + nla_stgs = nla_nest_start(skb, stg_array_type); + if (!nla_stgs) + /* Continue to learn diag_size */ + err = -EMSGSIZE; + + saved_len = skb->len; + hlist_for_each_entry_rcu(selem, &sk_storage->list, snode) { + smap = rcu_dereference(SDATA(selem)->smap); + diag_size += nla_value_size(smap->map.value_size); + + if (nla_stgs && diag_get(SDATA(selem), skb)) + /* Continue to learn diag_size */ + err = -EMSGSIZE; + } + + rcu_read_unlock(); + + if (nla_stgs) { + if (saved_len == skb->len) + nla_nest_cancel(skb, nla_stgs); + else + nla_nest_end(skb, nla_stgs); + } + + if (diag_size == nla_total_size(0)) { + *res_diag_size = 0; + return 0; + } + + *res_diag_size = diag_size; + return err; +} + +int bpf_sk_storage_diag_put(struct bpf_sk_storage_diag *diag, + struct sock *sk, struct sk_buff *skb, + int stg_array_type, + unsigned int *res_diag_size) +{ + /* stg_array_type (e.g. INET_DIAG_BPF_SK_STORAGES) */ + unsigned int diag_size = nla_total_size(0); + struct bpf_sk_storage *sk_storage; + struct bpf_sk_storage_data *sdata; + struct nlattr *nla_stgs; + unsigned int saved_len; + int err = 0; + u32 i; + + *res_diag_size = 0; + + /* No map has been specified. Dump all. */ + if (!diag->nr_maps) + return bpf_sk_storage_diag_put_all(sk, skb, stg_array_type, + res_diag_size); + + rcu_read_lock(); + sk_storage = rcu_dereference(sk->sk_bpf_storage); + if (!sk_storage || hlist_empty(&sk_storage->list)) { + rcu_read_unlock(); + return 0; + } + + nla_stgs = nla_nest_start(skb, stg_array_type); + if (!nla_stgs) + /* Continue to learn diag_size */ + err = -EMSGSIZE; + + saved_len = skb->len; + for (i = 0; i < diag->nr_maps; i++) { + sdata = __sk_storage_lookup(sk_storage, + (struct bpf_sk_storage_map *)diag->maps[i], + false); + + if (!sdata) + continue; + + diag_size += nla_value_size(diag->maps[i]->value_size); + + if (nla_stgs && diag_get(sdata, skb)) + /* Continue to learn diag_size */ + err = -EMSGSIZE; + } + rcu_read_unlock(); + + if (nla_stgs) { + if (saved_len == skb->len) + nla_nest_cancel(skb, nla_stgs); + else + nla_nest_end(skb, nla_stgs); + } + + if (diag_size == nla_total_size(0)) { + *res_diag_size = 0; + return 0; + } + + *res_diag_size = diag_size; + return err; +} +EXPORT_SYMBOL_GPL(bpf_sk_storage_diag_put); -- cgit v1.2.3 From 085c20cacf2b72991ce1c9d99a5e2f1d9e73bb68 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Tue, 25 Feb 2020 15:04:27 -0800 Subject: bpf: inet_diag: Dump bpf_sk_storages in inet_diag_dump() This patch will dump out the bpf_sk_storages of a sk if the request has the INET_DIAG_REQ_SK_BPF_STORAGES nlattr. An array of SK_DIAG_BPF_STORAGE_REQ_MAP_FD can be specified in INET_DIAG_REQ_SK_BPF_STORAGES to select which bpf_sk_storage to dump. If no map_fd is specified, all bpf_sk_storages of a sk will be dumped. bpf_sk_storages can be added to the system at runtime. It is difficult to find a proper static value for cb->min_dump_alloc. This patch learns the nlattr size required to dump the bpf_sk_storages of a sk. If it happens to be the very first nlmsg of a dump and it cannot fit the needed bpf_sk_storages, it will try to expand the skb by "pskb_expand_head()". Instead of expanding it in inet_sk_diag_fill(), it is expanded at a sleepable context in __inet_diag_dump() so __GFP_DIRECT_RECLAIM can be used. In __inet_diag_dump(), it will retry as long as the skb is empty and the cb->min_dump_alloc becomes larger than before. cb->min_dump_alloc is bounded by KMALLOC_MAX_SIZE. The min_dump_alloc is also changed from 'u16' to 'u32' to accommodate a sk that may have a few large bpf_sk_storages. The updated cb->min_dump_alloc will also be used to allocate the skb in the next dump. This logic already exists in netlink_dump(). Here is the sample output of a locally modified 'ss' and it could be made more readable by using BTF later: [root@arch-fb-vm1 ~]# ss --bpf-map-id 14 --bpf-map-id 13 -t6an 'dst [::1]:8989' State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess ESTAB 0 0 [::1]:51072 [::1]:8989 bpf_map_id:14 value:[ 3feb ] bpf_map_id:13 value:[ 3f ] ESTAB 0 0 [::1]:51070 [::1]:8989 bpf_map_id:14 value:[ 3feb ] bpf_map_id:13 value:[ 3f ] [root@arch-fb-vm1 ~]# ~/devshare/github/iproute2/misc/ss --bpf-maps -t6an 'dst [::1]:8989' State Recv-Q Send-Q Local Address:Port Peer Address:Port Process ESTAB 0 0 [::1]:51072 [::1]:8989 bpf_map_id:14 value:[ 3feb ] bpf_map_id:13 value:[ 3f ] bpf_map_id:12 value:[ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000... total:65407 ] ESTAB 0 0 [::1]:51070 [::1]:8989 bpf_map_id:14 value:[ 3feb ] bpf_map_id:13 value:[ 3f ] bpf_map_id:12 value:[ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000... total:65407 ] Signed-off-by: Martin KaFai Lau Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200225230427.1976129-1-kafai@fb.com --- include/linux/inet_diag.h | 4 +++ include/linux/netlink.h | 4 +-- include/uapi/linux/inet_diag.h | 2 ++ net/ipv4/inet_diag.c | 74 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index 1bb94cac265f..e4ba25d63913 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -38,9 +38,13 @@ struct inet_diag_handler { __u16 idiag_info_size; }; +struct bpf_sk_storage_diag; struct inet_diag_dump_data { struct nlattr *req_nlas[__INET_DIAG_REQ_MAX]; #define inet_diag_nla_bc req_nlas[INET_DIAG_REQ_BYTECODE] +#define inet_diag_nla_bpf_stgs req_nlas[INET_DIAG_REQ_SK_BPF_STORAGES] + + struct bpf_sk_storage_diag *bpf_stg_diag; }; struct inet_connection_sock; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 205fa7b1f07a..788969ccbbde 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -188,10 +188,10 @@ struct netlink_callback { struct module *module; struct netlink_ext_ack *extack; u16 family; - u16 min_dump_alloc; - bool strict_check; u16 answer_flags; + u32 min_dump_alloc; unsigned int prev_seq, seq; + bool strict_check; union { u8 ctx[48]; diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index bab9a9f8da12..75dffd78363a 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -64,6 +64,7 @@ struct inet_diag_req_raw { enum { INET_DIAG_REQ_NONE, INET_DIAG_REQ_BYTECODE, + INET_DIAG_REQ_SK_BPF_STORAGES, __INET_DIAG_REQ_MAX, }; @@ -155,6 +156,7 @@ enum { INET_DIAG_CLASS_ID, /* request as INET_DIAG_TCLASS */ INET_DIAG_MD5SIG, INET_DIAG_ULP_INFO, + INET_DIAG_SK_BPF_STORAGES, __INET_DIAG_MAX, }; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 4bce8a477699..e1cad25909df 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -156,6 +157,8 @@ errout: } EXPORT_SYMBOL_GPL(inet_diag_msg_attrs_fill); +#define MAX_DUMP_ALLOC_SIZE (KMALLOC_MAX_SIZE - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, struct sk_buff *skb, struct netlink_callback *cb, const struct inet_diag_req_v2 *req, @@ -163,12 +166,14 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, { const struct tcp_congestion_ops *ca_ops; const struct inet_diag_handler *handler; + struct inet_diag_dump_data *cb_data; int ext = req->idiag_ext; struct inet_diag_msg *r; struct nlmsghdr *nlh; struct nlattr *attr; void *info = NULL; + cb_data = cb->data; handler = inet_diag_table[req->sdiag_protocol]; BUG_ON(!handler); @@ -302,6 +307,48 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, goto errout; } + /* Keep it at the end for potential retry with a larger skb, + * or else do best-effort fitting, which is only done for the + * first_nlmsg. + */ + if (cb_data->bpf_stg_diag) { + bool first_nlmsg = ((unsigned char *)nlh == skb->data); + unsigned int prev_min_dump_alloc; + unsigned int total_nla_size = 0; + unsigned int msg_len; + int err; + + msg_len = skb_tail_pointer(skb) - (unsigned char *)nlh; + err = bpf_sk_storage_diag_put(cb_data->bpf_stg_diag, sk, skb, + INET_DIAG_SK_BPF_STORAGES, + &total_nla_size); + + if (!err) + goto out; + + total_nla_size += msg_len; + prev_min_dump_alloc = cb->min_dump_alloc; + if (total_nla_size > prev_min_dump_alloc) + cb->min_dump_alloc = min_t(u32, total_nla_size, + MAX_DUMP_ALLOC_SIZE); + + if (!first_nlmsg) + goto errout; + + if (cb->min_dump_alloc > prev_min_dump_alloc) + /* Retry with pskb_expand_head() with + * __GFP_DIRECT_RECLAIM + */ + goto errout; + + WARN_ON_ONCE(total_nla_size <= prev_min_dump_alloc); + + /* Send what we have for this sk + * and move on to the next sk in the following + * dump() + */ + } + out: nlmsg_end(skb, nlh); return 0; @@ -1022,8 +1069,11 @@ static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, const struct inet_diag_req_v2 *r) { const struct inet_diag_handler *handler; + u32 prev_min_dump_alloc; int err = 0; +again: + prev_min_dump_alloc = cb->min_dump_alloc; handler = inet_diag_lock_handler(r->sdiag_protocol); if (!IS_ERR(handler)) handler->dump(skb, cb, r); @@ -1031,6 +1081,15 @@ static int __inet_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, err = PTR_ERR(handler); inet_diag_unlock_handler(handler); + /* The skb is not large enough to fit one sk info and + * inet_sk_diag_fill() has requested for a larger skb. + */ + if (!skb->len && cb->min_dump_alloc > prev_min_dump_alloc) { + err = pskb_expand_head(skb, 0, cb->min_dump_alloc, GFP_KERNEL); + if (!err) + goto again; + } + return err ? : skb->len; } @@ -1068,6 +1127,18 @@ static int __inet_diag_dump_start(struct netlink_callback *cb, int hdrlen) } } + nla = cb_data->inet_diag_nla_bpf_stgs; + if (nla) { + struct bpf_sk_storage_diag *bpf_stg_diag; + + bpf_stg_diag = bpf_sk_storage_diag_alloc(nla); + if (IS_ERR(bpf_stg_diag)) { + kfree(cb_data); + return PTR_ERR(bpf_stg_diag); + } + cb_data->bpf_stg_diag = bpf_stg_diag; + } + cb->data = cb_data; return 0; } @@ -1084,6 +1155,9 @@ static int inet_diag_dump_start_compat(struct netlink_callback *cb) static int inet_diag_dump_done(struct netlink_callback *cb) { + struct inet_diag_dump_data *cb_data = cb->data; + + bpf_sk_storage_diag_free(cb_data->bpf_stg_diag); kfree(cb->data); return 0; -- cgit v1.2.3 From 253d3194c2b58152fe830fd27c2fd83ebc6fe5ee Mon Sep 17 00:00:00 2001 From: Mark Rutland Date: Mon, 10 Feb 2020 13:00:13 +0000 Subject: random: add arch_get_random_*long_early() Some architectures (e.g. arm64) can have heterogeneous CPUs, and the boot CPU may be able to provide entropy while secondary CPUs cannot. On such systems, arch_get_random_long() and arch_get_random_seed_long() will fail unless support for RNG instructions has been detected on all CPUs. This prevents the boot CPU from being able to provide (potentially) trusted entropy when seeding the primary CRNG. To make it possible to seed the primary CRNG from the boot CPU without adversely affecting the runtime versions of arch_get_random_long() and arch_get_random_seed_long(), this patch adds new early versions of the functions used when initializing the primary CRNG. Default implementations are provided atop of the existing arch_get_random_long() and arch_get_random_seed_long() so that only architectures with such constraints need to provide the new helpers. There should be no functional change as a result of this patch. Signed-off-by: Mark Rutland Cc: Mark Brown Cc: Theodore Ts'o Link: https://lore.kernel.org/r/20200210130015.17664-3-mark.rutland@arm.com Signed-off-by: Theodore Ts'o --- drivers/char/random.c | 20 +++++++++++++++++++- include/linux/random.h | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/char/random.c b/drivers/char/random.c index 62d32e62f2da..02a85b87b993 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -799,6 +799,24 @@ static bool crng_init_try_arch(struct crng_state *crng) return arch_init; } +static bool __init crng_init_try_arch_early(struct crng_state *crng) +{ + int i; + bool arch_init = true; + unsigned long rv; + + for (i = 4; i < 16; i++) { + if (!arch_get_random_seed_long_early(&rv) && + !arch_get_random_long_early(&rv)) { + rv = random_get_entropy(); + arch_init = false; + } + crng->state[i] ^= rv; + } + + return arch_init; +} + static void crng_initialize_secondary(struct crng_state *crng) { memcpy(&crng->state[0], "expand 32-byte k", 16); @@ -811,7 +829,7 @@ static void __init crng_initialize_primary(struct crng_state *crng) { memcpy(&crng->state[0], "expand 32-byte k", 16); _extract_entropy(&input_pool, &crng->state[4], sizeof(__u32) * 12, 0); - if (crng_init_try_arch(crng) && trust_cpu) { + if (crng_init_try_arch_early(crng) && trust_cpu) { invalidate_batched_entropy(); numa_crng_init(); crng_init = 2; diff --git a/include/linux/random.h b/include/linux/random.h index d319f9a1e429..45e1f8fa742b 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -7,6 +7,8 @@ #ifndef _LINUX_RANDOM_H #define _LINUX_RANDOM_H +#include +#include #include #include @@ -185,6 +187,26 @@ static inline bool __must_check arch_get_random_seed_int(unsigned int *v) } #endif +/* + * Called from the boot CPU during startup; not valid to call once + * secondary CPUs are up and preemption is possible. + */ +#ifndef arch_get_random_seed_long_early +static inline bool __init arch_get_random_seed_long_early(unsigned long *v) +{ + WARN_ON(system_state != SYSTEM_BOOTING); + return arch_get_random_seed_long(v); +} +#endif + +#ifndef arch_get_random_long_early +static inline bool __init arch_get_random_long_early(unsigned long *v) +{ + WARN_ON(system_state != SYSTEM_BOOTING); + return arch_get_random_long(v); +} +#endif + /* Pseudo random number generator from numerical recipes. */ static inline u32 next_pseudo_random32(u32 seed) { -- cgit v1.2.3 From a7e454542bf8d57c75f59e7e7326c21db3d0bb3f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Wed, 26 Feb 2020 17:02:27 -0600 Subject: Bluetooth: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btqca.h | 6 +++--- drivers/bluetooth/btrtl.h | 4 ++-- include/net/bluetooth/hci.h | 30 +++++++++++++++--------------- include/net/bluetooth/hci_sock.h | 6 +++--- include/net/bluetooth/l2cap.h | 8 ++++---- include/net/bluetooth/rfcomm.h | 2 +- net/bluetooth/a2mp.h | 10 +++++----- net/bluetooth/bnep/bnep.h | 6 +++--- 8 files changed, 36 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index f5795b1a3779..e16a4d650597 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -79,7 +79,7 @@ struct qca_fw_config { struct edl_event_hdr { __u8 cresp; __u8 rtype; - __u8 data[0]; + __u8 data[]; } __packed; struct qca_btsoc_version { @@ -112,12 +112,12 @@ struct tlv_type_nvm { __le16 tag_len; __le32 reserve1; __le32 reserve2; - __u8 data[0]; + __u8 data[]; } __packed; struct tlv_type_hdr { __le32 type_len; - __u8 data[0]; + __u8 data[]; } __packed; enum qca_btsoc_type { diff --git a/drivers/bluetooth/btrtl.h b/drivers/bluetooth/btrtl.h index 10ad40c3e42c..2a582682136d 100644 --- a/drivers/bluetooth/btrtl.h +++ b/drivers/bluetooth/btrtl.h @@ -38,13 +38,13 @@ struct rtl_epatch_header { struct rtl_vendor_config_entry { __le16 offset; __u8 len; - __u8 data[0]; + __u8 data[]; } __packed; struct rtl_vendor_config { __le32 signature; __le16 total_len; - struct rtl_vendor_config_entry entry[0]; + struct rtl_vendor_config_entry entry[]; } __packed; #if IS_ENABLED(CONFIG_BT_RTL) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6293bdd7d862..d878bf8dce20 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -935,7 +935,7 @@ struct hci_cp_sniff_subrate { struct hci_cp_set_event_flt { __u8 flt_type; __u8 cond_type; - __u8 condition[0]; + __u8 condition[]; } __packed; /* Filter types */ @@ -1335,7 +1335,7 @@ struct hci_rp_read_local_amp_assoc { __u8 status; __u8 phy_handle; __le16 rem_len; - __u8 frag[0]; + __u8 frag[]; } __packed; #define HCI_OP_WRITE_REMOTE_AMP_ASSOC 0x140b @@ -1343,7 +1343,7 @@ struct hci_cp_write_remote_amp_assoc { __u8 phy_handle; __le16 len_so_far; __le16 rem_len; - __u8 frag[0]; + __u8 frag[]; } __packed; struct hci_rp_write_remote_amp_assoc { __u8 status; @@ -1613,7 +1613,7 @@ struct hci_cp_le_set_ext_scan_params { __u8 own_addr_type; __u8 filter_policy; __u8 scanning_phys; - __u8 data[0]; + __u8 data[]; } __packed; #define LE_SCAN_PHY_1M 0x01 @@ -1641,7 +1641,7 @@ struct hci_cp_le_ext_create_conn { __u8 peer_addr_type; bdaddr_t peer_addr; __u8 phys; - __u8 data[0]; + __u8 data[]; } __packed; struct hci_cp_le_ext_conn_param { @@ -1693,7 +1693,7 @@ struct hci_rp_le_set_ext_adv_params { struct hci_cp_le_set_ext_adv_enable { __u8 enable; __u8 num_of_sets; - __u8 data[0]; + __u8 data[]; } __packed; struct hci_cp_ext_adv_set { @@ -1775,14 +1775,14 @@ struct hci_cp_le_set_cig_params { __le16 m_latency; __le16 s_latency; __u8 num_cis; - struct hci_cis_params cis[0]; + struct hci_cis_params cis[]; } __packed; struct hci_rp_le_set_cig_params { __u8 status; __u8 cig_id; __u8 num_handles; - __le16 handle[0]; + __le16 handle[]; } __packed; #define HCI_OP_LE_CREATE_CIS 0x2064 @@ -1793,7 +1793,7 @@ struct hci_cis { struct hci_cp_le_create_cis { __u8 num_cis; - struct hci_cis cis[0]; + struct hci_cis cis[]; } __packed; #define HCI_OP_LE_REMOVE_CIG 0x2065 @@ -1937,7 +1937,7 @@ struct hci_comp_pkts_info { struct hci_ev_num_comp_pkts { __u8 num_hndl; - struct hci_comp_pkts_info handles[0]; + struct hci_comp_pkts_info handles[]; } __packed; #define HCI_EV_MODE_CHANGE 0x14 @@ -2170,7 +2170,7 @@ struct hci_comp_blocks_info { struct hci_ev_num_comp_blocks { __le16 num_blocks; __u8 num_hndl; - struct hci_comp_blocks_info handles[0]; + struct hci_comp_blocks_info handles[]; } __packed; #define HCI_EV_SYNC_TRAIN_COMPLETE 0x4F @@ -2226,7 +2226,7 @@ struct hci_ev_le_advertising_info { __u8 bdaddr_type; bdaddr_t bdaddr; __u8 length; - __u8 data[0]; + __u8 data[]; } __packed; #define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 @@ -2302,7 +2302,7 @@ struct hci_ev_le_ext_adv_report { __u8 direct_addr_type; bdaddr_t direct_addr; __u8 length; - __u8 data[0]; + __u8 data[]; } __packed; #define HCI_EV_LE_ENHANCED_CONN_COMPLETE 0x0a @@ -2362,7 +2362,7 @@ struct hci_evt_le_cis_req { #define HCI_EV_STACK_INTERNAL 0xfd struct hci_ev_stack_internal { __u16 type; - __u8 data[0]; + __u8 data[]; } __packed; #define HCI_EV_SI_DEVICE 0x01 @@ -2409,7 +2409,7 @@ struct hci_sco_hdr { struct hci_iso_hdr { __le16 handle; __le16 dlen; - __u8 data[0]; + __u8 data[]; } __packed; /* ISO data packet status flags */ diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h index 8e9138acdae1..9352bb1bf34c 100644 --- a/include/net/bluetooth/hci_sock.h +++ b/include/net/bluetooth/hci_sock.h @@ -144,19 +144,19 @@ struct hci_dev_req { struct hci_dev_list_req { __u16 dev_num; - struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ + struct hci_dev_req dev_req[]; /* hci_dev_req structures */ }; struct hci_conn_list_req { __u16 dev_id; __u16 conn_num; - struct hci_conn_info conn_info[0]; + struct hci_conn_info conn_info[]; }; struct hci_conn_info_req { bdaddr_t bdaddr; __u8 type; - struct hci_conn_info conn_info[0]; + struct hci_conn_info conn_info[]; }; struct hci_auth_info_req { diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 093aedebdf0c..61dc731d5666 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -299,14 +299,14 @@ struct l2cap_conn_rsp { struct l2cap_conf_req { __le16 dcid; __le16 flags; - __u8 data[0]; + __u8 data[]; } __packed; struct l2cap_conf_rsp { __le16 scid; __le16 flags; __le16 result; - __u8 data[0]; + __u8 data[]; } __packed; #define L2CAP_CONF_SUCCESS 0x0000 @@ -322,7 +322,7 @@ struct l2cap_conf_rsp { struct l2cap_conf_opt { __u8 type; __u8 len; - __u8 val[0]; + __u8 val[]; } __packed; #define L2CAP_CONF_OPT_SIZE 2 @@ -392,7 +392,7 @@ struct l2cap_info_req { struct l2cap_info_rsp { __le16 type; __le16 result; - __u8 data[0]; + __u8 data[]; } __packed; struct l2cap_create_chan_req { diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 8d65d2a0b9b4..99d26879b02a 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -355,7 +355,7 @@ struct rfcomm_dev_info { struct rfcomm_dev_list_req { u16 dev_num; - struct rfcomm_dev_info dev_info[0]; + struct rfcomm_dev_info dev_info[]; }; int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); diff --git a/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h index 0029d5119be6..2fd253a61a2a 100644 --- a/net/bluetooth/a2mp.h +++ b/net/bluetooth/a2mp.h @@ -36,14 +36,14 @@ struct a2mp_cmd { __u8 code; __u8 ident; __le16 len; - __u8 data[0]; + __u8 data[]; } __packed; /* A2MP command codes */ #define A2MP_COMMAND_REJ 0x01 struct a2mp_cmd_rej { __le16 reason; - __u8 data[0]; + __u8 data[]; } __packed; #define A2MP_DISCOVER_REQ 0x02 @@ -62,7 +62,7 @@ struct a2mp_cl { struct a2mp_discov_rsp { __le16 mtu; __le16 ext_feat; - struct a2mp_cl cl[0]; + struct a2mp_cl cl[]; } __packed; #define A2MP_CHANGE_NOTIFY 0x04 @@ -93,7 +93,7 @@ struct a2mp_amp_assoc_req { struct a2mp_amp_assoc_rsp { __u8 id; __u8 status; - __u8 amp_assoc[0]; + __u8 amp_assoc[]; } __packed; #define A2MP_CREATEPHYSLINK_REQ 0x0A @@ -101,7 +101,7 @@ struct a2mp_amp_assoc_rsp { struct a2mp_physlink_req { __u8 local_id; __u8 remote_id; - __u8 amp_assoc[0]; + __u8 amp_assoc[]; } __packed; #define A2MP_CREATEPHYSLINK_RSP 0x0B diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h index 24f18b133959..9680473ed7ef 100644 --- a/net/bluetooth/bnep/bnep.h +++ b/net/bluetooth/bnep/bnep.h @@ -74,14 +74,14 @@ struct bnep_setup_conn_req { __u8 type; __u8 ctrl; __u8 uuid_size; - __u8 service[0]; + __u8 service[]; } __packed; struct bnep_set_filter_req { __u8 type; __u8 ctrl; __be16 len; - __u8 list[0]; + __u8 list[]; } __packed; struct bnep_control_rsp { @@ -93,7 +93,7 @@ struct bnep_control_rsp { struct bnep_ext_hdr { __u8 type; __u8 len; - __u8 data[0]; + __u8 data[]; } __packed; /* BNEP ioctl defines */ -- cgit v1.2.3 From a9e45698b37d4235ec98b5c0327de59759cb2ef2 Mon Sep 17 00:00:00 2001 From: Sathish Narsimman Date: Mon, 24 Feb 2020 11:02:24 +0530 Subject: Bluetooth: Remove adv set for directed advertising Extended advertising Data is set during bluetooth initialization by default which causes InvalidHCICommandParameters when setting Extended advertising parameters. As per Core Spec 5.2 Vol 2, PART E, Sec 7.8.53, for advertising_event_property LE_LEGACY_ADV_DIRECT_IND does not supports advertising data when the advertising set already contains some, the controller shall return erroc code 'InvalidHCICommandParameters(0x12). So it is required to remove adv set for handle 0x00. since we use instance 0 for directed adv. Signed-off-by: Sathish Narsimman Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 ++ net/bluetooth/hci_conn.c | 10 ++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index d878bf8dce20..29b638c6c934 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1724,6 +1724,8 @@ struct hci_cp_le_set_ext_scan_rsp_data { #define LE_SET_ADV_DATA_NO_FRAG 0x01 +#define HCI_OP_LE_REMOVE_ADV_SET 0x203c + #define HCI_OP_LE_CLEAR_ADV_SETS 0x203d #define HCI_OP_LE_SET_ADV_SET_RAND_ADDR 0x2035 diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a582c676e584..2731f0ad2a90 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -898,6 +898,16 @@ static void hci_req_directed_advertising(struct hci_request *req, cp.peer_addr_type = conn->dst_type; bacpy(&cp.peer_addr, &conn->dst); + /* As per Core Spec 5.2 Vol 2, PART E, Sec 7.8.53, for + * advertising_event_property LE_LEGACY_ADV_DIRECT_IND + * does not supports advertising data when the advertising set already + * contains some, the controller shall return erroc code 'Invalid + * HCI Command Parameters(0x12). + * So it is required to remove adv set for handle 0x00. since we use + * instance 0 for directed adv. + */ + hci_req_add(req, HCI_OP_LE_REMOVE_ADV_SET, sizeof(cp.handle), &cp.handle); + hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(cp), &cp); if (own_addr_type == ADDR_LE_DEV_RANDOM && -- cgit v1.2.3 From e37c57a888b72d91c07e1c6a11a6bda499b01cff Mon Sep 17 00:00:00 2001 From: Alain Michaud Date: Thu, 27 Feb 2020 22:00:49 +0000 Subject: Bluetooth: Fixing a few comment typos in the quirk definitions. This change simply fixes a few typos in the quirk definitions. Signed-off-by: Alain Michaud Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 29b638c6c934..27b6363dd9c6 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -115,7 +115,7 @@ enum { * wrongly configured local features that will require forcing * them to enable this mode. Getting RSSI information with the * inquiry responses is preferred since it allows for a better - * user expierence. + * user experience. * * This quirk must be set before hci_register_dev is called. */ @@ -142,7 +142,7 @@ enum { /* When this quirk is set, an external configuration step * is required and will be indicated with the controller - * configuation. + * configuration. * * This quirk can be set before hci_register_dev is called or * during the hdev->setup vendor callback. -- cgit v1.2.3 From 4b127bd5f2cc1b2da041f472dab6dc729cdd4711 Mon Sep 17 00:00:00 2001 From: Alain Michaud Date: Thu, 27 Feb 2020 18:29:39 +0000 Subject: Bluetooth: Support querying for WBS support through MGMT This patch provides a mechanism for MGMT interface client to query the capability of the controller to support WBS. Signed-off-by: Alain Michaud Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 3 +++ include/net/bluetooth/hci.h | 9 +++++++++ include/net/bluetooth/mgmt.h | 3 ++- net/bluetooth/mgmt.c | 4 ++++ 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b34a71716fe1..48e78fdc8e83 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3867,6 +3867,9 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_BROKEN_ISOC) data->isoc = NULL; + if (id->driver_info & BTUSB_WIDEBAND_SPEECH) + set_bit(HCI_QUIRK_WIDE_BAND_SPEECH_SUPPORTED, &hdev->quirks); + if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 27b6363dd9c6..0b3ebd35681d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -205,6 +205,15 @@ enum { * */ HCI_QUIRK_NON_PERSISTENT_SETUP, + + /* When this quirk is set, wide band speech is supported by + * the driver since no reliable mechanism exist to report + * this from the hardware, a driver flag is use to convey + * this support + * + * This quirk must be set before hci_register_dev is called. + */ + HCI_QUIRK_WIDE_BAND_SPEECH_SUPPORTED, }; /* HCI device flags */ diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index a90666af05bd..f69f88e8e109 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -101,7 +101,8 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_PRIVACY 0x00002000 #define MGMT_SETTING_CONFIGURATION 0x00004000 #define MGMT_SETTING_STATIC_ADDRESS 0x00008000 -#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000 +#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000 +#define MGMT_SETTING_WIDE_BAND_SPEECH 0x00020000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3074363c68df..1002c657768a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -762,6 +762,10 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (lmp_sc_capable(hdev)) settings |= MGMT_SETTING_SECURE_CONN; + + if (test_bit(HCI_QUIRK_WIDE_BAND_SPEECH_SUPPORTED, + &hdev->quirks)) + settings |= MGMT_SETTING_WIDE_BAND_SPEECH; } if (lmp_le_capable(hdev)) { -- cgit v1.2.3 From aae57773fbe01b573c9447d877964857cfe1d0f0 Mon Sep 17 00:00:00 2001 From: David Dai Date: Fri, 28 Feb 2020 12:11:40 +0200 Subject: interconnect: qcom: sdm845: Split qnodes into their respective NoCs In order to better represent the hardware and its different Network-On-Chip devices, split the sdm845 provider driver into NoC specific providers. Remove duplicate functionality already provided by the icc rpmh and bcm voter drivers to calculate and commit bandwidth requests to hardware. Signed-off-by: David Dai Signed-off-by: Odelu Kukatla Signed-off-by: Sibi Sankar Reviewed-by: Evan Green Link: https://lore.kernel.org/r/20200209183411.17195-6-sibis@codeaurora.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/sdm845.c | 1185 ++++++++++-------------- include/dt-bindings/interconnect/qcom,sdm845.h | 263 +++--- 2 files changed, 632 insertions(+), 816 deletions(-) (limited to 'include') diff --git a/drivers/interconnect/qcom/sdm845.c b/drivers/interconnect/qcom/sdm845.c index f078cf0fce56..ab968afeee59 100644 --- a/drivers/interconnect/qcom/sdm845.c +++ b/drivers/interconnect/qcom/sdm845.c @@ -1,379 +1,377 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. - * + * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. */ -#include -#include #include #include #include -#include #include #include -#include -#include -#include - -#include -#include -#include - -#define to_qcom_provider(_provider) \ - container_of(_provider, struct qcom_icc_provider, provider) - -struct qcom_icc_provider { - struct icc_provider provider; - struct device *dev; - struct qcom_icc_bcm **bcms; - size_t num_bcms; -}; - -/** - * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager (BCM) - * @unit: divisor used to convert bytes/sec bw value to an RPMh msg - * @width: multiplier used to convert bytes/sec bw value to an RPMh msg - * @vcd: virtual clock domain that this bcm belongs to - * @reserved: reserved field - */ -struct bcm_db { - __le32 unit; - __le16 width; - u8 vcd; - u8 reserved; -}; -#define SDM845_MAX_LINKS 43 -#define SDM845_MAX_BCMS 30 -#define SDM845_MAX_BCM_PER_NODE 2 -#define SDM845_MAX_VCD 10 +#include -/* - * The AMC bucket denotes constraints that are applied to hardware when - * icc_set_bw() completes, whereas the WAKE and SLEEP constraints are applied - * when the execution environment transitions between active and low power mode. - */ -#define QCOM_ICC_BUCKET_AMC 0 -#define QCOM_ICC_BUCKET_WAKE 1 -#define QCOM_ICC_BUCKET_SLEEP 2 -#define QCOM_ICC_NUM_BUCKETS 3 -#define QCOM_ICC_TAG_AMC BIT(QCOM_ICC_BUCKET_AMC) -#define QCOM_ICC_TAG_WAKE BIT(QCOM_ICC_BUCKET_WAKE) -#define QCOM_ICC_TAG_SLEEP BIT(QCOM_ICC_BUCKET_SLEEP) -#define QCOM_ICC_TAG_ACTIVE_ONLY (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE) -#define QCOM_ICC_TAG_ALWAYS (QCOM_ICC_TAG_AMC | QCOM_ICC_TAG_WAKE |\ - QCOM_ICC_TAG_SLEEP) - -/** - * struct qcom_icc_node - Qualcomm specific interconnect nodes - * @name: the node name used in debugfs - * @links: an array of nodes where we can go next while traversing - * @id: a unique node identifier - * @num_links: the total number of @links - * @channels: num of channels at this node - * @buswidth: width of the interconnect between a node and the bus - * @sum_avg: current sum aggregate value of all avg bw requests - * @max_peak: current max aggregate value of all peak bw requests - * @bcms: list of bcms associated with this logical node - * @num_bcms: num of @bcms - */ -struct qcom_icc_node { - const char *name; - u16 links[SDM845_MAX_LINKS]; - u16 id; - u16 num_links; - u16 channels; - u16 buswidth; - u64 sum_avg[QCOM_ICC_NUM_BUCKETS]; - u64 max_peak[QCOM_ICC_NUM_BUCKETS]; - struct qcom_icc_bcm *bcms[SDM845_MAX_BCM_PER_NODE]; - size_t num_bcms; +#include "bcm-voter.h" +#include "icc-rpmh.h" + +enum { + SDM845_MASTER_A1NOC_CFG = 1, + SDM845_MASTER_BLSP_1, + SDM845_MASTER_TSIF, + SDM845_MASTER_SDCC_2, + SDM845_MASTER_SDCC_4, + SDM845_MASTER_UFS_CARD, + SDM845_MASTER_UFS_MEM, + SDM845_MASTER_PCIE_0, + SDM845_MASTER_A2NOC_CFG, + SDM845_MASTER_QDSS_BAM, + SDM845_MASTER_BLSP_2, + SDM845_MASTER_CNOC_A2NOC, + SDM845_MASTER_CRYPTO, + SDM845_MASTER_IPA, + SDM845_MASTER_PCIE_1, + SDM845_MASTER_QDSS_ETR, + SDM845_MASTER_USB3_0, + SDM845_MASTER_USB3_1, + SDM845_MASTER_CAMNOC_HF0_UNCOMP, + SDM845_MASTER_CAMNOC_HF1_UNCOMP, + SDM845_MASTER_CAMNOC_SF_UNCOMP, + SDM845_MASTER_SPDM, + SDM845_MASTER_TIC, + SDM845_MASTER_SNOC_CNOC, + SDM845_MASTER_QDSS_DAP, + SDM845_MASTER_CNOC_DC_NOC, + SDM845_MASTER_APPSS_PROC, + SDM845_MASTER_GNOC_CFG, + SDM845_MASTER_LLCC, + SDM845_MASTER_TCU_0, + SDM845_MASTER_MEM_NOC_CFG, + SDM845_MASTER_GNOC_MEM_NOC, + SDM845_MASTER_MNOC_HF_MEM_NOC, + SDM845_MASTER_MNOC_SF_MEM_NOC, + SDM845_MASTER_SNOC_GC_MEM_NOC, + SDM845_MASTER_SNOC_SF_MEM_NOC, + SDM845_MASTER_GFX3D, + SDM845_MASTER_CNOC_MNOC_CFG, + SDM845_MASTER_CAMNOC_HF0, + SDM845_MASTER_CAMNOC_HF1, + SDM845_MASTER_CAMNOC_SF, + SDM845_MASTER_MDP0, + SDM845_MASTER_MDP1, + SDM845_MASTER_ROTATOR, + SDM845_MASTER_VIDEO_P0, + SDM845_MASTER_VIDEO_P1, + SDM845_MASTER_VIDEO_PROC, + SDM845_MASTER_SNOC_CFG, + SDM845_MASTER_A1NOC_SNOC, + SDM845_MASTER_A2NOC_SNOC, + SDM845_MASTER_GNOC_SNOC, + SDM845_MASTER_MEM_NOC_SNOC, + SDM845_MASTER_ANOC_PCIE_SNOC, + SDM845_MASTER_PIMEM, + SDM845_MASTER_GIC, + SDM845_SLAVE_A1NOC_SNOC, + SDM845_SLAVE_SERVICE_A1NOC, + SDM845_SLAVE_ANOC_PCIE_A1NOC_SNOC, + SDM845_SLAVE_A2NOC_SNOC, + SDM845_SLAVE_ANOC_PCIE_SNOC, + SDM845_SLAVE_SERVICE_A2NOC, + SDM845_SLAVE_CAMNOC_UNCOMP, + SDM845_SLAVE_A1NOC_CFG, + SDM845_SLAVE_A2NOC_CFG, + SDM845_SLAVE_AOP, + SDM845_SLAVE_AOSS, + SDM845_SLAVE_CAMERA_CFG, + SDM845_SLAVE_CLK_CTL, + SDM845_SLAVE_CDSP_CFG, + SDM845_SLAVE_RBCPR_CX_CFG, + SDM845_SLAVE_CRYPTO_0_CFG, + SDM845_SLAVE_DCC_CFG, + SDM845_SLAVE_CNOC_DDRSS, + SDM845_SLAVE_DISPLAY_CFG, + SDM845_SLAVE_GLM, + SDM845_SLAVE_GFX3D_CFG, + SDM845_SLAVE_IMEM_CFG, + SDM845_SLAVE_IPA_CFG, + SDM845_SLAVE_CNOC_MNOC_CFG, + SDM845_SLAVE_PCIE_0_CFG, + SDM845_SLAVE_PCIE_1_CFG, + SDM845_SLAVE_PDM, + SDM845_SLAVE_SOUTH_PHY_CFG, + SDM845_SLAVE_PIMEM_CFG, + SDM845_SLAVE_PRNG, + SDM845_SLAVE_QDSS_CFG, + SDM845_SLAVE_BLSP_2, + SDM845_SLAVE_BLSP_1, + SDM845_SLAVE_SDCC_2, + SDM845_SLAVE_SDCC_4, + SDM845_SLAVE_SNOC_CFG, + SDM845_SLAVE_SPDM_WRAPPER, + SDM845_SLAVE_SPSS_CFG, + SDM845_SLAVE_TCSR, + SDM845_SLAVE_TLMM_NORTH, + SDM845_SLAVE_TLMM_SOUTH, + SDM845_SLAVE_TSIF, + SDM845_SLAVE_UFS_CARD_CFG, + SDM845_SLAVE_UFS_MEM_CFG, + SDM845_SLAVE_USB3_0, + SDM845_SLAVE_USB3_1, + SDM845_SLAVE_VENUS_CFG, + SDM845_SLAVE_VSENSE_CTRL_CFG, + SDM845_SLAVE_CNOC_A2NOC, + SDM845_SLAVE_SERVICE_CNOC, + SDM845_SLAVE_LLCC_CFG, + SDM845_SLAVE_MEM_NOC_CFG, + SDM845_SLAVE_GNOC_SNOC, + SDM845_SLAVE_GNOC_MEM_NOC, + SDM845_SLAVE_SERVICE_GNOC, + SDM845_SLAVE_EBI1, + SDM845_SLAVE_MSS_PROC_MS_MPU_CFG, + SDM845_SLAVE_MEM_NOC_GNOC, + SDM845_SLAVE_LLCC, + SDM845_SLAVE_MEM_NOC_SNOC, + SDM845_SLAVE_SERVICE_MEM_NOC, + SDM845_SLAVE_MNOC_SF_MEM_NOC, + SDM845_SLAVE_MNOC_HF_MEM_NOC, + SDM845_SLAVE_SERVICE_MNOC, + SDM845_SLAVE_APPSS, + SDM845_SLAVE_SNOC_CNOC, + SDM845_SLAVE_SNOC_MEM_NOC_GC, + SDM845_SLAVE_SNOC_MEM_NOC_SF, + SDM845_SLAVE_IMEM, + SDM845_SLAVE_PCIE_0, + SDM845_SLAVE_PCIE_1, + SDM845_SLAVE_PIMEM, + SDM845_SLAVE_SERVICE_SNOC, + SDM845_SLAVE_QDSS_STM, + SDM845_SLAVE_TCU }; -/** - * struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes - * known as Bus Clock Manager (BCM) - * @name: the bcm node name used to fetch BCM data from command db - * @type: latency or bandwidth bcm - * @addr: address offsets used when voting to RPMH - * @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm - * @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm - * @dirty: flag used to indicate whether the bcm needs to be committed - * @keepalive: flag used to indicate whether a keepalive is required - * @aux_data: auxiliary data used when calculating threshold values and - * communicating with RPMh - * @list: used to link to other bcms when compiling lists for commit - * @num_nodes: total number of @num_nodes - * @nodes: list of qcom_icc_nodes that this BCM encapsulates - */ -struct qcom_icc_bcm { - const char *name; - u32 type; - u32 addr; - u64 vote_x[QCOM_ICC_NUM_BUCKETS]; - u64 vote_y[QCOM_ICC_NUM_BUCKETS]; - bool dirty; - bool keepalive; - struct bcm_db aux_data; - struct list_head list; - size_t num_nodes; - struct qcom_icc_node *nodes[]; +DEFINE_QNODE(qhm_a1noc_cfg, SDM845_MASTER_A1NOC_CFG, 1, 4, SDM845_SLAVE_SERVICE_A1NOC); +DEFINE_QNODE(qhm_qup1, SDM845_MASTER_BLSP_1, 1, 4, SDM845_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(qhm_tsif, SDM845_MASTER_TSIF, 1, 4, SDM845_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(xm_sdc2, SDM845_MASTER_SDCC_2, 1, 8, SDM845_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(xm_sdc4, SDM845_MASTER_SDCC_4, 1, 8, SDM845_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(xm_ufs_card, SDM845_MASTER_UFS_CARD, 1, 8, SDM845_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(xm_ufs_mem, SDM845_MASTER_UFS_MEM, 1, 8, SDM845_SLAVE_A1NOC_SNOC); +DEFINE_QNODE(xm_pcie_0, SDM845_MASTER_PCIE_0, 1, 8, SDM845_SLAVE_ANOC_PCIE_A1NOC_SNOC); +DEFINE_QNODE(qhm_a2noc_cfg, SDM845_MASTER_A2NOC_CFG, 1, 4, SDM845_SLAVE_SERVICE_A2NOC); +DEFINE_QNODE(qhm_qdss_bam, SDM845_MASTER_QDSS_BAM, 1, 4, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(qhm_qup2, SDM845_MASTER_BLSP_2, 1, 4, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(qnm_cnoc, SDM845_MASTER_CNOC_A2NOC, 1, 8, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(qxm_crypto, SDM845_MASTER_CRYPTO, 1, 8, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(qxm_ipa, SDM845_MASTER_IPA, 1, 8, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(xm_pcie3_1, SDM845_MASTER_PCIE_1, 1, 8, SDM845_SLAVE_ANOC_PCIE_SNOC); +DEFINE_QNODE(xm_qdss_etr, SDM845_MASTER_QDSS_ETR, 1, 8, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(xm_usb3_0, SDM845_MASTER_USB3_0, 1, 8, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(xm_usb3_1, SDM845_MASTER_USB3_1, 1, 8, SDM845_SLAVE_A2NOC_SNOC); +DEFINE_QNODE(qxm_camnoc_hf0_uncomp, SDM845_MASTER_CAMNOC_HF0_UNCOMP, 1, 32, SDM845_SLAVE_CAMNOC_UNCOMP); +DEFINE_QNODE(qxm_camnoc_hf1_uncomp, SDM845_MASTER_CAMNOC_HF1_UNCOMP, 1, 32, SDM845_SLAVE_CAMNOC_UNCOMP); +DEFINE_QNODE(qxm_camnoc_sf_uncomp, SDM845_MASTER_CAMNOC_SF_UNCOMP, 1, 32, SDM845_SLAVE_CAMNOC_UNCOMP); +DEFINE_QNODE(qhm_spdm, SDM845_MASTER_SPDM, 1, 4, SDM845_SLAVE_CNOC_A2NOC); +DEFINE_QNODE(qhm_tic, SDM845_MASTER_TIC, 1, 4, SDM845_SLAVE_A1NOC_CFG, SDM845_SLAVE_A2NOC_CFG, SDM845_SLAVE_AOP, SDM845_SLAVE_AOSS, SDM845_SLAVE_CAMERA_CFG, SDM845_SLAVE_CLK_CTL, SDM845_SLAVE_CDSP_CFG, SDM845_SLAVE_RBCPR_CX_CFG, SDM845_SLAVE_CRYPTO_0_CFG, SDM845_SLAVE_DCC_CFG, SDM845_SLAVE_CNOC_DDRSS, SDM845_SLAVE_DISPLAY_CFG, SDM845_SLAVE_GLM, SDM845_SLAVE_GFX3D_CFG, SDM845_SLAVE_IMEM_CFG, SDM845_SLAVE_IPA_CFG, SDM845_SLAVE_CNOC_MNOC_CFG, SDM845_SLAVE_PCIE_0_CFG, SDM845_SLAVE_PCIE_1_CFG, SDM845_SLAVE_PDM, SDM845_SLAVE_SOUTH_PHY_CFG, SDM845_SLAVE_PIMEM_CFG, SDM845_SLAVE_PRNG, SDM845_SLAVE_QDSS_CFG, SDM845_SLAVE_BLSP_2, SDM845_SLAVE_BLSP_1, SDM845_SLAVE_SDCC_2, SDM845_SLAVE_SDCC_4, SDM845_SLAVE_SNOC_CFG, SDM845_SLAVE_SPDM_WRAPPER, SDM845_SLAVE_SPSS_CFG, SDM845_SLAVE_TCSR, SDM845_SLAVE_TLMM_NORTH, SDM845_SLAVE_TLMM_SOUTH, SDM845_SLAVE_TSIF, SDM845_SLAVE_UFS_CARD_CFG, SDM845_SLAVE_UFS_MEM_CFG, SDM845_SLAVE_USB3_0, SDM845_SLAVE_USB3_1, SDM845_SLAVE_VENUS_CFG, SDM845_SLAVE_VSENSE_CTRL_CFG, SDM845_SLAVE_CNOC_A2NOC, SDM845_SLAVE_SERVICE_CNOC); +DEFINE_QNODE(qnm_snoc, SDM845_MASTER_SNOC_CNOC, 1, 8, SDM845_SLAVE_A1NOC_CFG, SDM845_SLAVE_A2NOC_CFG, SDM845_SLAVE_AOP, SDM845_SLAVE_AOSS, SDM845_SLAVE_CAMERA_CFG, SDM845_SLAVE_CLK_CTL, SDM845_SLAVE_CDSP_CFG, SDM845_SLAVE_RBCPR_CX_CFG, SDM845_SLAVE_CRYPTO_0_CFG, SDM845_SLAVE_DCC_CFG, SDM845_SLAVE_CNOC_DDRSS, SDM845_SLAVE_DISPLAY_CFG, SDM845_SLAVE_GLM, SDM845_SLAVE_GFX3D_CFG, SDM845_SLAVE_IMEM_CFG, SDM845_SLAVE_IPA_CFG, SDM845_SLAVE_CNOC_MNOC_CFG, SDM845_SLAVE_PCIE_0_CFG, SDM845_SLAVE_PCIE_1_CFG, SDM845_SLAVE_PDM, SDM845_SLAVE_SOUTH_PHY_CFG, SDM845_SLAVE_PIMEM_CFG, SDM845_SLAVE_PRNG, SDM845_SLAVE_QDSS_CFG, SDM845_SLAVE_BLSP_2, SDM845_SLAVE_BLSP_1, SDM845_SLAVE_SDCC_2, SDM845_SLAVE_SDCC_4, SDM845_SLAVE_SNOC_CFG, SDM845_SLAVE_SPDM_WRAPPER, SDM845_SLAVE_SPSS_CFG, SDM845_SLAVE_TCSR, SDM845_SLAVE_TLMM_NORTH, SDM845_SLAVE_TLMM_SOUTH, SDM845_SLAVE_TSIF, SDM845_SLAVE_UFS_CARD_CFG, SDM845_SLAVE_UFS_MEM_CFG, SDM845_SLAVE_USB3_0, SDM845_SLAVE_USB3_1, SDM845_SLAVE_VENUS_CFG, SDM845_SLAVE_VSENSE_CTRL_CFG, SDM845_SLAVE_SERVICE_CNOC); +DEFINE_QNODE(xm_qdss_dap, SDM845_MASTER_QDSS_DAP, 1, 8, SDM845_SLAVE_A1NOC_CFG, SDM845_SLAVE_A2NOC_CFG, SDM845_SLAVE_AOP, SDM845_SLAVE_AOSS, SDM845_SLAVE_CAMERA_CFG, SDM845_SLAVE_CLK_CTL, SDM845_SLAVE_CDSP_CFG, SDM845_SLAVE_RBCPR_CX_CFG, SDM845_SLAVE_CRYPTO_0_CFG, SDM845_SLAVE_DCC_CFG, SDM845_SLAVE_CNOC_DDRSS, SDM845_SLAVE_DISPLAY_CFG, SDM845_SLAVE_GLM, SDM845_SLAVE_GFX3D_CFG, SDM845_SLAVE_IMEM_CFG, SDM845_SLAVE_IPA_CFG, SDM845_SLAVE_CNOC_MNOC_CFG, SDM845_SLAVE_PCIE_0_CFG, SDM845_SLAVE_PCIE_1_CFG, SDM845_SLAVE_PDM, SDM845_SLAVE_SOUTH_PHY_CFG, SDM845_SLAVE_PIMEM_CFG, SDM845_SLAVE_PRNG, SDM845_SLAVE_QDSS_CFG, SDM845_SLAVE_BLSP_2, SDM845_SLAVE_BLSP_1, SDM845_SLAVE_SDCC_2, SDM845_SLAVE_SDCC_4, SDM845_SLAVE_SNOC_CFG, SDM845_SLAVE_SPDM_WRAPPER, SDM845_SLAVE_SPSS_CFG, SDM845_SLAVE_TCSR, SDM845_SLAVE_TLMM_NORTH, SDM845_SLAVE_TLMM_SOUTH, SDM845_SLAVE_TSIF, SDM845_SLAVE_UFS_CARD_CFG, SDM845_SLAVE_UFS_MEM_CFG, SDM845_SLAVE_USB3_0, SDM845_SLAVE_USB3_1, SDM845_SLAVE_VENUS_CFG, SDM845_SLAVE_VSENSE_CTRL_CFG, SDM845_SLAVE_CNOC_A2NOC, SDM845_SLAVE_SERVICE_CNOC); +DEFINE_QNODE(qhm_cnoc, SDM845_MASTER_CNOC_DC_NOC, 1, 4, SDM845_SLAVE_LLCC_CFG, SDM845_SLAVE_MEM_NOC_CFG); +DEFINE_QNODE(acm_l3, SDM845_MASTER_APPSS_PROC, 1, 16, SDM845_SLAVE_GNOC_SNOC, SDM845_SLAVE_GNOC_MEM_NOC, SDM845_SLAVE_SERVICE_GNOC); +DEFINE_QNODE(pm_gnoc_cfg, SDM845_MASTER_GNOC_CFG, 1, 4, SDM845_SLAVE_SERVICE_GNOC); +DEFINE_QNODE(llcc_mc, SDM845_MASTER_LLCC, 4, 4, SDM845_SLAVE_EBI1); +DEFINE_QNODE(acm_tcu, SDM845_MASTER_TCU_0, 1, 8, SDM845_SLAVE_MEM_NOC_GNOC, SDM845_SLAVE_LLCC, SDM845_SLAVE_MEM_NOC_SNOC); +DEFINE_QNODE(qhm_memnoc_cfg, SDM845_MASTER_MEM_NOC_CFG, 1, 4, SDM845_SLAVE_MSS_PROC_MS_MPU_CFG, SDM845_SLAVE_SERVICE_MEM_NOC); +DEFINE_QNODE(qnm_apps, SDM845_MASTER_GNOC_MEM_NOC, 2, 32, SDM845_SLAVE_LLCC); +DEFINE_QNODE(qnm_mnoc_hf, SDM845_MASTER_MNOC_HF_MEM_NOC, 2, 32, SDM845_SLAVE_MEM_NOC_GNOC, SDM845_SLAVE_LLCC); +DEFINE_QNODE(qnm_mnoc_sf, SDM845_MASTER_MNOC_SF_MEM_NOC, 1, 32, SDM845_SLAVE_MEM_NOC_GNOC, SDM845_SLAVE_LLCC, SDM845_SLAVE_MEM_NOC_SNOC); +DEFINE_QNODE(qnm_snoc_gc, SDM845_MASTER_SNOC_GC_MEM_NOC, 1, 8, SDM845_SLAVE_LLCC); +DEFINE_QNODE(qnm_snoc_sf, SDM845_MASTER_SNOC_SF_MEM_NOC, 1, 16, SDM845_SLAVE_MEM_NOC_GNOC, SDM845_SLAVE_LLCC); +DEFINE_QNODE(qxm_gpu, SDM845_MASTER_GFX3D, 2, 32, SDM845_SLAVE_MEM_NOC_GNOC, SDM845_SLAVE_LLCC, SDM845_SLAVE_MEM_NOC_SNOC); +DEFINE_QNODE(qhm_mnoc_cfg, SDM845_MASTER_CNOC_MNOC_CFG, 1, 4, SDM845_SLAVE_SERVICE_MNOC); +DEFINE_QNODE(qxm_camnoc_hf0, SDM845_MASTER_CAMNOC_HF0, 1, 32, SDM845_SLAVE_MNOC_HF_MEM_NOC); +DEFINE_QNODE(qxm_camnoc_hf1, SDM845_MASTER_CAMNOC_HF1, 1, 32, SDM845_SLAVE_MNOC_HF_MEM_NOC); +DEFINE_QNODE(qxm_camnoc_sf, SDM845_MASTER_CAMNOC_SF, 1, 32, SDM845_SLAVE_MNOC_SF_MEM_NOC); +DEFINE_QNODE(qxm_mdp0, SDM845_MASTER_MDP0, 1, 32, SDM845_SLAVE_MNOC_HF_MEM_NOC); +DEFINE_QNODE(qxm_mdp1, SDM845_MASTER_MDP1, 1, 32, SDM845_SLAVE_MNOC_HF_MEM_NOC); +DEFINE_QNODE(qxm_rot, SDM845_MASTER_ROTATOR, 1, 32, SDM845_SLAVE_MNOC_SF_MEM_NOC); +DEFINE_QNODE(qxm_venus0, SDM845_MASTER_VIDEO_P0, 1, 32, SDM845_SLAVE_MNOC_SF_MEM_NOC); +DEFINE_QNODE(qxm_venus1, SDM845_MASTER_VIDEO_P1, 1, 32, SDM845_SLAVE_MNOC_SF_MEM_NOC); +DEFINE_QNODE(qxm_venus_arm9, SDM845_MASTER_VIDEO_PROC, 1, 8, SDM845_SLAVE_MNOC_SF_MEM_NOC); +DEFINE_QNODE(qhm_snoc_cfg, SDM845_MASTER_SNOC_CFG, 1, 4, SDM845_SLAVE_SERVICE_SNOC); +DEFINE_QNODE(qnm_aggre1_noc, SDM845_MASTER_A1NOC_SNOC, 1, 16, SDM845_SLAVE_APPSS, SDM845_SLAVE_SNOC_CNOC, SDM845_SLAVE_SNOC_MEM_NOC_SF, SDM845_SLAVE_IMEM, SDM845_SLAVE_PIMEM, SDM845_SLAVE_QDSS_STM); +DEFINE_QNODE(qnm_aggre2_noc, SDM845_MASTER_A2NOC_SNOC, 1, 16, SDM845_SLAVE_APPSS, SDM845_SLAVE_SNOC_CNOC, SDM845_SLAVE_SNOC_MEM_NOC_SF, SDM845_SLAVE_IMEM, SDM845_SLAVE_PCIE_0, SDM845_SLAVE_PCIE_1, SDM845_SLAVE_PIMEM, SDM845_SLAVE_QDSS_STM, SDM845_SLAVE_TCU); +DEFINE_QNODE(qnm_gladiator_sodv, SDM845_MASTER_GNOC_SNOC, 1, 8, SDM845_SLAVE_APPSS, SDM845_SLAVE_SNOC_CNOC, SDM845_SLAVE_IMEM, SDM845_SLAVE_PCIE_0, SDM845_SLAVE_PCIE_1, SDM845_SLAVE_PIMEM, SDM845_SLAVE_QDSS_STM, SDM845_SLAVE_TCU); +DEFINE_QNODE(qnm_memnoc, SDM845_MASTER_MEM_NOC_SNOC, 1, 8, SDM845_SLAVE_APPSS, SDM845_SLAVE_SNOC_CNOC, SDM845_SLAVE_IMEM, SDM845_SLAVE_PIMEM, SDM845_SLAVE_QDSS_STM); +DEFINE_QNODE(qnm_pcie_anoc, SDM845_MASTER_ANOC_PCIE_SNOC, 1, 16, SDM845_SLAVE_APPSS, SDM845_SLAVE_SNOC_CNOC, SDM845_SLAVE_SNOC_MEM_NOC_SF, SDM845_SLAVE_IMEM, SDM845_SLAVE_QDSS_STM); +DEFINE_QNODE(qxm_pimem, SDM845_MASTER_PIMEM, 1, 8, SDM845_SLAVE_SNOC_MEM_NOC_GC, SDM845_SLAVE_IMEM); +DEFINE_QNODE(xm_gic, SDM845_MASTER_GIC, 1, 8, SDM845_SLAVE_SNOC_MEM_NOC_GC, SDM845_SLAVE_IMEM); +DEFINE_QNODE(qns_a1noc_snoc, SDM845_SLAVE_A1NOC_SNOC, 1, 16, SDM845_MASTER_A1NOC_SNOC); +DEFINE_QNODE(srvc_aggre1_noc, SDM845_SLAVE_SERVICE_A1NOC, 1, 4, 0); +DEFINE_QNODE(qns_pcie_a1noc_snoc, SDM845_SLAVE_ANOC_PCIE_A1NOC_SNOC, 1, 16, SDM845_MASTER_ANOC_PCIE_SNOC); +DEFINE_QNODE(qns_a2noc_snoc, SDM845_SLAVE_A2NOC_SNOC, 1, 16, SDM845_MASTER_A2NOC_SNOC); +DEFINE_QNODE(qns_pcie_snoc, SDM845_SLAVE_ANOC_PCIE_SNOC, 1, 16, SDM845_MASTER_ANOC_PCIE_SNOC); +DEFINE_QNODE(srvc_aggre2_noc, SDM845_SLAVE_SERVICE_A2NOC, 1, 4); +DEFINE_QNODE(qns_camnoc_uncomp, SDM845_SLAVE_CAMNOC_UNCOMP, 1, 32); +DEFINE_QNODE(qhs_a1_noc_cfg, SDM845_SLAVE_A1NOC_CFG, 1, 4, SDM845_MASTER_A1NOC_CFG); +DEFINE_QNODE(qhs_a2_noc_cfg, SDM845_SLAVE_A2NOC_CFG, 1, 4, SDM845_MASTER_A2NOC_CFG); +DEFINE_QNODE(qhs_aop, SDM845_SLAVE_AOP, 1, 4); +DEFINE_QNODE(qhs_aoss, SDM845_SLAVE_AOSS, 1, 4); +DEFINE_QNODE(qhs_camera_cfg, SDM845_SLAVE_CAMERA_CFG, 1, 4); +DEFINE_QNODE(qhs_clk_ctl, SDM845_SLAVE_CLK_CTL, 1, 4); +DEFINE_QNODE(qhs_compute_dsp_cfg, SDM845_SLAVE_CDSP_CFG, 1, 4); +DEFINE_QNODE(qhs_cpr_cx, SDM845_SLAVE_RBCPR_CX_CFG, 1, 4); +DEFINE_QNODE(qhs_crypto0_cfg, SDM845_SLAVE_CRYPTO_0_CFG, 1, 4); +DEFINE_QNODE(qhs_dcc_cfg, SDM845_SLAVE_DCC_CFG, 1, 4, SDM845_MASTER_CNOC_DC_NOC); +DEFINE_QNODE(qhs_ddrss_cfg, SDM845_SLAVE_CNOC_DDRSS, 1, 4); +DEFINE_QNODE(qhs_display_cfg, SDM845_SLAVE_DISPLAY_CFG, 1, 4); +DEFINE_QNODE(qhs_glm, SDM845_SLAVE_GLM, 1, 4); +DEFINE_QNODE(qhs_gpuss_cfg, SDM845_SLAVE_GFX3D_CFG, 1, 8); +DEFINE_QNODE(qhs_imem_cfg, SDM845_SLAVE_IMEM_CFG, 1, 4); +DEFINE_QNODE(qhs_ipa, SDM845_SLAVE_IPA_CFG, 1, 4); +DEFINE_QNODE(qhs_mnoc_cfg, SDM845_SLAVE_CNOC_MNOC_CFG, 1, 4, SDM845_MASTER_CNOC_MNOC_CFG); +DEFINE_QNODE(qhs_pcie0_cfg, SDM845_SLAVE_PCIE_0_CFG, 1, 4); +DEFINE_QNODE(qhs_pcie_gen3_cfg, SDM845_SLAVE_PCIE_1_CFG, 1, 4); +DEFINE_QNODE(qhs_pdm, SDM845_SLAVE_PDM, 1, 4); +DEFINE_QNODE(qhs_phy_refgen_south, SDM845_SLAVE_SOUTH_PHY_CFG, 1, 4); +DEFINE_QNODE(qhs_pimem_cfg, SDM845_SLAVE_PIMEM_CFG, 1, 4); +DEFINE_QNODE(qhs_prng, SDM845_SLAVE_PRNG, 1, 4); +DEFINE_QNODE(qhs_qdss_cfg, SDM845_SLAVE_QDSS_CFG, 1, 4); +DEFINE_QNODE(qhs_qupv3_north, SDM845_SLAVE_BLSP_2, 1, 4); +DEFINE_QNODE(qhs_qupv3_south, SDM845_SLAVE_BLSP_1, 1, 4); +DEFINE_QNODE(qhs_sdc2, SDM845_SLAVE_SDCC_2, 1, 4); +DEFINE_QNODE(qhs_sdc4, SDM845_SLAVE_SDCC_4, 1, 4); +DEFINE_QNODE(qhs_snoc_cfg, SDM845_SLAVE_SNOC_CFG, 1, 4, SDM845_MASTER_SNOC_CFG); +DEFINE_QNODE(qhs_spdm, SDM845_SLAVE_SPDM_WRAPPER, 1, 4); +DEFINE_QNODE(qhs_spss_cfg, SDM845_SLAVE_SPSS_CFG, 1, 4); +DEFINE_QNODE(qhs_tcsr, SDM845_SLAVE_TCSR, 1, 4); +DEFINE_QNODE(qhs_tlmm_north, SDM845_SLAVE_TLMM_NORTH, 1, 4); +DEFINE_QNODE(qhs_tlmm_south, SDM845_SLAVE_TLMM_SOUTH, 1, 4); +DEFINE_QNODE(qhs_tsif, SDM845_SLAVE_TSIF, 1, 4); +DEFINE_QNODE(qhs_ufs_card_cfg, SDM845_SLAVE_UFS_CARD_CFG, 1, 4); +DEFINE_QNODE(qhs_ufs_mem_cfg, SDM845_SLAVE_UFS_MEM_CFG, 1, 4); +DEFINE_QNODE(qhs_usb3_0, SDM845_SLAVE_USB3_0, 1, 4); +DEFINE_QNODE(qhs_usb3_1, SDM845_SLAVE_USB3_1, 1, 4); +DEFINE_QNODE(qhs_venus_cfg, SDM845_SLAVE_VENUS_CFG, 1, 4); +DEFINE_QNODE(qhs_vsense_ctrl_cfg, SDM845_SLAVE_VSENSE_CTRL_CFG, 1, 4); +DEFINE_QNODE(qns_cnoc_a2noc, SDM845_SLAVE_CNOC_A2NOC, 1, 8, SDM845_MASTER_CNOC_A2NOC); +DEFINE_QNODE(srvc_cnoc, SDM845_SLAVE_SERVICE_CNOC, 1, 4); +DEFINE_QNODE(qhs_llcc, SDM845_SLAVE_LLCC_CFG, 1, 4); +DEFINE_QNODE(qhs_memnoc, SDM845_SLAVE_MEM_NOC_CFG, 1, 4, SDM845_MASTER_MEM_NOC_CFG); +DEFINE_QNODE(qns_gladiator_sodv, SDM845_SLAVE_GNOC_SNOC, 1, 8, SDM845_MASTER_GNOC_SNOC); +DEFINE_QNODE(qns_gnoc_memnoc, SDM845_SLAVE_GNOC_MEM_NOC, 2, 32, SDM845_MASTER_GNOC_MEM_NOC); +DEFINE_QNODE(srvc_gnoc, SDM845_SLAVE_SERVICE_GNOC, 1, 4); +DEFINE_QNODE(ebi, SDM845_SLAVE_EBI1, 4, 4); +DEFINE_QNODE(qhs_mdsp_ms_mpu_cfg, SDM845_SLAVE_MSS_PROC_MS_MPU_CFG, 1, 4); +DEFINE_QNODE(qns_apps_io, SDM845_SLAVE_MEM_NOC_GNOC, 1, 32); +DEFINE_QNODE(qns_llcc, SDM845_SLAVE_LLCC, 4, 16, SDM845_MASTER_LLCC); +DEFINE_QNODE(qns_memnoc_snoc, SDM845_SLAVE_MEM_NOC_SNOC, 1, 8, SDM845_MASTER_MEM_NOC_SNOC); +DEFINE_QNODE(srvc_memnoc, SDM845_SLAVE_SERVICE_MEM_NOC, 1, 4); +DEFINE_QNODE(qns2_mem_noc, SDM845_SLAVE_MNOC_SF_MEM_NOC, 1, 32, SDM845_MASTER_MNOC_SF_MEM_NOC); +DEFINE_QNODE(qns_mem_noc_hf, SDM845_SLAVE_MNOC_HF_MEM_NOC, 2, 32, SDM845_MASTER_MNOC_HF_MEM_NOC); +DEFINE_QNODE(srvc_mnoc, SDM845_SLAVE_SERVICE_MNOC, 1, 4); +DEFINE_QNODE(qhs_apss, SDM845_SLAVE_APPSS, 1, 8); +DEFINE_QNODE(qns_cnoc, SDM845_SLAVE_SNOC_CNOC, 1, 8, SDM845_MASTER_SNOC_CNOC); +DEFINE_QNODE(qns_memnoc_gc, SDM845_SLAVE_SNOC_MEM_NOC_GC, 1, 8, SDM845_MASTER_SNOC_GC_MEM_NOC); +DEFINE_QNODE(qns_memnoc_sf, SDM845_SLAVE_SNOC_MEM_NOC_SF, 1, 16, SDM845_MASTER_SNOC_SF_MEM_NOC); +DEFINE_QNODE(qxs_imem, SDM845_SLAVE_IMEM, 1, 8); +DEFINE_QNODE(qxs_pcie, SDM845_SLAVE_PCIE_0, 1, 8); +DEFINE_QNODE(qxs_pcie_gen3, SDM845_SLAVE_PCIE_1, 1, 8); +DEFINE_QNODE(qxs_pimem, SDM845_SLAVE_PIMEM, 1, 8); +DEFINE_QNODE(srvc_snoc, SDM845_SLAVE_SERVICE_SNOC, 1, 4); +DEFINE_QNODE(xs_qdss_stm, SDM845_SLAVE_QDSS_STM, 1, 4); +DEFINE_QNODE(xs_sys_tcu_cfg, SDM845_SLAVE_TCU, 1, 8); + +DEFINE_QBCM(bcm_acv, "ACV", false, &ebi); +DEFINE_QBCM(bcm_mc0, "MC0", true, &ebi); +DEFINE_QBCM(bcm_sh0, "SH0", true, &qns_llcc); +DEFINE_QBCM(bcm_mm0, "MM0", false, &qns_mem_noc_hf); +DEFINE_QBCM(bcm_sh1, "SH1", false, &qns_apps_io); +DEFINE_QBCM(bcm_mm1, "MM1", false, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1); +DEFINE_QBCM(bcm_sh2, "SH2", false, &qns_memnoc_snoc); +DEFINE_QBCM(bcm_mm2, "MM2", false, &qns2_mem_noc); +DEFINE_QBCM(bcm_sh3, "SH3", false, &acm_tcu); +DEFINE_QBCM(bcm_mm3, "MM3", false, &qxm_camnoc_sf, &qxm_rot, &qxm_venus0, &qxm_venus1, &qxm_venus_arm9); +DEFINE_QBCM(bcm_sh5, "SH5", false, &qnm_apps); +DEFINE_QBCM(bcm_sn0, "SN0", true, &qns_memnoc_sf); +DEFINE_QBCM(bcm_ce0, "CE0", false, &qxm_crypto); +DEFINE_QBCM(bcm_cn0, "CN0", false, &qhm_spdm, &qhm_tic, &qnm_snoc, &xm_qdss_dap, &qhs_a1_noc_cfg, &qhs_a2_noc_cfg, &qhs_aop, &qhs_aoss, &qhs_camera_cfg, &qhs_clk_ctl, &qhs_compute_dsp_cfg, &qhs_cpr_cx, &qhs_crypto0_cfg, &qhs_dcc_cfg, &qhs_ddrss_cfg, &qhs_display_cfg, &qhs_glm, &qhs_gpuss_cfg, &qhs_imem_cfg, &qhs_ipa, &qhs_mnoc_cfg, &qhs_pcie0_cfg, &qhs_pcie_gen3_cfg, &qhs_pdm, &qhs_phy_refgen_south, &qhs_pimem_cfg, &qhs_prng, &qhs_qdss_cfg, &qhs_qupv3_north, &qhs_qupv3_south, &qhs_sdc2, &qhs_sdc4, &qhs_snoc_cfg, &qhs_spdm, &qhs_spss_cfg, &qhs_tcsr, &qhs_tlmm_north, &qhs_tlmm_south, &qhs_tsif, &qhs_ufs_card_cfg, &qhs_ufs_mem_cfg, &qhs_usb3_0, &qhs_usb3_1, &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, &qns_cnoc_a2noc, &srvc_cnoc); +DEFINE_QBCM(bcm_qup0, "QUP0", false, &qhm_qup1, &qhm_qup2); +DEFINE_QBCM(bcm_sn1, "SN1", false, &qxs_imem); +DEFINE_QBCM(bcm_sn2, "SN2", false, &qns_memnoc_gc); +DEFINE_QBCM(bcm_sn3, "SN3", false, &qns_cnoc); +DEFINE_QBCM(bcm_sn4, "SN4", false, &qxm_pimem); +DEFINE_QBCM(bcm_sn5, "SN5", false, &xs_qdss_stm); +DEFINE_QBCM(bcm_sn6, "SN6", false, &qhs_apss, &srvc_snoc, &xs_sys_tcu_cfg); +DEFINE_QBCM(bcm_sn7, "SN7", false, &qxs_pcie); +DEFINE_QBCM(bcm_sn8, "SN8", false, &qxs_pcie_gen3); +DEFINE_QBCM(bcm_sn9, "SN9", false, &srvc_aggre1_noc, &qnm_aggre1_noc); +DEFINE_QBCM(bcm_sn11, "SN11", false, &srvc_aggre2_noc, &qnm_aggre2_noc); +DEFINE_QBCM(bcm_sn12, "SN12", false, &qnm_gladiator_sodv, &xm_gic); +DEFINE_QBCM(bcm_sn14, "SN14", false, &qnm_pcie_anoc); +DEFINE_QBCM(bcm_sn15, "SN15", false, &qnm_memnoc); + +static struct qcom_icc_bcm *aggre1_noc_bcms[] = { + &bcm_sn9, }; -struct qcom_icc_fabric { - struct qcom_icc_node **nodes; - size_t num_nodes; +static struct qcom_icc_node *aggre1_noc_nodes[] = { + [MASTER_A1NOC_CFG] = &qhm_a1noc_cfg, + [MASTER_TSIF] = &qhm_tsif, + [MASTER_SDCC_2] = &xm_sdc2, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_CARD] = &xm_ufs_card, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_PCIE_0] = &xm_pcie_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, + [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, + [SLAVE_ANOC_PCIE_A1NOC_SNOC] = &qns_pcie_a1noc_snoc, }; -struct qcom_icc_desc { - struct qcom_icc_node **nodes; - size_t num_nodes; - struct qcom_icc_bcm **bcms; - size_t num_bcms; +const static struct qcom_icc_desc sdm845_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), }; -#define DEFINE_QNODE(_name, _id, _channels, _buswidth, \ - _numlinks, ...) \ - static struct qcom_icc_node _name = { \ - .id = _id, \ - .name = #_name, \ - .channels = _channels, \ - .buswidth = _buswidth, \ - .num_links = _numlinks, \ - .links = { __VA_ARGS__ }, \ - } - -DEFINE_QNODE(qhm_a1noc_cfg, MASTER_A1NOC_CFG, 1, 4, 1, SLAVE_SERVICE_A1NOC); -DEFINE_QNODE(qhm_qup1, MASTER_BLSP_1, 1, 4, 1, SLAVE_A1NOC_SNOC); -DEFINE_QNODE(qhm_tsif, MASTER_TSIF, 1, 4, 1, SLAVE_A1NOC_SNOC); -DEFINE_QNODE(xm_sdc2, MASTER_SDCC_2, 1, 8, 1, SLAVE_A1NOC_SNOC); -DEFINE_QNODE(xm_sdc4, MASTER_SDCC_4, 1, 8, 1, SLAVE_A1NOC_SNOC); -DEFINE_QNODE(xm_ufs_card, MASTER_UFS_CARD, 1, 8, 1, SLAVE_A1NOC_SNOC); -DEFINE_QNODE(xm_ufs_mem, MASTER_UFS_MEM, 1, 8, 1, SLAVE_A1NOC_SNOC); -DEFINE_QNODE(xm_pcie_0, MASTER_PCIE_0, 1, 8, 1, SLAVE_ANOC_PCIE_A1NOC_SNOC); -DEFINE_QNODE(qhm_a2noc_cfg, MASTER_A2NOC_CFG, 1, 4, 1, SLAVE_SERVICE_A2NOC); -DEFINE_QNODE(qhm_qdss_bam, MASTER_QDSS_BAM, 1, 4, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(qhm_qup2, MASTER_BLSP_2, 1, 4, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(qnm_cnoc, MASTER_CNOC_A2NOC, 1, 8, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(qxm_crypto, MASTER_CRYPTO, 1, 8, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(qxm_ipa, MASTER_IPA, 1, 8, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(xm_pcie3_1, MASTER_PCIE_1, 1, 8, 1, SLAVE_ANOC_PCIE_SNOC); -DEFINE_QNODE(xm_qdss_etr, MASTER_QDSS_ETR, 1, 8, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(xm_usb3_0, MASTER_USB3_0, 1, 8, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(xm_usb3_1, MASTER_USB3_1, 1, 8, 1, SLAVE_A2NOC_SNOC); -DEFINE_QNODE(qxm_camnoc_hf0_uncomp, MASTER_CAMNOC_HF0_UNCOMP, 1, 32, 1, SLAVE_CAMNOC_UNCOMP); -DEFINE_QNODE(qxm_camnoc_hf1_uncomp, MASTER_CAMNOC_HF1_UNCOMP, 1, 32, 1, SLAVE_CAMNOC_UNCOMP); -DEFINE_QNODE(qxm_camnoc_sf_uncomp, MASTER_CAMNOC_SF_UNCOMP, 1, 32, 1, SLAVE_CAMNOC_UNCOMP); -DEFINE_QNODE(qhm_spdm, MASTER_SPDM, 1, 4, 1, SLAVE_CNOC_A2NOC); -DEFINE_QNODE(qhm_tic, MASTER_TIC, 1, 4, 43, SLAVE_A1NOC_CFG, SLAVE_A2NOC_CFG, SLAVE_AOP, SLAVE_AOSS, SLAVE_CAMERA_CFG, SLAVE_CLK_CTL, SLAVE_CDSP_CFG, SLAVE_RBCPR_CX_CFG, SLAVE_CRYPTO_0_CFG, SLAVE_DCC_CFG, SLAVE_CNOC_DDRSS, SLAVE_DISPLAY_CFG, SLAVE_GLM, SLAVE_GFX3D_CFG, SLAVE_IMEM_CFG, SLAVE_IPA_CFG, SLAVE_CNOC_MNOC_CFG, SLAVE_PCIE_0_CFG, SLAVE_PCIE_1_CFG, SLAVE_PDM, SLAVE_SOUTH_PHY_CFG, SLAVE_PIMEM_CFG, SLAVE_PRNG, SLAVE_QDSS_CFG, SLAVE_BLSP_2, SLAVE_BLSP_1, SLAVE_SDCC_2, SLAVE_SDCC_4, SLAVE_SNOC_CFG, SLAVE_SPDM_WRAPPER, SLAVE_SPSS_CFG, SLAVE_TCSR, SLAVE_TLMM_NORTH, SLAVE_TLMM_SOUTH, SLAVE_TSIF, SLAVE_UFS_CARD_CFG, SLAVE_UFS_MEM_CFG, SLAVE_USB3_0, SLAVE_USB3_1, SLAVE_VENUS_CFG, SLAVE_VSENSE_CTRL_CFG, SLAVE_CNOC_A2NOC, SLAVE_SERVICE_CNOC); -DEFINE_QNODE(qnm_snoc, MASTER_SNOC_CNOC, 1, 8, 42, SLAVE_A1NOC_CFG, SLAVE_A2NOC_CFG, SLAVE_AOP, SLAVE_AOSS, SLAVE_CAMERA_CFG, SLAVE_CLK_CTL, SLAVE_CDSP_CFG, SLAVE_RBCPR_CX_CFG, SLAVE_CRYPTO_0_CFG, SLAVE_DCC_CFG, SLAVE_CNOC_DDRSS, SLAVE_DISPLAY_CFG, SLAVE_GLM, SLAVE_GFX3D_CFG, SLAVE_IMEM_CFG, SLAVE_IPA_CFG, SLAVE_CNOC_MNOC_CFG, SLAVE_PCIE_0_CFG, SLAVE_PCIE_1_CFG, SLAVE_PDM, SLAVE_SOUTH_PHY_CFG, SLAVE_PIMEM_CFG, SLAVE_PRNG, SLAVE_QDSS_CFG, SLAVE_BLSP_2, SLAVE_BLSP_1, SLAVE_SDCC_2, SLAVE_SDCC_4, SLAVE_SNOC_CFG, SLAVE_SPDM_WRAPPER, SLAVE_SPSS_CFG, SLAVE_TCSR, SLAVE_TLMM_NORTH, SLAVE_TLMM_SOUTH, SLAVE_TSIF, SLAVE_UFS_CARD_CFG, SLAVE_UFS_MEM_CFG, SLAVE_USB3_0, SLAVE_USB3_1, SLAVE_VENUS_CFG, SLAVE_VSENSE_CTRL_CFG, SLAVE_SERVICE_CNOC); -DEFINE_QNODE(xm_qdss_dap, MASTER_QDSS_DAP, 1, 8, 43, SLAVE_A1NOC_CFG, SLAVE_A2NOC_CFG, SLAVE_AOP, SLAVE_AOSS, SLAVE_CAMERA_CFG, SLAVE_CLK_CTL, SLAVE_CDSP_CFG, SLAVE_RBCPR_CX_CFG, SLAVE_CRYPTO_0_CFG, SLAVE_DCC_CFG, SLAVE_CNOC_DDRSS, SLAVE_DISPLAY_CFG, SLAVE_GLM, SLAVE_GFX3D_CFG, SLAVE_IMEM_CFG, SLAVE_IPA_CFG, SLAVE_CNOC_MNOC_CFG, SLAVE_PCIE_0_CFG, SLAVE_PCIE_1_CFG, SLAVE_PDM, SLAVE_SOUTH_PHY_CFG, SLAVE_PIMEM_CFG, SLAVE_PRNG, SLAVE_QDSS_CFG, SLAVE_BLSP_2, SLAVE_BLSP_1, SLAVE_SDCC_2, SLAVE_SDCC_4, SLAVE_SNOC_CFG, SLAVE_SPDM_WRAPPER, SLAVE_SPSS_CFG, SLAVE_TCSR, SLAVE_TLMM_NORTH, SLAVE_TLMM_SOUTH, SLAVE_TSIF, SLAVE_UFS_CARD_CFG, SLAVE_UFS_MEM_CFG, SLAVE_USB3_0, SLAVE_USB3_1, SLAVE_VENUS_CFG, SLAVE_VSENSE_CTRL_CFG, SLAVE_CNOC_A2NOC, SLAVE_SERVICE_CNOC); -DEFINE_QNODE(qhm_cnoc, MASTER_CNOC_DC_NOC, 1, 4, 2, SLAVE_LLCC_CFG, SLAVE_MEM_NOC_CFG); -DEFINE_QNODE(acm_l3, MASTER_APPSS_PROC, 1, 16, 3, SLAVE_GNOC_SNOC, SLAVE_GNOC_MEM_NOC, SLAVE_SERVICE_GNOC); -DEFINE_QNODE(pm_gnoc_cfg, MASTER_GNOC_CFG, 1, 4, 1, SLAVE_SERVICE_GNOC); -DEFINE_QNODE(llcc_mc, MASTER_LLCC, 4, 4, 1, SLAVE_EBI1); -DEFINE_QNODE(acm_tcu, MASTER_TCU_0, 1, 8, 3, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC, SLAVE_MEM_NOC_SNOC); -DEFINE_QNODE(qhm_memnoc_cfg, MASTER_MEM_NOC_CFG, 1, 4, 2, SLAVE_MSS_PROC_MS_MPU_CFG, SLAVE_SERVICE_MEM_NOC); -DEFINE_QNODE(qnm_apps, MASTER_GNOC_MEM_NOC, 2, 32, 1, SLAVE_LLCC); -DEFINE_QNODE(qnm_mnoc_hf, MASTER_MNOC_HF_MEM_NOC, 2, 32, 2, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC); -DEFINE_QNODE(qnm_mnoc_sf, MASTER_MNOC_SF_MEM_NOC, 1, 32, 3, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC, SLAVE_MEM_NOC_SNOC); -DEFINE_QNODE(qnm_snoc_gc, MASTER_SNOC_GC_MEM_NOC, 1, 8, 1, SLAVE_LLCC); -DEFINE_QNODE(qnm_snoc_sf, MASTER_SNOC_SF_MEM_NOC, 1, 16, 2, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC); -DEFINE_QNODE(qxm_gpu, MASTER_GFX3D, 2, 32, 3, SLAVE_MEM_NOC_GNOC, SLAVE_LLCC, SLAVE_MEM_NOC_SNOC); -DEFINE_QNODE(qhm_mnoc_cfg, MASTER_CNOC_MNOC_CFG, 1, 4, 1, SLAVE_SERVICE_MNOC); -DEFINE_QNODE(qxm_camnoc_hf0, MASTER_CAMNOC_HF0, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC); -DEFINE_QNODE(qxm_camnoc_hf1, MASTER_CAMNOC_HF1, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC); -DEFINE_QNODE(qxm_camnoc_sf, MASTER_CAMNOC_SF, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC); -DEFINE_QNODE(qxm_mdp0, MASTER_MDP0, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC); -DEFINE_QNODE(qxm_mdp1, MASTER_MDP1, 1, 32, 1, SLAVE_MNOC_HF_MEM_NOC); -DEFINE_QNODE(qxm_rot, MASTER_ROTATOR, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC); -DEFINE_QNODE(qxm_venus0, MASTER_VIDEO_P0, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC); -DEFINE_QNODE(qxm_venus1, MASTER_VIDEO_P1, 1, 32, 1, SLAVE_MNOC_SF_MEM_NOC); -DEFINE_QNODE(qxm_venus_arm9, MASTER_VIDEO_PROC, 1, 8, 1, SLAVE_MNOC_SF_MEM_NOC); -DEFINE_QNODE(qhm_snoc_cfg, MASTER_SNOC_CFG, 1, 4, 1, SLAVE_SERVICE_SNOC); -DEFINE_QNODE(qnm_aggre1_noc, MASTER_A1NOC_SNOC, 1, 16, 6, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_SNOC_MEM_NOC_SF, SLAVE_IMEM, SLAVE_PIMEM, SLAVE_QDSS_STM); -DEFINE_QNODE(qnm_aggre2_noc, MASTER_A2NOC_SNOC, 1, 16, 9, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_SNOC_MEM_NOC_SF, SLAVE_IMEM, SLAVE_PCIE_0, SLAVE_PCIE_1, SLAVE_PIMEM, SLAVE_QDSS_STM, SLAVE_TCU); -DEFINE_QNODE(qnm_gladiator_sodv, MASTER_GNOC_SNOC, 1, 8, 8, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_IMEM, SLAVE_PCIE_0, SLAVE_PCIE_1, SLAVE_PIMEM, SLAVE_QDSS_STM, SLAVE_TCU); -DEFINE_QNODE(qnm_memnoc, MASTER_MEM_NOC_SNOC, 1, 8, 5, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_IMEM, SLAVE_PIMEM, SLAVE_QDSS_STM); -DEFINE_QNODE(qnm_pcie_anoc, MASTER_ANOC_PCIE_SNOC, 1, 16, 5, SLAVE_APPSS, SLAVE_SNOC_CNOC, SLAVE_SNOC_MEM_NOC_SF, SLAVE_IMEM, SLAVE_QDSS_STM); -DEFINE_QNODE(qxm_pimem, MASTER_PIMEM, 1, 8, 2, SLAVE_SNOC_MEM_NOC_GC, SLAVE_IMEM); -DEFINE_QNODE(xm_gic, MASTER_GIC, 1, 8, 2, SLAVE_SNOC_MEM_NOC_GC, SLAVE_IMEM); -DEFINE_QNODE(qns_a1noc_snoc, SLAVE_A1NOC_SNOC, 1, 16, 1, MASTER_A1NOC_SNOC); -DEFINE_QNODE(srvc_aggre1_noc, SLAVE_SERVICE_A1NOC, 1, 4, 0); -DEFINE_QNODE(qns_pcie_a1noc_snoc, SLAVE_ANOC_PCIE_A1NOC_SNOC, 1, 16, 1, MASTER_ANOC_PCIE_SNOC); -DEFINE_QNODE(qns_a2noc_snoc, SLAVE_A2NOC_SNOC, 1, 16, 1, MASTER_A2NOC_SNOC); -DEFINE_QNODE(qns_pcie_snoc, SLAVE_ANOC_PCIE_SNOC, 1, 16, 1, MASTER_ANOC_PCIE_SNOC); -DEFINE_QNODE(srvc_aggre2_noc, SLAVE_SERVICE_A2NOC, 1, 4, 0); -DEFINE_QNODE(qns_camnoc_uncomp, SLAVE_CAMNOC_UNCOMP, 1, 32, 0); -DEFINE_QNODE(qhs_a1_noc_cfg, SLAVE_A1NOC_CFG, 1, 4, 1, MASTER_A1NOC_CFG); -DEFINE_QNODE(qhs_a2_noc_cfg, SLAVE_A2NOC_CFG, 1, 4, 1, MASTER_A2NOC_CFG); -DEFINE_QNODE(qhs_aop, SLAVE_AOP, 1, 4, 0); -DEFINE_QNODE(qhs_aoss, SLAVE_AOSS, 1, 4, 0); -DEFINE_QNODE(qhs_camera_cfg, SLAVE_CAMERA_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_clk_ctl, SLAVE_CLK_CTL, 1, 4, 0); -DEFINE_QNODE(qhs_compute_dsp_cfg, SLAVE_CDSP_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_cpr_cx, SLAVE_RBCPR_CX_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_crypto0_cfg, SLAVE_CRYPTO_0_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_dcc_cfg, SLAVE_DCC_CFG, 1, 4, 1, MASTER_CNOC_DC_NOC); -DEFINE_QNODE(qhs_ddrss_cfg, SLAVE_CNOC_DDRSS, 1, 4, 0); -DEFINE_QNODE(qhs_display_cfg, SLAVE_DISPLAY_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_glm, SLAVE_GLM, 1, 4, 0); -DEFINE_QNODE(qhs_gpuss_cfg, SLAVE_GFX3D_CFG, 1, 8, 0); -DEFINE_QNODE(qhs_imem_cfg, SLAVE_IMEM_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_ipa, SLAVE_IPA_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_mnoc_cfg, SLAVE_CNOC_MNOC_CFG, 1, 4, 1, MASTER_CNOC_MNOC_CFG); -DEFINE_QNODE(qhs_pcie0_cfg, SLAVE_PCIE_0_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_pcie_gen3_cfg, SLAVE_PCIE_1_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_pdm, SLAVE_PDM, 1, 4, 0); -DEFINE_QNODE(qhs_phy_refgen_south, SLAVE_SOUTH_PHY_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_pimem_cfg, SLAVE_PIMEM_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_prng, SLAVE_PRNG, 1, 4, 0); -DEFINE_QNODE(qhs_qdss_cfg, SLAVE_QDSS_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_qupv3_north, SLAVE_BLSP_2, 1, 4, 0); -DEFINE_QNODE(qhs_qupv3_south, SLAVE_BLSP_1, 1, 4, 0); -DEFINE_QNODE(qhs_sdc2, SLAVE_SDCC_2, 1, 4, 0); -DEFINE_QNODE(qhs_sdc4, SLAVE_SDCC_4, 1, 4, 0); -DEFINE_QNODE(qhs_snoc_cfg, SLAVE_SNOC_CFG, 1, 4, 1, MASTER_SNOC_CFG); -DEFINE_QNODE(qhs_spdm, SLAVE_SPDM_WRAPPER, 1, 4, 0); -DEFINE_QNODE(qhs_spss_cfg, SLAVE_SPSS_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_tcsr, SLAVE_TCSR, 1, 4, 0); -DEFINE_QNODE(qhs_tlmm_north, SLAVE_TLMM_NORTH, 1, 4, 0); -DEFINE_QNODE(qhs_tlmm_south, SLAVE_TLMM_SOUTH, 1, 4, 0); -DEFINE_QNODE(qhs_tsif, SLAVE_TSIF, 1, 4, 0); -DEFINE_QNODE(qhs_ufs_card_cfg, SLAVE_UFS_CARD_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_ufs_mem_cfg, SLAVE_UFS_MEM_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_usb3_0, SLAVE_USB3_0, 1, 4, 0); -DEFINE_QNODE(qhs_usb3_1, SLAVE_USB3_1, 1, 4, 0); -DEFINE_QNODE(qhs_venus_cfg, SLAVE_VENUS_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_vsense_ctrl_cfg, SLAVE_VSENSE_CTRL_CFG, 1, 4, 0); -DEFINE_QNODE(qns_cnoc_a2noc, SLAVE_CNOC_A2NOC, 1, 8, 1, MASTER_CNOC_A2NOC); -DEFINE_QNODE(srvc_cnoc, SLAVE_SERVICE_CNOC, 1, 4, 0); -DEFINE_QNODE(qhs_llcc, SLAVE_LLCC_CFG, 1, 4, 0); -DEFINE_QNODE(qhs_memnoc, SLAVE_MEM_NOC_CFG, 1, 4, 1, MASTER_MEM_NOC_CFG); -DEFINE_QNODE(qns_gladiator_sodv, SLAVE_GNOC_SNOC, 1, 8, 1, MASTER_GNOC_SNOC); -DEFINE_QNODE(qns_gnoc_memnoc, SLAVE_GNOC_MEM_NOC, 2, 32, 1, MASTER_GNOC_MEM_NOC); -DEFINE_QNODE(srvc_gnoc, SLAVE_SERVICE_GNOC, 1, 4, 0); -DEFINE_QNODE(ebi, SLAVE_EBI1, 4, 4, 0); -DEFINE_QNODE(qhs_mdsp_ms_mpu_cfg, SLAVE_MSS_PROC_MS_MPU_CFG, 1, 4, 0); -DEFINE_QNODE(qns_apps_io, SLAVE_MEM_NOC_GNOC, 1, 32, 0); -DEFINE_QNODE(qns_llcc, SLAVE_LLCC, 4, 16, 1, MASTER_LLCC); -DEFINE_QNODE(qns_memnoc_snoc, SLAVE_MEM_NOC_SNOC, 1, 8, 1, MASTER_MEM_NOC_SNOC); -DEFINE_QNODE(srvc_memnoc, SLAVE_SERVICE_MEM_NOC, 1, 4, 0); -DEFINE_QNODE(qns2_mem_noc, SLAVE_MNOC_SF_MEM_NOC, 1, 32, 1, MASTER_MNOC_SF_MEM_NOC); -DEFINE_QNODE(qns_mem_noc_hf, SLAVE_MNOC_HF_MEM_NOC, 2, 32, 1, MASTER_MNOC_HF_MEM_NOC); -DEFINE_QNODE(srvc_mnoc, SLAVE_SERVICE_MNOC, 1, 4, 0); -DEFINE_QNODE(qhs_apss, SLAVE_APPSS, 1, 8, 0); -DEFINE_QNODE(qns_cnoc, SLAVE_SNOC_CNOC, 1, 8, 1, MASTER_SNOC_CNOC); -DEFINE_QNODE(qns_memnoc_gc, SLAVE_SNOC_MEM_NOC_GC, 1, 8, 1, MASTER_SNOC_GC_MEM_NOC); -DEFINE_QNODE(qns_memnoc_sf, SLAVE_SNOC_MEM_NOC_SF, 1, 16, 1, MASTER_SNOC_SF_MEM_NOC); -DEFINE_QNODE(qxs_imem, SLAVE_IMEM, 1, 8, 0); -DEFINE_QNODE(qxs_pcie, SLAVE_PCIE_0, 1, 8, 0); -DEFINE_QNODE(qxs_pcie_gen3, SLAVE_PCIE_1, 1, 8, 0); -DEFINE_QNODE(qxs_pimem, SLAVE_PIMEM, 1, 8, 0); -DEFINE_QNODE(srvc_snoc, SLAVE_SERVICE_SNOC, 1, 4, 0); -DEFINE_QNODE(xs_qdss_stm, SLAVE_QDSS_STM, 1, 4, 0); -DEFINE_QNODE(xs_sys_tcu_cfg, SLAVE_TCU, 1, 8, 0); - -#define DEFINE_QBCM(_name, _bcmname, _keepalive, _numnodes, ...) \ - static struct qcom_icc_bcm _name = { \ - .name = _bcmname, \ - .keepalive = _keepalive, \ - .num_nodes = _numnodes, \ - .nodes = { __VA_ARGS__ }, \ - } +static struct qcom_icc_bcm *aggre2_noc_bcms[] = { + &bcm_ce0, + &bcm_sn11, + &bcm_qup0, +}; -DEFINE_QBCM(bcm_acv, "ACV", false, 1, &ebi); -DEFINE_QBCM(bcm_mc0, "MC0", true, 1, &ebi); -DEFINE_QBCM(bcm_sh0, "SH0", true, 1, &qns_llcc); -DEFINE_QBCM(bcm_mm0, "MM0", false, 1, &qns_mem_noc_hf); -DEFINE_QBCM(bcm_sh1, "SH1", false, 1, &qns_apps_io); -DEFINE_QBCM(bcm_mm1, "MM1", false, 7, &qxm_camnoc_hf0_uncomp, &qxm_camnoc_hf1_uncomp, &qxm_camnoc_sf_uncomp, &qxm_camnoc_hf0, &qxm_camnoc_hf1, &qxm_mdp0, &qxm_mdp1); -DEFINE_QBCM(bcm_sh2, "SH2", false, 1, &qns_memnoc_snoc); -DEFINE_QBCM(bcm_mm2, "MM2", false, 1, &qns2_mem_noc); -DEFINE_QBCM(bcm_sh3, "SH3", false, 1, &acm_tcu); -DEFINE_QBCM(bcm_mm3, "MM3", false, 5, &qxm_camnoc_sf, &qxm_rot, &qxm_venus0, &qxm_venus1, &qxm_venus_arm9); -DEFINE_QBCM(bcm_sh5, "SH5", false, 1, &qnm_apps); -DEFINE_QBCM(bcm_sn0, "SN0", true, 1, &qns_memnoc_sf); -DEFINE_QBCM(bcm_ce0, "CE0", false, 1, &qxm_crypto); -DEFINE_QBCM(bcm_cn0, "CN0", false, 47, &qhm_spdm, &qhm_tic, &qnm_snoc, &xm_qdss_dap, &qhs_a1_noc_cfg, &qhs_a2_noc_cfg, &qhs_aop, &qhs_aoss, &qhs_camera_cfg, &qhs_clk_ctl, &qhs_compute_dsp_cfg, &qhs_cpr_cx, &qhs_crypto0_cfg, &qhs_dcc_cfg, &qhs_ddrss_cfg, &qhs_display_cfg, &qhs_glm, &qhs_gpuss_cfg, &qhs_imem_cfg, &qhs_ipa, &qhs_mnoc_cfg, &qhs_pcie0_cfg, &qhs_pcie_gen3_cfg, &qhs_pdm, &qhs_phy_refgen_south, &qhs_pimem_cfg, &qhs_prng, &qhs_qdss_cfg, &qhs_qupv3_north, &qhs_qupv3_south, &qhs_sdc2, &qhs_sdc4, &qhs_snoc_cfg, &qhs_spdm, &qhs_spss_cfg, &qhs_tcsr, &qhs_tlmm_north, &qhs_tlmm_south, &qhs_tsif, &qhs_ufs_card_cfg, &qhs_ufs_mem_cfg, &qhs_usb3_0, &qhs_usb3_1, &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, &qns_cnoc_a2noc, &srvc_cnoc); -DEFINE_QBCM(bcm_qup0, "QUP0", false, 2, &qhm_qup1, &qhm_qup2); -DEFINE_QBCM(bcm_sn1, "SN1", false, 1, &qxs_imem); -DEFINE_QBCM(bcm_sn2, "SN2", false, 1, &qns_memnoc_gc); -DEFINE_QBCM(bcm_sn3, "SN3", false, 1, &qns_cnoc); -DEFINE_QBCM(bcm_sn4, "SN4", false, 1, &qxm_pimem); -DEFINE_QBCM(bcm_sn5, "SN5", false, 1, &xs_qdss_stm); -DEFINE_QBCM(bcm_sn6, "SN6", false, 3, &qhs_apss, &srvc_snoc, &xs_sys_tcu_cfg); -DEFINE_QBCM(bcm_sn7, "SN7", false, 1, &qxs_pcie); -DEFINE_QBCM(bcm_sn8, "SN8", false, 1, &qxs_pcie_gen3); -DEFINE_QBCM(bcm_sn9, "SN9", false, 2, &srvc_aggre1_noc, &qnm_aggre1_noc); -DEFINE_QBCM(bcm_sn11, "SN11", false, 2, &srvc_aggre2_noc, &qnm_aggre2_noc); -DEFINE_QBCM(bcm_sn12, "SN12", false, 2, &qnm_gladiator_sodv, &xm_gic); -DEFINE_QBCM(bcm_sn14, "SN14", false, 1, &qnm_pcie_anoc); -DEFINE_QBCM(bcm_sn15, "SN15", false, 1, &qnm_memnoc); - -static struct qcom_icc_node *rsc_hlos_nodes[] = { - [MASTER_APPSS_PROC] = &acm_l3, - [MASTER_TCU_0] = &acm_tcu, - [MASTER_LLCC] = &llcc_mc, - [MASTER_GNOC_CFG] = &pm_gnoc_cfg, - [MASTER_A1NOC_CFG] = &qhm_a1noc_cfg, +static struct qcom_icc_node *aggre2_noc_nodes[] = { [MASTER_A2NOC_CFG] = &qhm_a2noc_cfg, - [MASTER_CNOC_DC_NOC] = &qhm_cnoc, - [MASTER_MEM_NOC_CFG] = &qhm_memnoc_cfg, - [MASTER_CNOC_MNOC_CFG] = &qhm_mnoc_cfg, [MASTER_QDSS_BAM] = &qhm_qdss_bam, - [MASTER_BLSP_1] = &qhm_qup1, - [MASTER_BLSP_2] = &qhm_qup2, - [MASTER_SNOC_CFG] = &qhm_snoc_cfg, - [MASTER_SPDM] = &qhm_spdm, - [MASTER_TIC] = &qhm_tic, - [MASTER_TSIF] = &qhm_tsif, - [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, - [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, - [MASTER_GNOC_MEM_NOC] = &qnm_apps, [MASTER_CNOC_A2NOC] = &qnm_cnoc, - [MASTER_GNOC_SNOC] = &qnm_gladiator_sodv, - [MASTER_MEM_NOC_SNOC] = &qnm_memnoc, - [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, - [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, - [MASTER_ANOC_PCIE_SNOC] = &qnm_pcie_anoc, - [MASTER_SNOC_CNOC] = &qnm_snoc, - [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, - [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, - [MASTER_CAMNOC_HF0] = &qxm_camnoc_hf0, - [MASTER_CAMNOC_HF0_UNCOMP] = &qxm_camnoc_hf0_uncomp, - [MASTER_CAMNOC_HF1] = &qxm_camnoc_hf1, - [MASTER_CAMNOC_HF1_UNCOMP] = &qxm_camnoc_hf1_uncomp, - [MASTER_CAMNOC_SF] = &qxm_camnoc_sf, - [MASTER_CAMNOC_SF_UNCOMP] = &qxm_camnoc_sf_uncomp, [MASTER_CRYPTO] = &qxm_crypto, - [MASTER_GFX3D] = &qxm_gpu, [MASTER_IPA] = &qxm_ipa, - [MASTER_MDP0] = &qxm_mdp0, - [MASTER_MDP1] = &qxm_mdp1, - [MASTER_PIMEM] = &qxm_pimem, - [MASTER_ROTATOR] = &qxm_rot, - [MASTER_VIDEO_P0] = &qxm_venus0, - [MASTER_VIDEO_P1] = &qxm_venus1, - [MASTER_VIDEO_PROC] = &qxm_venus_arm9, - [MASTER_GIC] = &xm_gic, [MASTER_PCIE_1] = &xm_pcie3_1, - [MASTER_PCIE_0] = &xm_pcie_0, - [MASTER_QDSS_DAP] = &xm_qdss_dap, [MASTER_QDSS_ETR] = &xm_qdss_etr, - [MASTER_SDCC_2] = &xm_sdc2, - [MASTER_SDCC_4] = &xm_sdc4, - [MASTER_UFS_CARD] = &xm_ufs_card, - [MASTER_UFS_MEM] = &xm_ufs_mem, [MASTER_USB3_0] = &xm_usb3_0, [MASTER_USB3_1] = &xm_usb3_1, - [SLAVE_EBI1] = &ebi, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, + [SLAVE_ANOC_PCIE_SNOC] = &qns_pcie_snoc, + [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, +}; + +const static struct qcom_icc_desc sdm845_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), +}; + +static struct qcom_icc_bcm *config_noc_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node *config_noc_nodes[] = { + [MASTER_SPDM] = &qhm_spdm, + [MASTER_TIC] = &qhm_tic, + [MASTER_SNOC_CNOC] = &qnm_snoc, + [MASTER_QDSS_DAP] = &xm_qdss_dap, [SLAVE_A1NOC_CFG] = &qhs_a1_noc_cfg, [SLAVE_A2NOC_CFG] = &qhs_a2_noc_cfg, [SLAVE_AOP] = &qhs_aop, [SLAVE_AOSS] = &qhs_aoss, - [SLAVE_APPSS] = &qhs_apss, [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, [SLAVE_CLK_CTL] = &qhs_clk_ctl, [SLAVE_CDSP_CFG] = &qhs_compute_dsp_cfg, @@ -386,9 +384,6 @@ static struct qcom_icc_node *rsc_hlos_nodes[] = { [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, [SLAVE_IMEM_CFG] = &qhs_imem_cfg, [SLAVE_IPA_CFG] = &qhs_ipa, - [SLAVE_LLCC_CFG] = &qhs_llcc, - [SLAVE_MSS_PROC_MS_MPU_CFG] = &qhs_mdsp_ms_mpu_cfg, - [SLAVE_MEM_NOC_CFG] = &qhs_memnoc, [SLAVE_CNOC_MNOC_CFG] = &qhs_mnoc_cfg, [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, [SLAVE_PCIE_1_CFG] = &qhs_pcie_gen3_cfg, @@ -414,53 +409,122 @@ static struct qcom_icc_node *rsc_hlos_nodes[] = { [SLAVE_USB3_1] = &qhs_usb3_1, [SLAVE_VENUS_CFG] = &qhs_venus_cfg, [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, - [SLAVE_MNOC_SF_MEM_NOC] = &qns2_mem_noc, - [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, - [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, - [SLAVE_MEM_NOC_GNOC] = &qns_apps_io, - [SLAVE_CAMNOC_UNCOMP] = &qns_camnoc_uncomp, - [SLAVE_SNOC_CNOC] = &qns_cnoc, [SLAVE_CNOC_A2NOC] = &qns_cnoc_a2noc, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc, +}; + +const static struct qcom_icc_desc sdm845_config_noc = { + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .bcms = config_noc_bcms, + .num_bcms = ARRAY_SIZE(config_noc_bcms), +}; + +static struct qcom_icc_bcm *dc_noc_bcms[] = { +}; + +static struct qcom_icc_node *dc_noc_nodes[] = { + [MASTER_CNOC_DC_NOC] = &qhm_cnoc, + [SLAVE_LLCC_CFG] = &qhs_llcc, + [SLAVE_MEM_NOC_CFG] = &qhs_memnoc, +}; + +const static struct qcom_icc_desc sdm845_dc_noc = { + .nodes = dc_noc_nodes, + .num_nodes = ARRAY_SIZE(dc_noc_nodes), + .bcms = dc_noc_bcms, + .num_bcms = ARRAY_SIZE(dc_noc_bcms), +}; + +static struct qcom_icc_bcm *gladiator_noc_bcms[] = { +}; + +static struct qcom_icc_node *gladiator_noc_nodes[] = { + [MASTER_APPSS_PROC] = &acm_l3, + [MASTER_GNOC_CFG] = &pm_gnoc_cfg, [SLAVE_GNOC_SNOC] = &qns_gladiator_sodv, [SLAVE_GNOC_MEM_NOC] = &qns_gnoc_memnoc, - [SLAVE_LLCC] = &qns_llcc, - [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, - [SLAVE_SNOC_MEM_NOC_GC] = &qns_memnoc_gc, - [SLAVE_SNOC_MEM_NOC_SF] = &qns_memnoc_sf, - [SLAVE_MEM_NOC_SNOC] = &qns_memnoc_snoc, - [SLAVE_ANOC_PCIE_A1NOC_SNOC] = &qns_pcie_a1noc_snoc, - [SLAVE_ANOC_PCIE_SNOC] = &qns_pcie_snoc, - [SLAVE_IMEM] = &qxs_imem, - [SLAVE_PCIE_0] = &qxs_pcie, - [SLAVE_PCIE_1] = &qxs_pcie_gen3, - [SLAVE_PIMEM] = &qxs_pimem, - [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, - [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, - [SLAVE_SERVICE_CNOC] = &srvc_cnoc, [SLAVE_SERVICE_GNOC] = &srvc_gnoc, - [SLAVE_SERVICE_MEM_NOC] = &srvc_memnoc, - [SLAVE_SERVICE_MNOC] = &srvc_mnoc, - [SLAVE_SERVICE_SNOC] = &srvc_snoc, - [SLAVE_QDSS_STM] = &xs_qdss_stm, - [SLAVE_TCU] = &xs_sys_tcu_cfg, }; -static struct qcom_icc_bcm *rsc_hlos_bcms[] = { - &bcm_acv, +const static struct qcom_icc_desc sdm845_gladiator_noc = { + .nodes = gladiator_noc_nodes, + .num_nodes = ARRAY_SIZE(gladiator_noc_nodes), + .bcms = gladiator_noc_bcms, + .num_bcms = ARRAY_SIZE(gladiator_noc_bcms), +}; + +static struct qcom_icc_bcm *mem_noc_bcms[] = { &bcm_mc0, + &bcm_acv, &bcm_sh0, - &bcm_mm0, &bcm_sh1, - &bcm_mm1, &bcm_sh2, - &bcm_mm2, &bcm_sh3, - &bcm_mm3, &bcm_sh5, +}; + +static struct qcom_icc_node *mem_noc_nodes[] = { + [MASTER_TCU_0] = &acm_tcu, + [MASTER_MEM_NOC_CFG] = &qhm_memnoc_cfg, + [MASTER_GNOC_MEM_NOC] = &qnm_apps, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [MASTER_GFX3D] = &qxm_gpu, + [SLAVE_MSS_PROC_MS_MPU_CFG] = &qhs_mdsp_ms_mpu_cfg, + [SLAVE_MEM_NOC_GNOC] = &qns_apps_io, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_SNOC] = &qns_memnoc_snoc, + [SLAVE_SERVICE_MEM_NOC] = &srvc_memnoc, + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, +}; + +const static struct qcom_icc_desc sdm845_mem_noc = { + .nodes = mem_noc_nodes, + .num_nodes = ARRAY_SIZE(mem_noc_nodes), + .bcms = mem_noc_bcms, + .num_bcms = ARRAY_SIZE(mem_noc_bcms), +}; + +static struct qcom_icc_bcm *mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, + &bcm_mm2, + &bcm_mm3, +}; + +static struct qcom_icc_node *mmss_noc_nodes[] = { + [MASTER_CNOC_MNOC_CFG] = &qhm_mnoc_cfg, + [MASTER_CAMNOC_HF0] = &qxm_camnoc_hf0, + [MASTER_CAMNOC_HF1] = &qxm_camnoc_hf1, + [MASTER_CAMNOC_SF] = &qxm_camnoc_sf, + [MASTER_MDP0] = &qxm_mdp0, + [MASTER_MDP1] = &qxm_mdp1, + [MASTER_ROTATOR] = &qxm_rot, + [MASTER_VIDEO_P0] = &qxm_venus0, + [MASTER_VIDEO_P1] = &qxm_venus1, + [MASTER_VIDEO_PROC] = &qxm_venus_arm9, + [SLAVE_MNOC_SF_MEM_NOC] = &qns2_mem_noc, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_SERVICE_MNOC] = &srvc_mnoc, + [MASTER_CAMNOC_HF0_UNCOMP] = &qxm_camnoc_hf0_uncomp, + [MASTER_CAMNOC_HF1_UNCOMP] = &qxm_camnoc_hf1_uncomp, + [MASTER_CAMNOC_SF_UNCOMP] = &qxm_camnoc_sf_uncomp, + [SLAVE_CAMNOC_UNCOMP] = &qns_camnoc_uncomp, +}; + +const static struct qcom_icc_desc sdm845_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm *system_noc_bcms[] = { &bcm_sn0, - &bcm_ce0, - &bcm_cn0, - &bcm_qup0, &bcm_sn1, &bcm_sn2, &bcm_sn3, @@ -476,297 +540,34 @@ static struct qcom_icc_bcm *rsc_hlos_bcms[] = { &bcm_sn15, }; -static struct qcom_icc_desc sdm845_rsc_hlos = { - .nodes = rsc_hlos_nodes, - .num_nodes = ARRAY_SIZE(rsc_hlos_nodes), - .bcms = rsc_hlos_bcms, - .num_bcms = ARRAY_SIZE(rsc_hlos_bcms), +static struct qcom_icc_node *system_noc_nodes[] = { + [MASTER_SNOC_CFG] = &qhm_snoc_cfg, + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_GNOC_SNOC] = &qnm_gladiator_sodv, + [MASTER_MEM_NOC_SNOC] = &qnm_memnoc, + [MASTER_ANOC_PCIE_SNOC] = &qnm_pcie_anoc, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [SLAVE_APPSS] = &qhs_apss, + [SLAVE_SNOC_CNOC] = &qns_cnoc, + [SLAVE_SNOC_MEM_NOC_GC] = &qns_memnoc_gc, + [SLAVE_SNOC_MEM_NOC_SF] = &qns_memnoc_sf, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PCIE_0] = &qxs_pcie, + [SLAVE_PCIE_1] = &qxs_pcie_gen3, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, }; -static int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) -{ - struct qcom_icc_node *qn; - const struct bcm_db *data; - size_t data_count; - int i; - - bcm->addr = cmd_db_read_addr(bcm->name); - if (!bcm->addr) { - dev_err(dev, "%s could not find RPMh address\n", - bcm->name); - return -EINVAL; - } - - data = cmd_db_read_aux_data(bcm->name, &data_count); - if (IS_ERR(data)) { - dev_err(dev, "%s command db read error (%ld)\n", - bcm->name, PTR_ERR(data)); - return PTR_ERR(data); - } - if (!data_count) { - dev_err(dev, "%s command db missing or partial aux data\n", - bcm->name); - return -EINVAL; - } - - bcm->aux_data.unit = le32_to_cpu(data->unit); - bcm->aux_data.width = le16_to_cpu(data->width); - bcm->aux_data.vcd = data->vcd; - bcm->aux_data.reserved = data->reserved; - - /* - * Link Qnodes to their respective BCMs - */ - for (i = 0; i < bcm->num_nodes; i++) { - qn = bcm->nodes[i]; - qn->bcms[qn->num_bcms] = bcm; - qn->num_bcms++; - } - - return 0; -} - -inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y, - u32 addr, bool commit) -{ - bool valid = true; - - if (!cmd) - return; - - if (vote_x == 0 && vote_y == 0) - valid = false; - - if (vote_x > BCM_TCS_CMD_VOTE_MASK) - vote_x = BCM_TCS_CMD_VOTE_MASK; - - if (vote_y > BCM_TCS_CMD_VOTE_MASK) - vote_y = BCM_TCS_CMD_VOTE_MASK; - - cmd->addr = addr; - cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y); - - /* - * Set the wait for completion flag on command that need to be completed - * before the next command. - */ - if (commit) - cmd->wait = true; -} - -static void tcs_list_gen(struct list_head *bcm_list, int bucket, - struct tcs_cmd tcs_list[SDM845_MAX_VCD], - int n[SDM845_MAX_VCD]) -{ - struct qcom_icc_bcm *bcm; - bool commit; - size_t idx = 0, batch = 0, cur_vcd_size = 0; - - memset(n, 0, sizeof(int) * SDM845_MAX_VCD); - - list_for_each_entry(bcm, bcm_list, list) { - commit = false; - cur_vcd_size++; - if ((list_is_last(&bcm->list, bcm_list)) || - bcm->aux_data.vcd != list_next_entry(bcm, list)->aux_data.vcd) { - commit = true; - cur_vcd_size = 0; - } - tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket], - bcm->vote_y[bucket], bcm->addr, commit); - idx++; - n[batch]++; - /* - * Batch the BCMs in such a way that we do not split them in - * multiple payloads when they are under the same VCD. This is - * to ensure that every BCM is committed since we only set the - * commit bit on the last BCM request of every VCD. - */ - if (n[batch] >= MAX_RPMH_PAYLOAD) { - if (!commit) { - n[batch] -= cur_vcd_size; - n[batch + 1] = cur_vcd_size; - } - batch++; - } - } -} - -static void bcm_aggregate(struct qcom_icc_bcm *bcm) -{ - size_t i, bucket; - u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0}; - u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0}; - u64 temp; - - for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) { - for (i = 0; i < bcm->num_nodes; i++) { - temp = bcm->nodes[i]->sum_avg[bucket] * bcm->aux_data.width; - do_div(temp, bcm->nodes[i]->buswidth * bcm->nodes[i]->channels); - agg_avg[bucket] = max(agg_avg[bucket], temp); - - temp = bcm->nodes[i]->max_peak[bucket] * bcm->aux_data.width; - do_div(temp, bcm->nodes[i]->buswidth); - agg_peak[bucket] = max(agg_peak[bucket], temp); - } - - temp = agg_avg[bucket] * 1000ULL; - do_div(temp, bcm->aux_data.unit); - bcm->vote_x[bucket] = temp; - - temp = agg_peak[bucket] * 1000ULL; - do_div(temp, bcm->aux_data.unit); - bcm->vote_y[bucket] = temp; - } - - if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 && - bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) { - bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1; - bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1; - bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1; - bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1; - } - - bcm->dirty = false; -} - -static void qcom_icc_pre_aggregate(struct icc_node *node) -{ - size_t i; - struct qcom_icc_node *qn; - - qn = node->data; - - for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { - qn->sum_avg[i] = 0; - qn->max_peak[i] = 0; - } -} - -static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, - u32 peak_bw, u32 *agg_avg, u32 *agg_peak) -{ - size_t i; - struct qcom_icc_node *qn; - - qn = node->data; - - if (!tag) - tag = QCOM_ICC_TAG_ALWAYS; - - for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { - if (tag & BIT(i)) { - qn->sum_avg[i] += avg_bw; - qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw); - } - } - - *agg_avg += avg_bw; - *agg_peak = max_t(u32, *agg_peak, peak_bw); - - for (i = 0; i < qn->num_bcms; i++) - qn->bcms[i]->dirty = true; - - return 0; -} - -static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) -{ - struct qcom_icc_provider *qp; - struct icc_node *node; - struct tcs_cmd cmds[SDM845_MAX_BCMS]; - struct list_head commit_list; - int commit_idx[SDM845_MAX_VCD]; - int ret = 0, i; - - if (!src) - node = dst; - else - node = src; - - qp = to_qcom_provider(node->provider); - - INIT_LIST_HEAD(&commit_list); - - for (i = 0; i < qp->num_bcms; i++) { - if (qp->bcms[i]->dirty) { - bcm_aggregate(qp->bcms[i]); - list_add_tail(&qp->bcms[i]->list, &commit_list); - } - } - - /* - * Construct the command list based on a pre ordered list of BCMs - * based on VCD. - */ - tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_AMC, cmds, commit_idx); - - if (!commit_idx[0]) - return ret; - - ret = rpmh_invalidate(qp->dev); - if (ret) { - pr_err("Error invalidating RPMH client (%d)\n", ret); - return ret; - } - - ret = rpmh_write_batch(qp->dev, RPMH_ACTIVE_ONLY_STATE, - cmds, commit_idx); - if (ret) { - pr_err("Error sending AMC RPMH requests (%d)\n", ret); - return ret; - } - - INIT_LIST_HEAD(&commit_list); - - for (i = 0; i < qp->num_bcms; i++) { - /* - * Only generate WAKE and SLEEP commands if a resource's - * requirements change as the execution environment transitions - * between different power states. - */ - if (qp->bcms[i]->vote_x[QCOM_ICC_BUCKET_WAKE] != - qp->bcms[i]->vote_x[QCOM_ICC_BUCKET_SLEEP] || - qp->bcms[i]->vote_y[QCOM_ICC_BUCKET_WAKE] != - qp->bcms[i]->vote_y[QCOM_ICC_BUCKET_SLEEP]) { - list_add_tail(&qp->bcms[i]->list, &commit_list); - } - } - - if (list_empty(&commit_list)) - return ret; - - tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_WAKE, cmds, commit_idx); - - ret = rpmh_write_batch(qp->dev, RPMH_WAKE_ONLY_STATE, cmds, commit_idx); - if (ret) { - pr_err("Error sending WAKE RPMH requests (%d)\n", ret); - return ret; - } - - tcs_list_gen(&commit_list, QCOM_ICC_BUCKET_SLEEP, cmds, commit_idx); - - ret = rpmh_write_batch(qp->dev, RPMH_SLEEP_STATE, cmds, commit_idx); - if (ret) { - pr_err("Error sending SLEEP RPMH requests (%d)\n", ret); - return ret; - } - - return ret; -} - -static int cmp_vcd(const void *_l, const void *_r) -{ - const struct qcom_icc_bcm **l = (const struct qcom_icc_bcm **)_l; - const struct qcom_icc_bcm **r = (const struct qcom_icc_bcm **)_r; - - if (l[0]->aux_data.vcd < r[0]->aux_data.vcd) - return -1; - else if (l[0]->aux_data.vcd == r[0]->aux_data.vcd) - return 0; - else - return 1; -} +const static struct qcom_icc_desc sdm845_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; static int qnoc_probe(struct platform_device *pdev) { @@ -779,7 +580,7 @@ static int qnoc_probe(struct platform_device *pdev) size_t num_nodes, i; int ret; - desc = of_device_get_match_data(&pdev->dev); + desc = device_get_match_data(&pdev->dev); if (!desc) return -EINVAL; @@ -808,6 +609,12 @@ static int qnoc_probe(struct platform_device *pdev) qp->bcms = desc->bcms; qp->num_bcms = desc->num_bcms; + qp->voter = of_bcm_voter_get(qp->dev, NULL); + if (IS_ERR(qp->voter)) { + dev_err(&pdev->dev, "bcm_voter err:%ld\n", PTR_ERR(qp->voter)); + return PTR_ERR(qp->voter); + } + ret = icc_provider_add(provider); if (ret) { dev_err(&pdev->dev, "error adding interconnect provider\n"); @@ -817,6 +624,9 @@ static int qnoc_probe(struct platform_device *pdev) for (i = 0; i < num_nodes; i++) { size_t j; + if (!qnodes[i]) + continue; + node = icc_node_create(qnodes[i]->id); if (IS_ERR(node)) { ret = PTR_ERR(node); @@ -827,10 +637,6 @@ static int qnoc_probe(struct platform_device *pdev) node->data = qnodes[i]; icc_node_add(node, provider); - dev_dbg(&pdev->dev, "registered node %p %s %d\n", node, - qnodes[i]->name, node->id); - - /* populate links */ for (j = 0; j < qnodes[i]->num_links; j++) icc_link_create(node, qnodes[i]->links[j]); @@ -841,19 +647,9 @@ static int qnoc_probe(struct platform_device *pdev) for (i = 0; i < qp->num_bcms; i++) qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - /* - * Pre sort the BCMs based on VCD for ease of generating a command list - * that groups the BCMs with the same VCD together. VCDs are numbered - * with lowest being the most expensive time wise, ensuring that - * those commands are being sent the earliest in the queue. - */ - sort(qp->bcms, qp->num_bcms, sizeof(*qp->bcms), cmp_vcd, NULL); - platform_set_drvdata(pdev, qp); - dev_dbg(&pdev->dev, "Registered SDM845 ICC\n"); - - return ret; + return 0; err: icc_nodes_remove(provider); icc_provider_del(provider); @@ -869,8 +665,23 @@ static int qnoc_remove(struct platform_device *pdev) } static const struct of_device_id qnoc_of_match[] = { - { .compatible = "qcom,sdm845-rsc-hlos", .data = &sdm845_rsc_hlos }, - { }, + { .compatible = "qcom,sdm845-aggre1-noc", + .data = &sdm845_aggre1_noc}, + { .compatible = "qcom,sdm845-aggre2-noc", + .data = &sdm845_aggre2_noc}, + { .compatible = "qcom,sdm845-config-noc", + .data = &sdm845_config_noc}, + { .compatible = "qcom,sdm845-dc-noc", + .data = &sdm845_dc_noc}, + { .compatible = "qcom,sdm845-gladiator-noc", + .data = &sdm845_gladiator_noc}, + { .compatible = "qcom,sdm845-mem-noc", + .data = &sdm845_mem_noc}, + { .compatible = "qcom,sdm845-mmss-noc", + .data = &sdm845_mmss_noc}, + { .compatible = "qcom,sdm845-system-noc", + .data = &sdm845_system_noc}, + { } }; MODULE_DEVICE_TABLE(of, qnoc_of_match); diff --git a/include/dt-bindings/interconnect/qcom,sdm845.h b/include/dt-bindings/interconnect/qcom,sdm845.h index 7b2393be7361..290be38f40e6 100644 --- a/include/dt-bindings/interconnect/qcom,sdm845.h +++ b/include/dt-bindings/interconnect/qcom,sdm845.h @@ -10,134 +10,139 @@ #define __DT_BINDINGS_INTERCONNECT_QCOM_SDM845_H #define MASTER_A1NOC_CFG 0 -#define MASTER_BLSP_1 1 -#define MASTER_TSIF 2 -#define MASTER_SDCC_2 3 -#define MASTER_SDCC_4 4 -#define MASTER_UFS_CARD 5 -#define MASTER_UFS_MEM 6 -#define MASTER_PCIE_0 7 -#define MASTER_A2NOC_CFG 8 -#define MASTER_QDSS_BAM 9 -#define MASTER_BLSP_2 10 -#define MASTER_CNOC_A2NOC 11 -#define MASTER_CRYPTO 12 -#define MASTER_IPA 13 -#define MASTER_PCIE_1 14 -#define MASTER_QDSS_ETR 15 -#define MASTER_USB3_0 16 -#define MASTER_USB3_1 17 -#define MASTER_CAMNOC_HF0_UNCOMP 18 -#define MASTER_CAMNOC_HF1_UNCOMP 19 -#define MASTER_CAMNOC_SF_UNCOMP 20 -#define MASTER_SPDM 21 -#define MASTER_TIC 22 -#define MASTER_SNOC_CNOC 23 -#define MASTER_QDSS_DAP 24 -#define MASTER_CNOC_DC_NOC 25 -#define MASTER_APPSS_PROC 26 -#define MASTER_GNOC_CFG 27 -#define MASTER_LLCC 28 -#define MASTER_TCU_0 29 -#define MASTER_MEM_NOC_CFG 30 -#define MASTER_GNOC_MEM_NOC 31 -#define MASTER_MNOC_HF_MEM_NOC 32 -#define MASTER_MNOC_SF_MEM_NOC 33 -#define MASTER_SNOC_GC_MEM_NOC 34 -#define MASTER_SNOC_SF_MEM_NOC 35 -#define MASTER_GFX3D 36 -#define MASTER_CNOC_MNOC_CFG 37 -#define MASTER_CAMNOC_HF0 38 -#define MASTER_CAMNOC_HF1 39 -#define MASTER_CAMNOC_SF 40 -#define MASTER_MDP0 41 -#define MASTER_MDP1 42 -#define MASTER_ROTATOR 43 -#define MASTER_VIDEO_P0 44 -#define MASTER_VIDEO_P1 45 -#define MASTER_VIDEO_PROC 46 -#define MASTER_SNOC_CFG 47 -#define MASTER_A1NOC_SNOC 48 -#define MASTER_A2NOC_SNOC 49 -#define MASTER_GNOC_SNOC 50 -#define MASTER_MEM_NOC_SNOC 51 -#define MASTER_ANOC_PCIE_SNOC 52 -#define MASTER_PIMEM 53 -#define MASTER_GIC 54 -#define SLAVE_A1NOC_SNOC 55 -#define SLAVE_SERVICE_A1NOC 56 -#define SLAVE_ANOC_PCIE_A1NOC_SNOC 57 -#define SLAVE_A2NOC_SNOC 58 -#define SLAVE_ANOC_PCIE_SNOC 59 -#define SLAVE_SERVICE_A2NOC 60 -#define SLAVE_CAMNOC_UNCOMP 61 -#define SLAVE_A1NOC_CFG 62 -#define SLAVE_A2NOC_CFG 63 -#define SLAVE_AOP 64 -#define SLAVE_AOSS 65 -#define SLAVE_CAMERA_CFG 66 -#define SLAVE_CLK_CTL 67 -#define SLAVE_CDSP_CFG 68 -#define SLAVE_RBCPR_CX_CFG 69 -#define SLAVE_CRYPTO_0_CFG 70 -#define SLAVE_DCC_CFG 71 -#define SLAVE_CNOC_DDRSS 72 -#define SLAVE_DISPLAY_CFG 73 -#define SLAVE_GLM 74 -#define SLAVE_GFX3D_CFG 75 -#define SLAVE_IMEM_CFG 76 -#define SLAVE_IPA_CFG 77 -#define SLAVE_CNOC_MNOC_CFG 78 -#define SLAVE_PCIE_0_CFG 79 -#define SLAVE_PCIE_1_CFG 80 -#define SLAVE_PDM 81 -#define SLAVE_SOUTH_PHY_CFG 82 -#define SLAVE_PIMEM_CFG 83 -#define SLAVE_PRNG 84 -#define SLAVE_QDSS_CFG 85 -#define SLAVE_BLSP_2 86 -#define SLAVE_BLSP_1 87 -#define SLAVE_SDCC_2 88 -#define SLAVE_SDCC_4 89 -#define SLAVE_SNOC_CFG 90 -#define SLAVE_SPDM_WRAPPER 91 -#define SLAVE_SPSS_CFG 92 -#define SLAVE_TCSR 93 -#define SLAVE_TLMM_NORTH 94 -#define SLAVE_TLMM_SOUTH 95 -#define SLAVE_TSIF 96 -#define SLAVE_UFS_CARD_CFG 97 -#define SLAVE_UFS_MEM_CFG 98 -#define SLAVE_USB3_0 99 -#define SLAVE_USB3_1 100 -#define SLAVE_VENUS_CFG 101 -#define SLAVE_VSENSE_CTRL_CFG 102 -#define SLAVE_CNOC_A2NOC 103 -#define SLAVE_SERVICE_CNOC 104 -#define SLAVE_LLCC_CFG 105 -#define SLAVE_MEM_NOC_CFG 106 -#define SLAVE_GNOC_SNOC 107 -#define SLAVE_GNOC_MEM_NOC 108 -#define SLAVE_SERVICE_GNOC 109 -#define SLAVE_EBI1 110 -#define SLAVE_MSS_PROC_MS_MPU_CFG 111 -#define SLAVE_MEM_NOC_GNOC 112 -#define SLAVE_LLCC 113 -#define SLAVE_MEM_NOC_SNOC 114 -#define SLAVE_SERVICE_MEM_NOC 115 -#define SLAVE_MNOC_SF_MEM_NOC 116 -#define SLAVE_MNOC_HF_MEM_NOC 117 -#define SLAVE_SERVICE_MNOC 118 -#define SLAVE_APPSS 119 -#define SLAVE_SNOC_CNOC 120 -#define SLAVE_SNOC_MEM_NOC_GC 121 -#define SLAVE_SNOC_MEM_NOC_SF 122 -#define SLAVE_IMEM 123 -#define SLAVE_PCIE_0 124 -#define SLAVE_PCIE_1 125 -#define SLAVE_PIMEM 126 -#define SLAVE_SERVICE_SNOC 127 -#define SLAVE_QDSS_STM 128 -#define SLAVE_TCU 129 +#define MASTER_TSIF 1 +#define MASTER_SDCC_2 2 +#define MASTER_SDCC_4 3 +#define MASTER_UFS_CARD 4 +#define MASTER_UFS_MEM 5 +#define MASTER_PCIE_0 6 +#define SLAVE_A1NOC_SNOC 7 +#define SLAVE_SERVICE_A1NOC 8 +#define SLAVE_ANOC_PCIE_A1NOC_SNOC 9 + +#define MASTER_A2NOC_CFG 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_CNOC_A2NOC 2 +#define MASTER_CRYPTO 3 +#define MASTER_IPA 4 +#define MASTER_PCIE_1 5 +#define MASTER_QDSS_ETR 6 +#define MASTER_USB3_0 7 +#define MASTER_USB3_1 8 +#define SLAVE_A2NOC_SNOC 9 +#define SLAVE_ANOC_PCIE_SNOC 10 +#define SLAVE_SERVICE_A2NOC 11 + +#define MASTER_SPDM 0 +#define MASTER_TIC 1 +#define MASTER_SNOC_CNOC 2 +#define MASTER_QDSS_DAP 3 +#define SLAVE_A1NOC_CFG 4 +#define SLAVE_A2NOC_CFG 5 +#define SLAVE_AOP 6 +#define SLAVE_AOSS 7 +#define SLAVE_CAMERA_CFG 8 +#define SLAVE_CLK_CTL 9 +#define SLAVE_CDSP_CFG 10 +#define SLAVE_RBCPR_CX_CFG 11 +#define SLAVE_CRYPTO_0_CFG 12 +#define SLAVE_DCC_CFG 13 +#define SLAVE_CNOC_DDRSS 14 +#define SLAVE_DISPLAY_CFG 15 +#define SLAVE_GLM 16 +#define SLAVE_GFX3D_CFG 17 +#define SLAVE_IMEM_CFG 18 +#define SLAVE_IPA_CFG 19 +#define SLAVE_CNOC_MNOC_CFG 20 +#define SLAVE_PCIE_0_CFG 21 +#define SLAVE_PCIE_1_CFG 22 +#define SLAVE_PDM 23 +#define SLAVE_SOUTH_PHY_CFG 24 +#define SLAVE_PIMEM_CFG 25 +#define SLAVE_PRNG 26 +#define SLAVE_QDSS_CFG 27 +#define SLAVE_BLSP_2 28 +#define SLAVE_BLSP_1 29 +#define SLAVE_SDCC_2 30 +#define SLAVE_SDCC_4 31 +#define SLAVE_SNOC_CFG 32 +#define SLAVE_SPDM_WRAPPER 33 +#define SLAVE_SPSS_CFG 34 +#define SLAVE_TCSR 35 +#define SLAVE_TLMM_NORTH 36 +#define SLAVE_TLMM_SOUTH 37 +#define SLAVE_TSIF 38 +#define SLAVE_UFS_CARD_CFG 39 +#define SLAVE_UFS_MEM_CFG 40 +#define SLAVE_USB3_0 41 +#define SLAVE_USB3_1 42 +#define SLAVE_VENUS_CFG 43 +#define SLAVE_VSENSE_CTRL_CFG 44 +#define SLAVE_CNOC_A2NOC 45 +#define SLAVE_SERVICE_CNOC 46 + +#define MASTER_CNOC_DC_NOC 0 +#define SLAVE_LLCC_CFG 1 +#define SLAVE_MEM_NOC_CFG 2 + +#define MASTER_APPSS_PROC 0 +#define MASTER_GNOC_CFG 1 +#define SLAVE_GNOC_SNOC 2 +#define SLAVE_GNOC_MEM_NOC 3 +#define SLAVE_SERVICE_GNOC 4 + +#define MASTER_TCU_0 0 +#define MASTER_MEM_NOC_CFG 1 +#define MASTER_GNOC_MEM_NOC 2 +#define MASTER_MNOC_HF_MEM_NOC 3 +#define MASTER_MNOC_SF_MEM_NOC 4 +#define MASTER_SNOC_GC_MEM_NOC 5 +#define MASTER_SNOC_SF_MEM_NOC 6 +#define MASTER_GFX3D 7 +#define SLAVE_MSS_PROC_MS_MPU_CFG 8 +#define SLAVE_MEM_NOC_GNOC 9 +#define SLAVE_LLCC 10 +#define SLAVE_MEM_NOC_SNOC 11 +#define SLAVE_SERVICE_MEM_NOC 12 +#define MASTER_LLCC 13 +#define SLAVE_EBI1 14 + +#define MASTER_CNOC_MNOC_CFG 0 +#define MASTER_CAMNOC_HF0 1 +#define MASTER_CAMNOC_HF1 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_MDP0 4 +#define MASTER_MDP1 5 +#define MASTER_ROTATOR 6 +#define MASTER_VIDEO_P0 7 +#define MASTER_VIDEO_P1 8 +#define MASTER_VIDEO_PROC 9 +#define SLAVE_MNOC_SF_MEM_NOC 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_SERVICE_MNOC 12 +#define MASTER_CAMNOC_HF0_UNCOMP 13 +#define MASTER_CAMNOC_HF1_UNCOMP 14 +#define MASTER_CAMNOC_SF_UNCOMP 15 +#define SLAVE_CAMNOC_UNCOMP 16 + +#define MASTER_SNOC_CFG 0 +#define MASTER_A1NOC_SNOC 1 +#define MASTER_A2NOC_SNOC 2 +#define MASTER_GNOC_SNOC 3 +#define MASTER_MEM_NOC_SNOC 4 +#define MASTER_ANOC_PCIE_SNOC 5 +#define MASTER_PIMEM 6 +#define MASTER_GIC 7 +#define SLAVE_APPSS 8 +#define SLAVE_SNOC_CNOC 9 +#define SLAVE_SNOC_MEM_NOC_GC 10 +#define SLAVE_SNOC_MEM_NOC_SF 11 +#define SLAVE_IMEM 12 +#define SLAVE_PCIE_0 13 +#define SLAVE_PCIE_1 14 +#define SLAVE_PIMEM 15 +#define SLAVE_SERVICE_SNOC 16 +#define SLAVE_QDSS_STM 17 +#define SLAVE_TCU 18 #endif -- cgit v1.2.3 From 59a135f6fb669f4f79f43160c7b8c8d6bfb37f75 Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Thu, 7 Nov 2019 11:42:49 +0100 Subject: tee: remove linked list of struct tee_shm Removes list_shm from struct tee_context since the linked list isn't used any longer. Signed-off-by: Jens Wiklander --- drivers/tee/tee_core.c | 1 - drivers/tee/tee_shm.c | 12 +----------- include/linux/tee_drv.h | 3 --- 3 files changed, 1 insertion(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 37d22e39fd8d..6aec502c495c 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -44,7 +44,6 @@ static struct tee_context *teedev_open(struct tee_device *teedev) kref_init(&ctx->refcount); ctx->teedev = teedev; - INIT_LIST_HEAD(&ctx->list_shm); rc = teedev->desc->ops->open(ctx); if (rc) goto err; diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 937ac5aaa6d8..99f1c890ca3d 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -17,8 +17,6 @@ static void tee_shm_release(struct tee_shm *shm) mutex_lock(&teedev->mutex); idr_remove(&teedev->idr, shm->id); - if (shm->ctx) - list_del(&shm->link); mutex_unlock(&teedev->mutex); if (shm->flags & TEE_SHM_POOL) { @@ -168,12 +166,8 @@ static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, } } - if (ctx) { + if (ctx) teedev_ctx_get(ctx); - mutex_lock(&teedev->mutex); - list_add_tail(&shm->link, &ctx->list_shm); - mutex_unlock(&teedev->mutex); - } return shm; err_rem: @@ -301,10 +295,6 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, } } - mutex_lock(&teedev->mutex); - list_add_tail(&shm->link, &ctx->list_shm); - mutex_unlock(&teedev->mutex); - return shm; err: if (shm) { diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 7a03f68fb982..cbddb883a7f8 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -49,7 +49,6 @@ struct tee_shm_pool; */ struct tee_context { struct tee_device *teedev; - struct list_head list_shm; void *data; struct kref refcount; bool releasing; @@ -170,7 +169,6 @@ void tee_device_unregister(struct tee_device *teedev); * struct tee_shm - shared memory object * @teedev: device used to allocate the object * @ctx: context using the object, if NULL the context is gone - * @link link element * @paddr: physical address of the shared memory * @kaddr: virtual address of the shared memory * @size: size of shared memory @@ -187,7 +185,6 @@ void tee_device_unregister(struct tee_device *teedev); struct tee_shm { struct tee_device *teedev; struct tee_context *ctx; - struct list_head link; phys_addr_t paddr; void *kaddr; size_t size; -- cgit v1.2.3 From c180f9bbe29a403459dd76422f435382aec6adaa Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Thu, 7 Nov 2019 11:42:52 +0100 Subject: tee: remove unused tee_shm_priv_alloc() tee_shm_priv_alloc() isn't useful in the current state and it's also not not used so remove it. Signed-off-by: Jens Wiklander --- drivers/tee/tee_shm.c | 33 ++------------------------------- include/linux/tee_drv.h | 12 ------------ 2 files changed, 2 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 99f1c890ca3d..b666854c2491 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -89,20 +89,14 @@ static const struct dma_buf_ops tee_shm_dma_buf_ops = { .mmap = tee_shm_op_mmap, }; -static struct tee_shm *__tee_shm_alloc(struct tee_context *ctx, - struct tee_device *teedev, - size_t size, u32 flags) +struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) { + struct tee_device *teedev = ctx->teedev; struct tee_shm_pool_mgr *poolm = NULL; struct tee_shm *shm; void *ret; int rc; - if (ctx && ctx->teedev != teedev) { - dev_err(teedev->dev.parent, "ctx and teedev mismatch\n"); - return ERR_PTR(-EINVAL); - } - if (!(flags & TEE_SHM_MAPPED)) { dev_err(teedev->dev.parent, "only mapped allocations supported\n"); @@ -182,31 +176,8 @@ err_dev_put: tee_device_put(teedev); return ret; } - -/** - * tee_shm_alloc() - Allocate shared memory - * @ctx: Context that allocates the shared memory - * @size: Requested size of shared memory - * @flags: Flags setting properties for the requested shared memory. - * - * Memory allocated as global shared memory is automatically freed when the - * TEE file pointer is closed. The @flags field uses the bits defined by - * TEE_SHM_* in . TEE_SHM_MAPPED must currently always be - * set. If TEE_SHM_DMA_BUF global shared memory will be allocated and - * associated with a dma-buf handle, else driver private memory. - */ -struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) -{ - return __tee_shm_alloc(ctx, ctx->teedev, size, flags); -} EXPORT_SYMBOL_GPL(tee_shm_alloc); -struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size) -{ - return __tee_shm_alloc(NULL, teedev, size, TEE_SHM_MAPPED); -} -EXPORT_SYMBOL_GPL(tee_shm_priv_alloc); - struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, size_t length, u32 flags) { diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index cbddb883a7f8..42687f6c546d 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -315,18 +315,6 @@ void *tee_get_drvdata(struct tee_device *teedev); */ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags); -/** - * tee_shm_priv_alloc() - Allocate shared memory privately - * @dev: Device that allocates the shared memory - * @size: Requested size of shared memory - * - * Allocates shared memory buffer that is not associated with any client - * context. Such buffers are owned by TEE driver and used for internal calls. - * - * @returns a pointer to 'struct tee_shm' - */ -struct tee_shm *tee_shm_priv_alloc(struct tee_device *teedev, size_t size); - /** * tee_shm_register() - Register shared memory buffer * @ctx: Context that registers the shared memory -- cgit v1.2.3 From 5271b2011e448f1be7433554e4684e91951476fa Mon Sep 17 00:00:00 2001 From: Jens Wiklander Date: Thu, 7 Nov 2019 11:42:59 +0100 Subject: tee: remove redundant teedev in struct tee_shm The ctx element in struct tee_shm is always valid. So remove the now redundant teedev element. Signed-off-by: Jens Wiklander --- drivers/tee/tee_shm.c | 7 ++----- include/linux/tee_drv.h | 4 +--- 2 files changed, 3 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/tee/tee_shm.c b/drivers/tee/tee_shm.c index 02210f179ae3..b01d2b7eea71 100644 --- a/drivers/tee/tee_shm.c +++ b/drivers/tee/tee_shm.c @@ -13,7 +13,7 @@ static void tee_shm_release(struct tee_shm *shm) { - struct tee_device *teedev = shm->teedev; + struct tee_device *teedev = shm->ctx->teedev; if (shm->flags & TEE_SHM_DMA_BUF) { mutex_lock(&teedev->mutex); @@ -44,8 +44,7 @@ static void tee_shm_release(struct tee_shm *shm) kfree(shm->pages); } - if (shm->ctx) - teedev_ctx_put(shm->ctx); + teedev_ctx_put(shm->ctx); kfree(shm); @@ -126,7 +125,6 @@ struct tee_shm *tee_shm_alloc(struct tee_context *ctx, size_t size, u32 flags) } shm->flags = flags | TEE_SHM_POOL; - shm->teedev = teedev; shm->ctx = ctx; if (flags & TEE_SHM_DMA_BUF) poolm = teedev->pool->dma_buf_mgr; @@ -215,7 +213,6 @@ struct tee_shm *tee_shm_register(struct tee_context *ctx, unsigned long addr, } shm->flags = flags | TEE_SHM_REGISTER; - shm->teedev = teedev; shm->ctx = ctx; shm->id = -1; addr = untagged_addr(addr); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 42687f6c546d..1412e9cc79ce 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -167,8 +167,7 @@ void tee_device_unregister(struct tee_device *teedev); /** * struct tee_shm - shared memory object - * @teedev: device used to allocate the object - * @ctx: context using the object, if NULL the context is gone + * @ctx: context using the object * @paddr: physical address of the shared memory * @kaddr: virtual address of the shared memory * @size: size of shared memory @@ -183,7 +182,6 @@ void tee_device_unregister(struct tee_device *teedev); * subsystem and from drivers that implements their own shm pool manager. */ struct tee_shm { - struct tee_device *teedev; struct tee_context *ctx; phys_addr_t paddr; void *kaddr; -- cgit v1.2.3 From 8788a1ee070ab9984015ac6ab6af49f2fbfb2cb3 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 07:47:58 -0600 Subject: 6lowpan: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Marcel Holtmann --- include/net/6lowpan.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/6lowpan.h b/include/net/6lowpan.h index a71378007e61..c80539be1542 100644 --- a/include/net/6lowpan.h +++ b/include/net/6lowpan.h @@ -138,7 +138,7 @@ struct lowpan_dev { struct lowpan_iphc_ctx_table ctx; /* must be last */ - u8 priv[0] __aligned(sizeof(void *)); + u8 priv[] __aligned(sizeof(void *)); }; struct lowpan_802154_neigh { -- cgit v1.2.3 From 098accf2da940189f4d62d3514d17f8bb05dc6e1 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 13 Feb 2020 14:00:21 +0000 Subject: iommu: Use C99 flexible array in fwspec Although the 1-element array was a typical pre-C99 way to implement variable-length structures, and indeed is a fundamental construct in the APIs of certain other popular platforms, there's no good reason for it here (and in particular the sizeof() trick is far too "clever" for its own good). We can just as easily implement iommu_fwspec's preallocation behaviour using a standard flexible array member, so let's make it look the way most readers would expect. Signed-off-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/iommu.c | 15 ++++++++------- include/linux/iommu.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 3e3528436e0b..660eea8d1d2f 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -2405,7 +2405,8 @@ int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode, if (fwspec) return ops == fwspec->ops ? 0 : -EINVAL; - fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL); + /* Preallocate for the overwhelmingly common case of 1 ID */ + fwspec = kzalloc(struct_size(fwspec, ids, 1), GFP_KERNEL); if (!fwspec) return -ENOMEM; @@ -2432,15 +2433,15 @@ EXPORT_SYMBOL_GPL(iommu_fwspec_free); int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) { struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); - size_t size; - int i; + int i, new_num; if (!fwspec) return -EINVAL; - size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + num_ids]); - if (size > sizeof(*fwspec)) { - fwspec = krealloc(fwspec, size, GFP_KERNEL); + new_num = fwspec->num_ids + num_ids; + if (new_num > 1) { + fwspec = krealloc(fwspec, struct_size(fwspec, ids, new_num), + GFP_KERNEL); if (!fwspec) return -ENOMEM; @@ -2450,7 +2451,7 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids) for (i = 0; i < num_ids; i++) fwspec->ids[fwspec->num_ids + i] = ids[i]; - fwspec->num_ids += num_ids; + fwspec->num_ids = new_num; return 0; } EXPORT_SYMBOL_GPL(iommu_fwspec_add_ids); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d1b5f4d98569..4d1ba76c9a64 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -592,7 +592,7 @@ struct iommu_fwspec { u32 flags; u32 num_pasid_bits; unsigned int num_ids; - u32 ids[1]; + u32 ids[]; }; /* ATS is supported */ -- cgit v1.2.3 From ea23578611dce2eeaf31dcfe12cd7130cf3d1411 Mon Sep 17 00:00:00 2001 From: John Garry Date: Fri, 28 Feb 2020 23:18:49 +0800 Subject: spi: Allow SPI controller override device buswidth Currently ACPI firmware description for a SPI device does not have any method to describe the data buswidth on the board. So even through the controller and device may support higher modes than standard SPI, it cannot be assumed that the board does - as such, that device is limited to standard SPI in such a circumstance. As a workaround, allow the controller driver supply buswidth override bits, which are used inform the core code that the controller driver knows the buswidth supported on that board for that device. A host controller driver might know this info from DMI tables, for example. Signed-off-by: John Garry Link: https://lore.kernel.org/r/1582903131-160033-2-git-send-email-john.garry@huawei.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 4 +++- include/linux/spi/spi.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 38b4c78df506..292f26807b41 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -510,6 +510,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr) spi->dev.bus = &spi_bus_type; spi->dev.release = spidev_release; spi->cs_gpio = -ENOENT; + spi->mode = ctlr->buswidth_override_bits; spin_lock_init(&spi->statistics.lock); @@ -2181,9 +2182,10 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr, return AE_NO_MEMORY; } + ACPI_COMPANION_SET(&spi->dev, adev); spi->max_speed_hz = lookup.max_speed_hz; - spi->mode = lookup.mode; + spi->mode |= lookup.mode; spi->irq = lookup.irq; spi->bits_per_word = lookup.bits_per_word; spi->chip_select = lookup.chip_select; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 6d16ba01ff5a..600e3793303e 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -481,6 +481,9 @@ struct spi_controller { /* spi_device.mode flags understood by this controller driver */ u32 mode_bits; + /* spi_device.mode flags override flags for this controller */ + u32 buswidth_override_bits; + /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; #define SPI_BPW_MASK(bits) BIT((bits) - 1) -- cgit v1.2.3 From 69879c01a0c3f70e0887cfb4d9ff439814361e46 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 20 Feb 2020 08:08:20 -0600 Subject: proc: Remove the now unnecessary internal mount of proc There remains no more code in the kernel using pids_ns->proc_mnt, therefore remove it from the kernel. The big benefit of this change is that one of the most error prone and tricky parts of the pid namespace implementation, maintaining kernel mounts of proc is removed. In addition removing the unnecessary complexity of the kernel mount fixes a regression that caused the proc mount options to be ignored. Now that the initial mount of proc comes from userspace, those mount options are again honored. This fixes Android's usage of the proc hidepid option. Reported-by: Alistair Strachan Fixes: e94591d0d90c ("proc: Convert proc_mount to use mount_ns.") Signed-off-by: "Eric W. Biederman" --- fs/proc/root.c | 36 ------------------------------------ include/linux/pid_namespace.h | 2 -- include/linux/proc_ns.h | 5 ----- kernel/pid.c | 8 -------- kernel/pid_namespace.c | 7 ------- 5 files changed, 58 deletions(-) (limited to 'include') diff --git a/fs/proc/root.c b/fs/proc/root.c index 608233dfd29c..2633f10446c3 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c @@ -292,39 +292,3 @@ struct proc_dir_entry proc_root = { .subdir = RB_ROOT, .name = "/proc", }; - -int pid_ns_prepare_proc(struct pid_namespace *ns) -{ - struct proc_fs_context *ctx; - struct fs_context *fc; - struct vfsmount *mnt; - - fc = fs_context_for_mount(&proc_fs_type, SB_KERNMOUNT); - if (IS_ERR(fc)) - return PTR_ERR(fc); - - if (fc->user_ns != ns->user_ns) { - put_user_ns(fc->user_ns); - fc->user_ns = get_user_ns(ns->user_ns); - } - - ctx = fc->fs_private; - if (ctx->pid_ns != ns) { - put_pid_ns(ctx->pid_ns); - get_pid_ns(ns); - ctx->pid_ns = ns; - } - - mnt = fc_mount(fc); - put_fs_context(fc); - if (IS_ERR(mnt)) - return PTR_ERR(mnt); - - ns->proc_mnt = mnt; - return 0; -} - -void pid_ns_release_proc(struct pid_namespace *ns) -{ - kern_unmount(ns->proc_mnt); -} diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 2ed6af88794b..4956e362e55e 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -33,7 +33,6 @@ struct pid_namespace { unsigned int level; struct pid_namespace *parent; #ifdef CONFIG_PROC_FS - struct vfsmount *proc_mnt; struct dentry *proc_self; struct dentry *proc_thread_self; #endif @@ -42,7 +41,6 @@ struct pid_namespace { #endif struct user_namespace *user_ns; struct ucounts *ucounts; - struct work_struct proc_work; kgid_t pid_gid; int hide_pid; int reboot; /* group exit code if this pidns was rebooted */ diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h index 4626b1ac3b6c..e1106a077c1a 100644 --- a/include/linux/proc_ns.h +++ b/include/linux/proc_ns.h @@ -50,16 +50,11 @@ enum { #ifdef CONFIG_PROC_FS -extern int pid_ns_prepare_proc(struct pid_namespace *ns); -extern void pid_ns_release_proc(struct pid_namespace *ns); extern int proc_alloc_inum(unsigned int *pino); extern void proc_free_inum(unsigned int inum); #else /* CONFIG_PROC_FS */ -static inline int pid_ns_prepare_proc(struct pid_namespace *ns) { return 0; } -static inline void pid_ns_release_proc(struct pid_namespace *ns) {} - static inline int proc_alloc_inum(unsigned int *inum) { *inum = 1; diff --git a/kernel/pid.c b/kernel/pid.c index ca08d6a3aa77..60820e72634c 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -144,9 +144,6 @@ void free_pid(struct pid *pid) /* Handle a fork failure of the first process */ WARN_ON(ns->child_reaper); ns->pid_allocated = 0; - /* fall through */ - case 0: - schedule_work(&ns->proc_work); break; } @@ -247,11 +244,6 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid, tmp = tmp->parent; } - if (unlikely(is_child_reaper(pid))) { - if (pid_ns_prepare_proc(ns)) - goto out_free; - } - get_pid_ns(ns); refcount_set(&pid->count, 1); for (type = 0; type < PIDTYPE_MAX; ++type) diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index d40017e79ebe..318fcc6ba301 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -57,12 +57,6 @@ static struct kmem_cache *create_pid_cachep(unsigned int level) return READ_ONCE(*pkc); } -static void proc_cleanup_work(struct work_struct *work) -{ - struct pid_namespace *ns = container_of(work, struct pid_namespace, proc_work); - pid_ns_release_proc(ns); -} - static struct ucounts *inc_pid_namespaces(struct user_namespace *ns) { return inc_ucount(ns, current_euid(), UCOUNT_PID_NAMESPACES); @@ -114,7 +108,6 @@ static struct pid_namespace *create_pid_namespace(struct user_namespace *user_ns ns->user_ns = get_user_ns(user_ns); ns->ucounts = ucounts; ns->pid_allocated = PIDNS_ADDING; - INIT_WORK(&ns->proc_work, proc_cleanup_work); return ns; -- cgit v1.2.3 From 680a93166e80e43e3ff85be06005c5cfa492d852 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 07:26:15 -0600 Subject: net: mpls: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/mpls_iptunnel.h | 2 +- net/mpls/internal.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mpls_iptunnel.h b/include/net/mpls_iptunnel.h index 6b4759eae158..9deb3a3735da 100644 --- a/include/net/mpls_iptunnel.h +++ b/include/net/mpls_iptunnel.h @@ -11,7 +11,7 @@ struct mpls_iptunnel_encap { u8 ttl_propagate; u8 default_ttl; u8 reserved1; - u32 label[0]; + u32 label[]; }; static inline struct mpls_iptunnel_encap *mpls_lwtunnel_encap(struct lwtunnel_state *lwtstate) diff --git a/net/mpls/internal.h b/net/mpls/internal.h index 768a302879b4..0e9aa94adc07 100644 --- a/net/mpls/internal.h +++ b/net/mpls/internal.h @@ -98,7 +98,7 @@ struct mpls_nh { /* next hop label forwarding entry */ u8 nh_via_table; u8 nh_reserved1; - u32 nh_label[0]; + u32 nh_label[]; }; /* offset of via from beginning of mpls_nh */ @@ -154,7 +154,7 @@ struct mpls_route { /* next hop label forwarding entry */ u8 rt_nh_size; u8 rt_via_offset; u8 rt_reserved1; - struct mpls_nh rt_nh[0]; + struct mpls_nh rt_nh[]; }; #define for_nexthops(rt) { \ -- cgit v1.2.3 From 8402a31dd803e091fd2ec9cd22040b34a0b07085 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 07:33:37 -0600 Subject: net: dccp: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/linux/dccp.h | 2 +- net/dccp/ccid.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/dccp.h b/include/linux/dccp.h index 6b64b6cc2175..07e547c02fd8 100644 --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -198,7 +198,7 @@ enum dccp_role { struct dccp_service_list { __u32 dccpsl_nr; - __be32 dccpsl_list[0]; + __be32 dccpsl_list[]; }; #define DCCP_SERVICE_INVALID_VALUE htonl((__u32)-1) diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h index 70f88f2b4456..105f3734dadb 100644 --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -95,7 +95,7 @@ void ccid_cleanup_builtins(void); struct ccid { struct ccid_operations *ccid_ops; - char ccid_priv[0]; + char ccid_priv[]; }; static inline void *ccid_priv(const struct ccid *ccid) -- cgit v1.2.3 From 749db093040788ff474937d2452d4cf0176b5a1f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 07:56:29 -0600 Subject: bonding: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/bonding.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bonding.h b/include/net/bonding.h index 3d56b026bb9e..dc2ce31a1f52 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -183,7 +183,7 @@ struct slave { struct bond_up_slave { unsigned int count; struct rcu_head rcu; - struct slave *arr[0]; + struct slave *arr[]; }; /* -- cgit v1.2.3 From e955376277839db92774ec24d559ab42442b95fc Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 08:01:43 -0600 Subject: af_unix: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/af_unix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 17e10fba2152..e51d727cc3cd 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -27,7 +27,7 @@ struct unix_address { refcount_t refcnt; int len; unsigned int hash; - struct sockaddr_un name[0]; + struct sockaddr_un name[]; }; struct unix_skb_parms { -- cgit v1.2.3 From 7782040b950b5d0433f734fb2bba8b8b5ed6ce5a Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 28 Feb 2020 14:45:21 +0100 Subject: unix: uses an atomic type for scm files accounting So the scm_stat_{add,del} helper can be invoked with no additional lock held. This clean-up the code a bit and will make the next patch easier. Signed-off-by: Paolo Abeni Reviewed-by: Kirill Tkhai Signed-off-by: David S. Miller --- include/net/af_unix.h | 2 +- net/unix/af_unix.c | 21 ++++++--------------- 2 files changed, 7 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/af_unix.h b/include/net/af_unix.h index e51d727cc3cd..f42fdddecd41 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -42,7 +42,7 @@ struct unix_skb_parms { } __randomize_layout; struct scm_stat { - u32 nr_fds; + atomic_t nr_fds; }; #define UNIXCB(skb) (*(struct unix_skb_parms *)&((skb)->cb)) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 9d0518d9bdd4..c46fa271fc4a 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -690,7 +690,8 @@ static void unix_show_fdinfo(struct seq_file *m, struct socket *sock) if (sk) { u = unix_sk(sock->sk); - seq_printf(m, "scm_fds: %u\n", READ_ONCE(u->scm_stat.nr_fds)); + seq_printf(m, "scm_fds: %u\n", + atomic_read(&u->scm_stat.nr_fds)); } } #else @@ -1602,10 +1603,8 @@ static void scm_stat_add(struct sock *sk, struct sk_buff *skb) struct scm_fp_list *fp = UNIXCB(skb).fp; struct unix_sock *u = unix_sk(sk); - lockdep_assert_held(&sk->sk_receive_queue.lock); - if (unlikely(fp && fp->count)) - u->scm_stat.nr_fds += fp->count; + atomic_add(fp->count, &u->scm_stat.nr_fds); } static void scm_stat_del(struct sock *sk, struct sk_buff *skb) @@ -1613,10 +1612,8 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb) struct scm_fp_list *fp = UNIXCB(skb).fp; struct unix_sock *u = unix_sk(sk); - lockdep_assert_held(&sk->sk_receive_queue.lock); - if (unlikely(fp && fp->count)) - u->scm_stat.nr_fds -= fp->count; + atomic_sub(fp->count, &u->scm_stat.nr_fds); } /* @@ -1805,10 +1802,8 @@ restart_locked: if (sock_flag(other, SOCK_RCVTSTAMP)) __net_timestamp(skb); maybe_add_creds(skb, sock, other); - spin_lock(&other->sk_receive_queue.lock); scm_stat_add(other, skb); - __skb_queue_tail(&other->sk_receive_queue, skb); - spin_unlock(&other->sk_receive_queue.lock); + skb_queue_tail(&other->sk_receive_queue, skb); unix_state_unlock(other); other->sk_data_ready(other); sock_put(other); @@ -1910,10 +1905,8 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, goto pipe_err_free; maybe_add_creds(skb, sock, other); - spin_lock(&other->sk_receive_queue.lock); scm_stat_add(other, skb); - __skb_queue_tail(&other->sk_receive_queue, skb); - spin_unlock(&other->sk_receive_queue.lock); + skb_queue_tail(&other->sk_receive_queue, skb); unix_state_unlock(other); other->sk_data_ready(other); sent += size; @@ -2409,9 +2402,7 @@ unlock: sk_peek_offset_bwd(sk, chunk); if (UNIXCB(skb).fp) { - spin_lock(&sk->sk_receive_queue.lock); scm_stat_del(sk, skb); - spin_unlock(&sk->sk_receive_queue.lock); unix_detach_fds(&scm, skb); } -- cgit v1.2.3 From e427cad6eee47e2daf207cd7a4156ae72496ee07 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Fri, 28 Feb 2020 14:45:22 +0100 Subject: net: datagram: drop 'destructor' argument from several helpers The only users for such argument are the UDP protocol and the UNIX socket family. We can safely reclaim the accounted memory directly from the UDP code and, after the previous patch, we can do scm stats accounting outside the datagram helpers. Overall this cleans up a bit some datagram-related helpers, and avoids an indirect call per packet in the UDP receive path. v1 -> v2: - call scm_stat_del() only when not peeking - Kirill - fix build issue with CONFIG_INET_ESPINTCP Signed-off-by: Paolo Abeni Reviewed-by: Kirill Tkhai Reviewed-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/skbuff.h | 12 ++---------- net/core/datagram.c | 25 +++++++------------------ net/ipv4/udp.c | 14 ++++++++------ net/unix/af_unix.c | 7 +++++-- net/xfrm/espintcp.c | 2 +- 5 files changed, 23 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 5b50278c4bc8..21749b2cdc9b 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3514,23 +3514,15 @@ int __skb_wait_for_more_packets(struct sock *sk, struct sk_buff_head *queue, struct sk_buff *__skb_try_recv_from_queue(struct sock *sk, struct sk_buff_head *queue, unsigned int flags, - void (*destructor)(struct sock *sk, - struct sk_buff *skb), int *off, int *err, struct sk_buff **last); struct sk_buff *__skb_try_recv_datagram(struct sock *sk, struct sk_buff_head *queue, - unsigned int flags, - void (*destructor)(struct sock *sk, - struct sk_buff *skb), - int *off, int *err, + unsigned int flags, int *off, int *err, struct sk_buff **last); struct sk_buff *__skb_recv_datagram(struct sock *sk, struct sk_buff_head *sk_queue, - unsigned int flags, - void (*destructor)(struct sock *sk, - struct sk_buff *skb), - int *off, int *err); + unsigned int flags, int *off, int *err); struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err); __poll_t datagram_poll(struct file *file, struct socket *sock, diff --git a/net/core/datagram.c b/net/core/datagram.c index a78e7f864c1e..4213081c6ed3 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -166,8 +166,6 @@ done: struct sk_buff *__skb_try_recv_from_queue(struct sock *sk, struct sk_buff_head *queue, unsigned int flags, - void (*destructor)(struct sock *sk, - struct sk_buff *skb), int *off, int *err, struct sk_buff **last) { @@ -198,8 +196,6 @@ struct sk_buff *__skb_try_recv_from_queue(struct sock *sk, refcount_inc(&skb->users); } else { __skb_unlink(skb, queue); - if (destructor) - destructor(sk, skb); } *off = _off; return skb; @@ -212,7 +208,6 @@ struct sk_buff *__skb_try_recv_from_queue(struct sock *sk, * @sk: socket * @queue: socket queue from which to receive * @flags: MSG\_ flags - * @destructor: invoked under the receive lock on successful dequeue * @off: an offset in bytes to peek skb from. Returns an offset * within an skb where data actually starts * @err: error code returned @@ -245,10 +240,7 @@ struct sk_buff *__skb_try_recv_from_queue(struct sock *sk, */ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, struct sk_buff_head *queue, - unsigned int flags, - void (*destructor)(struct sock *sk, - struct sk_buff *skb), - int *off, int *err, + unsigned int flags, int *off, int *err, struct sk_buff **last) { struct sk_buff *skb; @@ -269,8 +261,8 @@ struct sk_buff *__skb_try_recv_datagram(struct sock *sk, * However, this function was correct in any case. 8) */ spin_lock_irqsave(&queue->lock, cpu_flags); - skb = __skb_try_recv_from_queue(sk, queue, flags, destructor, - off, &error, last); + skb = __skb_try_recv_from_queue(sk, queue, flags, off, &error, + last); spin_unlock_irqrestore(&queue->lock, cpu_flags); if (error) goto no_packet; @@ -293,10 +285,7 @@ EXPORT_SYMBOL(__skb_try_recv_datagram); struct sk_buff *__skb_recv_datagram(struct sock *sk, struct sk_buff_head *sk_queue, - unsigned int flags, - void (*destructor)(struct sock *sk, - struct sk_buff *skb), - int *off, int *err) + unsigned int flags, int *off, int *err) { struct sk_buff *skb, *last; long timeo; @@ -304,8 +293,8 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, timeo = sock_rcvtimeo(sk, flags & MSG_DONTWAIT); do { - skb = __skb_try_recv_datagram(sk, sk_queue, flags, destructor, - off, err, &last); + skb = __skb_try_recv_datagram(sk, sk_queue, flags, off, err, + &last); if (skb) return skb; @@ -326,7 +315,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned int flags, return __skb_recv_datagram(sk, &sk->sk_receive_queue, flags | (noblock ? MSG_DONTWAIT : 0), - NULL, &off, err); + &off, err); } EXPORT_SYMBOL(skb_recv_datagram); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 08a41f1e1cd2..a68e2ac37f26 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1671,10 +1671,11 @@ struct sk_buff *__skb_recv_udp(struct sock *sk, unsigned int flags, error = -EAGAIN; do { spin_lock_bh(&queue->lock); - skb = __skb_try_recv_from_queue(sk, queue, flags, - udp_skb_destructor, - off, err, &last); + skb = __skb_try_recv_from_queue(sk, queue, flags, off, + err, &last); if (skb) { + if (!(flags & MSG_PEEK)) + udp_skb_destructor(sk, skb); spin_unlock_bh(&queue->lock); return skb; } @@ -1692,9 +1693,10 @@ struct sk_buff *__skb_recv_udp(struct sock *sk, unsigned int flags, spin_lock(&sk_queue->lock); skb_queue_splice_tail_init(sk_queue, queue); - skb = __skb_try_recv_from_queue(sk, queue, flags, - udp_skb_dtor_locked, - off, err, &last); + skb = __skb_try_recv_from_queue(sk, queue, flags, off, + err, &last); + if (skb && !(flags & MSG_PEEK)) + udp_skb_dtor_locked(sk, skb); spin_unlock(&sk_queue->lock); spin_unlock_bh(&queue->lock); if (skb) diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index c46fa271fc4a..3385a7a0b231 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -2106,9 +2106,12 @@ static int unix_dgram_recvmsg(struct socket *sock, struct msghdr *msg, skip = sk_peek_offset(sk, flags); skb = __skb_try_recv_datagram(sk, &sk->sk_receive_queue, flags, - scm_stat_del, &skip, &err, &last); - if (skb) + &skip, &err, &last); + if (skb) { + if (!(flags & MSG_PEEK)) + scm_stat_del(sk, skb); break; + } mutex_unlock(&u->iolock); diff --git a/net/xfrm/espintcp.c b/net/xfrm/espintcp.c index f15d6a564b0e..037ea156d2f9 100644 --- a/net/xfrm/espintcp.c +++ b/net/xfrm/espintcp.c @@ -100,7 +100,7 @@ static int espintcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, flags |= nonblock ? MSG_DONTWAIT : 0; - skb = __skb_recv_datagram(sk, &ctx->ike_queue, flags, NULL, &off, &err); + skb = __skb_recv_datagram(sk, &ctx->ike_queue, flags, &off, &err); if (!skb) return err; -- cgit v1.2.3 From 466552b9f3730f06fee75776a208f5fa9c06c6a3 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 28 Feb 2020 08:53:07 +0100 Subject: scsi: core: add scsi_host_complete_all_commands() helper Add a helper scsi_host_complete_all_commands() to terminate all outstanding commands on a SCSI host. Link: https://lore.kernel.org/r/20200228075318.91255-3-hare@suse.de Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 28 ++++++++++++++++++++++++++++ include/scsi/scsi_host.h | 2 ++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 1d669e47b692..00ae9d43ce9f 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -650,3 +650,31 @@ void scsi_flush_work(struct Scsi_Host *shost) flush_workqueue(shost->work_q); } EXPORT_SYMBOL_GPL(scsi_flush_work); + +static bool complete_all_cmds_iter(struct request *rq, void *data, bool rsvd) +{ + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); + int status = *(int *)data; + + scsi_dma_unmap(scmd); + scmd->result = status << 16; + scmd->scsi_done(scmd); + return true; +} + +/** + * scsi_host_complete_all_commands - Terminate all running commands + * @shost: Scsi Host on which commands should be terminated + * @status: Status to be set for the terminated commands + * + * There is no protection against modification of the number + * of outstanding commands. It is the responsibility of the + * caller to ensure that concurrent I/O submission and/or + * completion is stopped when calling this function. + */ +void scsi_host_complete_all_commands(struct Scsi_Host *shost, int status) +{ + blk_mq_tagset_busy_iter(&shost->tag_set, complete_all_cmds_iter, + &status); +} +EXPORT_SYMBOL_GPL(scsi_host_complete_all_commands); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 7a97fb8104cf..25bef781cbe1 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -732,6 +732,8 @@ extern int scsi_host_busy(struct Scsi_Host *shost); extern void scsi_host_put(struct Scsi_Host *t); extern struct Scsi_Host *scsi_host_lookup(unsigned short); extern const char *scsi_host_state_name(enum scsi_host_state); +extern void scsi_host_complete_all_commands(struct Scsi_Host *shost, + int status); static inline int __must_check scsi_add_host(struct Scsi_Host *host, struct device *dev) -- cgit v1.2.3 From 2bb955840c1dc44746af8a7873d9a0ba70debbd6 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 28 Feb 2020 08:53:13 +0100 Subject: scsi: core: add scsi_host_(block,unblock) helper function Add helper functions to call scsi_internal_device_block()/ scsi_internal_device_unblock() for all attached devices on a SCSI host. Link: https://lore.kernel.org/r/20200228075318.91255-9-hare@suse.de Reviewed-by: Christoph Hellwig Reviewed-by: Bart Van Assche Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 30 ++++++++++++++++++++++++++++++ include/scsi/scsi_host.h | 2 ++ 2 files changed, 32 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 610ee41fa54c..a48a5727831b 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2845,6 +2845,36 @@ scsi_target_unblock(struct device *dev, enum scsi_device_state new_state) } EXPORT_SYMBOL_GPL(scsi_target_unblock); +int +scsi_host_block(struct Scsi_Host *shost) +{ + struct scsi_device *sdev; + int ret = 0; + + shost_for_each_device(sdev, shost) { + ret = scsi_internal_device_block(sdev); + if (ret) + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(scsi_host_block); + +int +scsi_host_unblock(struct Scsi_Host *shost, int new_state) +{ + struct scsi_device *sdev; + int ret = 0; + + shost_for_each_device(sdev, shost) { + ret = scsi_internal_device_unblock(sdev, new_state); + if (ret) + break; + } + return ret; +} +EXPORT_SYMBOL_GPL(scsi_host_unblock); + /** * scsi_kmap_atomic_sg - find and atomically map an sg-elemnt * @sgl: scatter-gather list diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 25bef781cbe1..613c3820028e 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -758,6 +758,8 @@ static inline int scsi_host_scan_allowed(struct Scsi_Host *shost) extern void scsi_unblock_requests(struct Scsi_Host *); extern void scsi_block_requests(struct Scsi_Host *); +extern int scsi_host_block(struct Scsi_Host *shost); +extern int scsi_host_unblock(struct Scsi_Host *shost, int new_state); struct class_container; -- cgit v1.2.3 From dcece99e86b1191d2789fb5b5caa4c5b7fc2bfef Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 28 Feb 2020 08:53:15 +0100 Subject: scsi: core: add scsi_host_busy_iter() Add an iterator scsi_host_busy_iter() to traverse all busy commands. If locking against concurrent command completions is required, it has to be provided by the caller. Link: https://lore.kernel.org/r/20200228075318.91255-11-hare@suse.de Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/hosts.c | 37 +++++++++++++++++++++++++++++++++++++ include/scsi/scsi_host.h | 3 +++ 2 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 00ae9d43ce9f..7ec91c3a66ca 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -678,3 +678,40 @@ void scsi_host_complete_all_commands(struct Scsi_Host *shost, int status) &status); } EXPORT_SYMBOL_GPL(scsi_host_complete_all_commands); + +struct scsi_host_busy_iter_data { + bool (*fn)(struct scsi_cmnd *, void *, bool); + void *priv; +}; + +static bool __scsi_host_busy_iter_fn(struct request *req, void *priv, + bool reserved) +{ + struct scsi_host_busy_iter_data *iter_data = priv; + struct scsi_cmnd *sc = blk_mq_rq_to_pdu(req); + + return iter_data->fn(sc, iter_data->priv, reserved); +} + +/** + * scsi_host_busy_iter - Iterate over all busy commands + * @shost: Pointer to Scsi_Host. + * @fn: Function to call on each busy command + * @priv: Data pointer passed to @fn + * + * If locking against concurrent command completions is required + * ithas to be provided by the caller + **/ +void scsi_host_busy_iter(struct Scsi_Host *shost, + bool (*fn)(struct scsi_cmnd *, void *, bool), + void *priv) +{ + struct scsi_host_busy_iter_data iter_data = { + .fn = fn, + .priv = priv, + }; + + blk_mq_tagset_busy_iter(&shost->tag_set, __scsi_host_busy_iter_fn, + &iter_data); +} +EXPORT_SYMBOL_GPL(scsi_host_busy_iter); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 613c3820028e..eff12445b823 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -761,6 +761,9 @@ extern void scsi_block_requests(struct Scsi_Host *); extern int scsi_host_block(struct Scsi_Host *shost); extern int scsi_host_unblock(struct Scsi_Host *shost, int new_state); +void scsi_host_busy_iter(struct Scsi_Host *, + bool (*fn)(struct scsi_cmnd *, void *, bool), void *priv); + struct class_container; /* -- cgit v1.2.3 From c5a9707672fe22865d90fc04ac2fbc4f812666f3 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Fri, 28 Feb 2020 08:53:18 +0100 Subject: scsi: core: Remove cmd_list functionality Remove cmd_list functionality; no users left. With that the scsi_put_command() becomes empty, so remove that one, too. Link: https://lore.kernel.org/r/20200228075318.91255-14-hare@suse.de Reviewed-by: Christoph Hellwig Reviewed-by: Bart van Assche Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi.c | 14 -------------- drivers/scsi/scsi_error.c | 1 - drivers/scsi/scsi_lib.c | 31 ------------------------------- drivers/scsi/scsi_priv.h | 2 -- drivers/scsi/scsi_scan.c | 1 - include/scsi/scsi_cmnd.h | 1 - include/scsi/scsi_device.h | 1 - include/scsi/scsi_host.h | 2 -- 8 files changed, 53 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 4b9fdfab77d9..56c24a73e0c7 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -94,20 +94,6 @@ EXPORT_SYMBOL(scsi_logging_level); ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain); EXPORT_SYMBOL(scsi_sd_pm_domain); -/** - * scsi_put_command - Free a scsi command block - * @cmd: command block to free - * - * Returns: Nothing. - * - * Notes: The command must not belong to any lists. - */ -void scsi_put_command(struct scsi_cmnd *cmd) -{ - scsi_del_cmd_from_list(cmd); - BUG_ON(delayed_work_pending(&cmd->abort_work)); -} - #ifdef CONFIG_SCSI_LOGGING void scsi_log_send(struct scsi_cmnd *cmd) { diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index ae2fa170f6ad..978be1602f71 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -2412,7 +2412,6 @@ scsi_ioctl_reset(struct scsi_device *dev, int __user *arg) wake_up(&shost->host_wait); scsi_run_host_queues(shost); - scsi_put_command(scmd); kfree(rq); out_put_autopm_host: diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index a48a5727831b..258a428a0a3f 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -562,7 +562,6 @@ static void scsi_mq_uninit_cmd(struct scsi_cmnd *cmd) { scsi_mq_free_sgtables(cmd); scsi_uninit_cmd(cmd); - scsi_del_cmd_from_list(cmd); } /* Returns false when no more bytes to process, true if there are more */ @@ -1098,35 +1097,6 @@ static void scsi_cleanup_rq(struct request *rq) } } -/* Add a command to the list used by the aacraid and dpt_i2o drivers */ -void scsi_add_cmd_to_list(struct scsi_cmnd *cmd) -{ - struct scsi_device *sdev = cmd->device; - struct Scsi_Host *shost = sdev->host; - unsigned long flags; - - if (shost->use_cmd_list) { - spin_lock_irqsave(&sdev->list_lock, flags); - list_add_tail(&cmd->list, &sdev->cmd_list); - spin_unlock_irqrestore(&sdev->list_lock, flags); - } -} - -/* Remove a command from the list used by the aacraid and dpt_i2o drivers */ -void scsi_del_cmd_from_list(struct scsi_cmnd *cmd) -{ - struct scsi_device *sdev = cmd->device; - struct Scsi_Host *shost = sdev->host; - unsigned long flags; - - if (shost->use_cmd_list) { - spin_lock_irqsave(&sdev->list_lock, flags); - BUG_ON(list_empty(&cmd->list)); - list_del_init(&cmd->list); - spin_unlock_irqrestore(&sdev->list_lock, flags); - } -} - /* Called after a request has been started. */ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) { @@ -1160,7 +1130,6 @@ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) if (in_flight) __set_bit(SCMD_STATE_INFLIGHT, &cmd->state); - scsi_add_cmd_to_list(cmd); } static blk_status_t scsi_setup_scsi_cmnd(struct scsi_device *sdev, diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 25b0aaaf5ae8..22b6585e28b4 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -83,8 +83,6 @@ int scsi_eh_get_sense(struct list_head *work_q, int scsi_noretry_cmd(struct scsi_cmnd *scmd); /* scsi_lib.c */ -extern void scsi_add_cmd_to_list(struct scsi_cmnd *cmd); -extern void scsi_del_cmd_from_list(struct scsi_cmnd *cmd); extern int scsi_maybe_unblock_host(struct scsi_device *sdev); extern void scsi_device_unbusy(struct scsi_device *sdev, struct scsi_cmnd *cmd); extern void scsi_queue_insert(struct scsi_cmnd *cmd, int reason); diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 058079f915f1..f2437a7570ce 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -236,7 +236,6 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget, sdev->sdev_state = SDEV_CREATED; INIT_LIST_HEAD(&sdev->siblings); INIT_LIST_HEAD(&sdev->same_target_siblings); - INIT_LIST_HEAD(&sdev->cmd_list); INIT_LIST_HEAD(&sdev->starved_entry); INIT_LIST_HEAD(&sdev->event_list); spin_lock_init(&sdev->list_lock); diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index a2849bb9cd19..80ac89e47b47 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -159,7 +159,6 @@ static inline struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd) return *(struct scsi_driver **)cmd->request->rq_disk->private_data; } -extern void scsi_put_command(struct scsi_cmnd *); extern void scsi_finish_command(struct scsi_cmnd *cmd); extern void *scsi_kmap_atomic_sg(struct scatterlist *sg, int sg_count, diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index f8312a3e5b42..f146a4557787 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -110,7 +110,6 @@ struct scsi_device { atomic_t device_blocked; /* Device returned QUEUE_FULL. */ spinlock_t list_lock; - struct list_head cmd_list; /* queue of in use SCSI Command structures */ struct list_head starved_entry; unsigned short queue_depth; /* How deep of a queue we want */ unsigned short max_queue_depth; /* max queue depth */ diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index eff12445b823..74dc7d4f2a96 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -624,8 +624,6 @@ struct Scsi_Host { /* The controller does not support WRITE SAME */ unsigned no_write_same:1; - unsigned use_cmd_list:1; - /* Host responded with short (<36 bytes) INQUIRY result */ unsigned short_inquiry:1; -- cgit v1.2.3 From 65ca846a53149a1a72cd8d02e7b2e73dd545b834 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 22 Jan 2020 19:56:34 -0800 Subject: scsi: core: Introduce {init,exit}_cmd_priv() The current behavior of the SCSI core is to clear driver-private data before preparing a request for submission to the SCSI LLD. Make it possible for SCSI LLDs to disable clearing of driver-private data. These hooks will be used by a later patch, namely "scsi: ufs: Let the SCSI core allocate per-command UFS data". Link: https://lore.kernel.org/r/20200123035637.21848-2-bvanassche@acm.org Cc: Tomas Winkler Cc: Stanley Chu Cc: Bean Huo Cc: Avri Altman Cc: Can Guo Cc: Ming Lei Cc: Johannes Thumshirn Cc: Hannes Reinecke Cc: Christoph Hellwig Signed-off-by: Bart Van Assche Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 29 +++++++++++++++++++++++------ include/scsi/scsi_host.h | 3 +++ 2 files changed, 26 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 258a428a0a3f..9f201194c46a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1097,7 +1097,7 @@ static void scsi_cleanup_rq(struct request *rq) } } -/* Called after a request has been started. */ +/* Called before a request is prepared. See also scsi_mq_prep_fn(). */ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) { void *buf = cmd->sense_buffer; @@ -1105,7 +1105,7 @@ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) struct request *rq = blk_mq_rq_from_pdu(cmd); unsigned int flags = cmd->flags & SCMD_PRESERVED_FLAGS; unsigned long jiffies_at_alloc; - int retries; + int retries, to_clear; bool in_flight; if (!blk_rq_is_scsi(rq) && !(flags & SCMD_INITIALIZED)) { @@ -1116,9 +1116,15 @@ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd) jiffies_at_alloc = cmd->jiffies_at_alloc; retries = cmd->retries; in_flight = test_bit(SCMD_STATE_INFLIGHT, &cmd->state); - /* zero out the cmd, except for the embedded scsi_request */ - memset((char *)cmd + sizeof(cmd->req), 0, - sizeof(*cmd) - sizeof(cmd->req) + dev->host->hostt->cmd_size); + /* + * Zero out the cmd, except for the embedded scsi_request. Only clear + * the driver-private command data if the LLD does not supply a + * function to initialize that data. + */ + to_clear = sizeof(*cmd) - sizeof(cmd->req); + if (!dev->host->hostt->init_cmd_priv) + to_clear += dev->host->hostt->cmd_size; + memset((char *)cmd + sizeof(cmd->req), 0, to_clear); cmd->device = dev; cmd->sense_buffer = buf; @@ -1711,6 +1717,7 @@ static int scsi_mq_init_request(struct blk_mq_tag_set *set, struct request *rq, const bool unchecked_isa_dma = shost->unchecked_isa_dma; struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); struct scatterlist *sg; + int ret = 0; if (unchecked_isa_dma) cmd->flags |= SCMD_UNCHECKED_ISA_DMA; @@ -1726,14 +1733,24 @@ static int scsi_mq_init_request(struct blk_mq_tag_set *set, struct request *rq, cmd->prot_sdb = (void *)sg + scsi_mq_inline_sgl_size(shost); } - return 0; + if (shost->hostt->init_cmd_priv) { + ret = shost->hostt->init_cmd_priv(shost, cmd); + if (ret < 0) + scsi_free_sense_buffer(unchecked_isa_dma, + cmd->sense_buffer); + } + + return ret; } static void scsi_mq_exit_request(struct blk_mq_tag_set *set, struct request *rq, unsigned int hctx_idx) { + struct Scsi_Host *shost = set->driver_data; struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); + if (shost->hostt->exit_cmd_priv) + shost->hostt->exit_cmd_priv(shost, cmd); scsi_free_sense_buffer(cmd->flags & SCMD_UNCHECKED_ISA_DMA, cmd->sense_buffer); } diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 74dc7d4f2a96..7464394e7d01 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -62,6 +62,9 @@ struct scsi_host_template { void __user *arg); #endif + int (*init_cmd_priv)(struct Scsi_Host *shost, struct scsi_cmnd *cmd); + int (*exit_cmd_priv)(struct Scsi_Host *shost, struct scsi_cmnd *cmd); + /* * The queuecommand function is used to queue up a scsi * command block to the LLDD. When the driver finished -- cgit v1.2.3 From badc61982adb6018a48ed8fe32087b9754cae14b Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Fri, 28 Feb 2020 13:14:04 +0100 Subject: efi/x86: Add RNG seed EFI table to unencrypted mapping check When booting with SME active, EFI tables must be mapped unencrypted since they were built by UEFI in unencrypted memory. Update the list of tables to be checked during early_memremap() processing to account for the EFI RNG seed table. Signed-off-by: Tom Lendacky Signed-off-by: Ard Biesheuvel Signed-off-by: Ingo Molnar Cc: linux-efi@vger.kernel.org Cc: Ingo Molnar Cc: Thomas Gleixner Cc: David Hildenbrand Cc: Heinrich Schuchardt Link: https://lore.kernel.org/r/b64385fc13e5d7ad4b459216524f138e7879234f.1582662842.git.thomas.lendacky@amd.com Link: https://lore.kernel.org/r/20200228121408.9075-3-ardb@kernel.org --- arch/x86/platform/efi/efi.c | 1 + drivers/firmware/efi/efi.c | 18 ++++++++++-------- include/linux/efi.h | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 0a8117865430..aca9bdd87bca 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -90,6 +90,7 @@ static const unsigned long * const efi_tables[] = { #endif &efi.tpm_log, &efi.tpm_final_log, + &efi_rng_seed, }; u64 efi_setup; /* efi setup_data physical address */ diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 69a585106d30..1e79f77d4e6c 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -45,7 +45,7 @@ struct efi __read_mostly efi = { }; EXPORT_SYMBOL(efi); -static unsigned long __ro_after_init rng_seed = EFI_INVALID_TABLE_ADDR; +unsigned long __ro_after_init efi_rng_seed = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata mem_reserve = EFI_INVALID_TABLE_ADDR; static unsigned long __initdata rt_prop = EFI_INVALID_TABLE_ADDR; @@ -451,7 +451,7 @@ static const efi_config_table_type_t common_tables[] __initconst = { {SMBIOS3_TABLE_GUID, "SMBIOS 3.0", &efi.smbios3}, {EFI_SYSTEM_RESOURCE_TABLE_GUID, "ESRT", &efi.esrt}, {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi_mem_attr_table}, - {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &rng_seed}, + {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi_rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &mem_reserve}, @@ -519,11 +519,11 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, pr_cont("\n"); set_bit(EFI_CONFIG_TABLES, &efi.flags); - if (rng_seed != EFI_INVALID_TABLE_ADDR) { + if (efi_rng_seed != EFI_INVALID_TABLE_ADDR) { struct linux_efi_random_seed *seed; u32 size = 0; - seed = early_memremap(rng_seed, sizeof(*seed)); + seed = early_memremap(efi_rng_seed, sizeof(*seed)); if (seed != NULL) { size = seed->size; early_memunmap(seed, sizeof(*seed)); @@ -531,7 +531,8 @@ int __init efi_config_parse_tables(const efi_config_table_t *config_tables, pr_err("Could not map UEFI random seed!\n"); } if (size > 0) { - seed = early_memremap(rng_seed, sizeof(*seed) + size); + seed = early_memremap(efi_rng_seed, + sizeof(*seed) + size); if (seed != NULL) { pr_notice("seeding entropy pool\n"); add_bootloader_randomness(seed->bits, seed->size); @@ -923,7 +924,7 @@ static int update_efi_random_seed(struct notifier_block *nb, if (!kexec_in_progress) return NOTIFY_DONE; - seed = memremap(rng_seed, sizeof(*seed), MEMREMAP_WB); + seed = memremap(efi_rng_seed, sizeof(*seed), MEMREMAP_WB); if (seed != NULL) { size = min(seed->size, EFI_RANDOM_SEED_SIZE); memunmap(seed); @@ -931,7 +932,8 @@ static int update_efi_random_seed(struct notifier_block *nb, pr_err("Could not map UEFI random seed!\n"); } if (size > 0) { - seed = memremap(rng_seed, sizeof(*seed) + size, MEMREMAP_WB); + seed = memremap(efi_rng_seed, sizeof(*seed) + size, + MEMREMAP_WB); if (seed != NULL) { seed->size = size; get_random_bytes(seed->bits, seed->size); @@ -949,7 +951,7 @@ static struct notifier_block efi_random_seed_nb = { static int __init register_update_efi_random_seed(void) { - if (rng_seed == EFI_INVALID_TABLE_ADDR) + if (efi_rng_seed == EFI_INVALID_TABLE_ADDR) return 0; return register_reboot_notifier(&efi_random_seed_nb); } diff --git a/include/linux/efi.h b/include/linux/efi.h index 2ab33d5d6ca5..e8a08a499131 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -526,6 +526,8 @@ typedef struct { efi_time_t time_of_revocation; } efi_cert_x509_sha256_t; +extern unsigned long __ro_after_init efi_rng_seed; /* RNG Seed table */ + /* * All runtime access to EFI goes through this structure: */ -- cgit v1.2.3 From 53cb2cfaa62d122fa9d92398926a6b7e8f052844 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 07:59:59 -0600 Subject: cfg802154: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: Stefan Schmidt --- include/net/cfg802154.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/cfg802154.h b/include/net/cfg802154.h index 6f86073a5d7d..6ed07844eb24 100644 --- a/include/net/cfg802154.h +++ b/include/net/cfg802154.h @@ -214,7 +214,7 @@ struct wpan_phy { /* the network namespace this phy lives in currently */ possible_net_t _net; - char priv[0] __aligned(NETDEV_ALIGN); + char priv[] __aligned(NETDEV_ALIGN); }; static inline struct net *wpan_phy_net(struct wpan_phy *wpan_phy) -- cgit v1.2.3 From b90feaff2a2cbf339069adec4bfd6091cfb44b50 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 27 Feb 2020 14:58:44 -0600 Subject: net: sched: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 2 +- net/sched/em_ipt.c | 2 +- net/sched/em_nbyte.c | 2 +- net/sched/sch_atm.c | 2 +- net/sched/sch_netem.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 6a70845bd9ab..20d2c6419612 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -181,7 +181,7 @@ struct tc_taprio_qopt_offload { u64 cycle_time_extension; size_t num_entries; - struct tc_taprio_sched_entry entries[0]; + struct tc_taprio_sched_entry entries[]; }; /* Reference counting */ diff --git a/net/sched/em_ipt.c b/net/sched/em_ipt.c index 9fff6480acc6..eecfe072c508 100644 --- a/net/sched/em_ipt.c +++ b/net/sched/em_ipt.c @@ -22,7 +22,7 @@ struct em_ipt_match { const struct xt_match *match; u32 hook; u8 nfproto; - u8 match_data[0] __aligned(8); + u8 match_data[] __aligned(8); }; struct em_ipt_xt_match { diff --git a/net/sched/em_nbyte.c b/net/sched/em_nbyte.c index 88c7ce42df7e..2c1192a2ee5e 100644 --- a/net/sched/em_nbyte.c +++ b/net/sched/em_nbyte.c @@ -16,7 +16,7 @@ struct nbyte_data { struct tcf_em_nbyte hdr; - char pattern[0]; + char pattern[]; }; static int em_nbyte_change(struct net *net, void *data, int data_len, diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index f4f9b8cdbffb..ee12ca9f55b4 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -58,7 +58,7 @@ struct atm_flow_data { struct atm_flow_data *excess; /* flow for excess traffic; NULL to set CLP instead */ int hdr_len; - unsigned char hdr[0]; /* header data; MUST BE LAST */ + unsigned char hdr[]; /* header data; MUST BE LAST */ }; struct atm_qdisc_data { diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 42e557d48e4e..84f82771cdf5 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -66,7 +66,7 @@ struct disttable { u32 size; - s16 table[0]; + s16 table[]; }; struct netem_sched_data { -- cgit v1.2.3 From 70ae1e127b486704c62d3537d69ca65c446d4d83 Mon Sep 17 00:00:00 2001 From: Cris Forno Date: Fri, 28 Feb 2020 14:12:04 -0600 Subject: ethtool: Factored out similar ethtool link settings for virtual devices to core Three virtual devices (ibmveth, virtio_net, and netvsc) all have similar code to set link settings and validate ethtool command. To eliminate duplication of code, it is factored out into core/ethtool.c. Signed-off-by: Cris Forno Signed-off-by: David S. Miller --- include/linux/ethtool.h | 6 ++++++ net/ethtool/ioctl.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 95991e4300bf..23373978cb3c 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -420,4 +420,10 @@ struct ethtool_rx_flow_rule * ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input); void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule); +bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd); +int ethtool_virtdev_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd, + u32 *dev_speed, u8 *dev_duplex); + + #endif /* _LINUX_ETHTOOL_H */ diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index b987052d91ef..f2fe8e5896dc 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -459,6 +459,24 @@ static int load_link_ksettings_from_user(struct ethtool_link_ksettings *to, return 0; } +/* Check if the user is trying to change anything besides speed/duplex */ +bool ethtool_virtdev_validate_cmd(const struct ethtool_link_ksettings *cmd) +{ + struct ethtool_link_settings base2 = {}; + + base2.speed = cmd->base.speed; + base2.port = PORT_OTHER; + base2.duplex = cmd->base.duplex; + base2.cmd = cmd->base.cmd; + base2.link_mode_masks_nwords = cmd->base.link_mode_masks_nwords; + + return !memcmp(&base2, &cmd->base, sizeof(base2)) && + bitmap_empty(cmd->link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS) && + bitmap_empty(cmd->link_modes.lp_advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); +} + /* convert a kernel internal ethtool_link_ksettings to * ethtool_link_usettings in user space. return 0 on success, errno on * error. @@ -581,6 +599,27 @@ static int ethtool_set_link_ksettings(struct net_device *dev, return err; } +int ethtool_virtdev_set_link_ksettings(struct net_device *dev, + const struct ethtool_link_ksettings *cmd, + u32 *dev_speed, u8 *dev_duplex) +{ + u32 speed; + u8 duplex; + + speed = cmd->base.speed; + duplex = cmd->base.duplex; + /* don't allow custom speed and duplex */ + if (!ethtool_validate_speed(speed) || + !ethtool_validate_duplex(duplex) || + !ethtool_virtdev_validate_cmd(cmd)) + return -EINVAL; + *dev_speed = speed; + *dev_duplex = duplex; + + return 0; +} +EXPORT_SYMBOL(ethtool_virtdev_set_link_ksettings); + /* Query device for its ethtool_cmd settings. * * Backward compatibility note: for compatibility with legacy ethtool, this is -- cgit v1.2.3 From 2603c29e6c12135c1ef248ddaccf91de32567454 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:11:02 -0600 Subject: net: sock_reuseport: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/sock_reuseport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sock_reuseport.h b/include/net/sock_reuseport.h index 3ecaa15d1850..505f1e18e9bf 100644 --- a/include/net/sock_reuseport.h +++ b/include/net/sock_reuseport.h @@ -24,7 +24,7 @@ struct sock_reuseport { unsigned int bind_inany:1; unsigned int has_conns:1; struct bpf_prog __rcu *prog; /* optional BPF sock selector */ - struct sock *socks[0]; /* array of sock pointers */ + struct sock *socks[]; /* array of sock pointers */ }; extern int reuseport_alloc(struct sock *sk, bool bind_inany); -- cgit v1.2.3 From 2e7aaaa19cf226944c19fbefbbbbd76f9720b310 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:13:05 -0600 Subject: net: sctp: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 314a2fa21d6b..fb42c90348d3 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -326,7 +326,7 @@ struct sctp_cookie { * the association TCB is re-constructed from the cookie. */ __u32 raw_addr_list_len; - struct sctp_init_chunk peer_init[0]; + struct sctp_init_chunk peer_init[]; }; -- cgit v1.2.3 From 97a888c2ff6bbb92bdb0cb6cfff458cc412da788 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:14:11 -0600 Subject: net: nexthop: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Reviewed-by: David Ahern Signed-off-by: David S. Miller --- include/net/nexthop.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/nexthop.h b/include/net/nexthop.h index 331ebbc94fe7..c440ccc861fc 100644 --- a/include/net/nexthop.h +++ b/include/net/nexthop.h @@ -73,7 +73,7 @@ struct nh_group { u16 num_nh; bool mpath; bool has_v4; - struct nh_grp_entry nh_entries[0]; + struct nh_grp_entry nh_entries[]; }; struct nexthop { -- cgit v1.2.3 From 207644f5138fb8e14debaa22f72adaa78c6a08cc Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:44:10 -0600 Subject: net: ip6_route: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/ip6_route.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index b69c16cbbf71..f7543c095b33 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -16,7 +16,7 @@ struct route_info { reserved_h:3; #endif __be32 lifetime; - __u8 prefix[0]; /* 0,8 or 16 */ + __u8 prefix[]; /* 0,8 or 16 */ }; #include -- cgit v1.2.3 From e8316026d538873f60f41dab4caaa493a21684b0 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:50:23 -0600 Subject: net: lwtunnel: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/lwtunnel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/lwtunnel.h b/include/net/lwtunnel.h index 5d6c5b1fc695..b5e6edf74b70 100644 --- a/include/net/lwtunnel.h +++ b/include/net/lwtunnel.h @@ -30,7 +30,7 @@ struct lwtunnel_state { int (*orig_output)(struct net *net, struct sock *sk, struct sk_buff *skb); int (*orig_input)(struct sk_buff *); struct rcu_head rcu; - __u8 data[0]; + __u8 data[]; }; struct lwtunnel_encap_ops { -- cgit v1.2.3 From c61a2a76e5da267d8f5070a34c074d104155c0f6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:51:41 -0600 Subject: net: ipv6: mld: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/mld.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mld.h b/include/net/mld.h index b0f5b3105ef0..496bddb59942 100644 --- a/include/net/mld.h +++ b/include/net/mld.h @@ -24,12 +24,12 @@ struct mld2_grec { __u8 grec_auxwords; __be16 grec_nsrcs; struct in6_addr grec_mca; - struct in6_addr grec_src[0]; + struct in6_addr grec_src[]; }; struct mld2_report { struct icmp6hdr mld2r_hdr; - struct mld2_grec mld2r_grec[0]; + struct mld2_grec mld2r_grec[]; }; #define mld2r_type mld2r_hdr.icmp6_type @@ -55,7 +55,7 @@ struct mld2_query { #endif __u8 mld2q_qqic; __be16 mld2q_nsrcs; - struct in6_addr mld2q_srcs[0]; + struct in6_addr mld2q_srcs[]; }; #define mld2q_type mld2q_hdr.icmp6_type -- cgit v1.2.3 From 53e76f4824f0a501353e0da3f9506542d137b634 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 18:53:11 -0600 Subject: ndisc: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/ndisc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/ndisc.h b/include/net/ndisc.h index b5ebeb3b0de0..1c61aeb3a1c0 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -80,12 +80,12 @@ extern struct neigh_table nd_tbl; struct nd_msg { struct icmp6hdr icmph; struct in6_addr target; - __u8 opt[0]; + __u8 opt[]; }; struct rs_msg { struct icmp6hdr icmph; - __u8 opt[0]; + __u8 opt[]; }; struct ra_msg { @@ -98,7 +98,7 @@ struct rd_msg { struct icmp6hdr icmph; struct in6_addr target; struct in6_addr dest; - __u8 opt[0]; + __u8 opt[]; }; struct nd_opt_hdr { -- cgit v1.2.3 From a79b41ec983620ee43c35fd9082ec49816654a71 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 19:00:40 -0600 Subject: net: dn_fib: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/dn_fib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h index 6dd2213c5eb2..ccc6e9df178b 100644 --- a/include/net/dn_fib.h +++ b/include/net/dn_fib.h @@ -90,7 +90,7 @@ struct dn_fib_table { int (*flush)(struct dn_fib_table *t); int (*dump)(struct dn_fib_table *t, struct sk_buff *skb, struct netlink_callback *cb); - unsigned char data[0]; + unsigned char data[]; }; #ifdef CONFIG_DECNET_ROUTER -- cgit v1.2.3 From 8661b6e7c46408bb6a74d3a1d77ce9af35f584d8 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 19:02:54 -0600 Subject: net: flow_offload: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/flow_offload.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 4e864c34a1b0..cd3510ac66b0 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -229,7 +229,7 @@ struct flow_action_entry { struct flow_action { unsigned int num_entries; - struct flow_action_entry entries[0]; + struct flow_action_entry entries[]; }; static inline bool flow_action_has_entries(const struct flow_action *action) -- cgit v1.2.3 From 08ca27d027c238ed3f9b9968d349cebde44d99a6 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 19:05:02 -0600 Subject: neighbour: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/neighbour.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 8ec77bfdc1a4..e1476775769c 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -174,7 +174,7 @@ struct pneigh_entry { struct net_device *dev; u8 flags; u8 protocol; - u8 key[0]; + u8 key[]; }; /* -- cgit v1.2.3 From 5a8b7c4b7f95c8936cd3dcba01169cd5840e291f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 28 Feb 2020 19:07:01 -0600 Subject: arcnet: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/uapi/linux/if_arcnet.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/if_arcnet.h b/include/uapi/linux/if_arcnet.h index 683878036d76..b122cfac7128 100644 --- a/include/uapi/linux/if_arcnet.h +++ b/include/uapi/linux/if_arcnet.h @@ -60,7 +60,7 @@ struct arc_rfc1201 { __u8 proto; /* protocol ID field - varies */ __u8 split_flag; /* for use with split packets */ __be16 sequence; /* sequence number */ - __u8 payload[0]; /* space remaining in packet (504 bytes)*/ + __u8 payload[]; /* space remaining in packet (504 bytes)*/ }; #define RFC1201_HDR_SIZE 4 @@ -69,7 +69,7 @@ struct arc_rfc1201 { */ struct arc_rfc1051 { __u8 proto; /* ARC_P_RFC1051_ARP/RFC1051_IP */ - __u8 payload[0]; /* 507 bytes */ + __u8 payload[]; /* 507 bytes */ }; #define RFC1051_HDR_SIZE 1 @@ -80,7 +80,7 @@ struct arc_rfc1051 { struct arc_eth_encap { __u8 proto; /* Always ARC_P_ETHER */ struct ethhdr eth; /* standard ethernet header (yuck!) */ - __u8 payload[0]; /* 493 bytes */ + __u8 payload[]; /* 493 bytes */ }; #define ETH_ENCAP_HDR_SIZE 14 -- cgit v1.2.3 From 249bc9744e165abe74ae326f43e9d70bad54c3b7 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 1 Mar 2020 21:36:09 +0100 Subject: net: phy: avoid clearing PHY interrupts twice in irq handler On all PHY drivers that implement did_interrupt() reading the interrupt status bits clears them. This means we may loose an interrupt that is triggered between calling did_interrupt() and phy_clear_interrupt(). As part of the fix make it a requirement that did_interrupt() clears the interrupt. The Fixes tag refers to the first commit where the patch applies cleanly. Fixes: 49644e68f472 ("net: phy: add callback for custom interrupt handler to struct phy_driver") Reported-by: Michael Walle Signed-off-by: Heiner Kallweit Signed-off-by: David S. Miller --- drivers/net/phy/phy.c | 3 ++- include/linux/phy.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d76e038cf2cb..355bfdef48d2 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -727,7 +727,8 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) phy_trigger_machine(phydev); } - if (phy_clear_interrupt(phydev)) + /* did_interrupt() may have cleared the interrupt already */ + if (!phydev->drv->did_interrupt && phy_clear_interrupt(phydev)) goto phy_err; return IRQ_HANDLED; diff --git a/include/linux/phy.h b/include/linux/phy.h index c570e162e05e..22f5e763e894 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -557,6 +557,7 @@ struct phy_driver { /* * Checks if the PHY generated an interrupt. * For multi-PHY devices with shared PHY interrupt pin + * Set interrupt bits have to be cleared. */ int (*did_interrupt)(struct phy_device *phydev); -- cgit v1.2.3 From 88ac039cbed125bd9ed132d27ec9f689c6442748 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Feb 2020 12:18:38 +0200 Subject: dmaengine: Refactor dmaengine_check_align() to be bit operations only There is no need to have branch and temporary variable in the function. Simple convert it to be a set of bit and arithmetic operations. Signed-off-by: Andy Shevchenko Reviewed-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200226101842.29426-1-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 64461fc64e1b..9f3f5582816a 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1155,14 +1155,7 @@ static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc static inline bool dmaengine_check_align(enum dmaengine_alignment align, size_t off1, size_t off2, size_t len) { - size_t mask; - - if (!align) - return true; - mask = (1 << align) - 1; - if (mask & (off1 | off2 | len)) - return false; - return true; + return !(((1 << align) - 1) & (off1 | off2 | len)); } static inline bool is_dma_copy_aligned(struct dma_device *dev, size_t off1, -- cgit v1.2.3 From 3a92063be16873a10648a81be0b1be42a9d54ee9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Feb 2020 12:18:39 +0200 Subject: dmaengine: Use negative condition for better readability When negative condition is in use we may decrease indentation level and make the main part of logic better visible. Signed-off-by: Andy Shevchenko Reviewed-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200226101842.29426-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 9f3f5582816a..ae56a91c2a05 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -618,10 +618,11 @@ static inline void dmaengine_unmap_put(struct dmaengine_unmap_data *unmap) static inline void dma_descriptor_unmap(struct dma_async_tx_descriptor *tx) { - if (tx->unmap) { - dmaengine_unmap_put(tx->unmap); - tx->unmap = NULL; - } + if (!tx->unmap) + return; + + dmaengine_unmap_put(tx->unmap); + tx->unmap = NULL; } #ifndef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH @@ -1408,11 +1409,12 @@ static inline enum dma_status dma_async_is_complete(dma_cookie_t cookie, static inline void dma_set_tx_state(struct dma_tx_state *st, dma_cookie_t last, dma_cookie_t used, u32 residue) { - if (st) { - st->last = last; - st->used = used; - st->residue = residue; - } + if (!st) + return; + + st->last = last; + st->used = used; + st->residue = residue; } #ifdef CONFIG_DMA_ENGINE @@ -1489,12 +1491,11 @@ static inline int dmaengine_desc_set_reuse(struct dma_async_tx_descriptor *tx) if (ret) return ret; - if (caps.descriptor_reuse) { - tx->flags |= DMA_CTRL_REUSE; - return 0; - } else { + if (!caps.descriptor_reuse) return -EPERM; - } + + tx->flags |= DMA_CTRL_REUSE; + return 0; } static inline void dmaengine_desc_clear_reuse(struct dma_async_tx_descriptor *tx) @@ -1510,10 +1511,10 @@ static inline bool dmaengine_desc_test_reuse(struct dma_async_tx_descriptor *tx) static inline int dmaengine_desc_free(struct dma_async_tx_descriptor *desc) { /* this is supported for reusable desc, so check that */ - if (dmaengine_desc_test_reuse(desc)) - return desc->desc_free(desc); - else + if (!dmaengine_desc_test_reuse(desc)) return -EPERM; + + return desc->desc_free(desc); } /* --- DMA device --- */ -- cgit v1.2.3 From 5f77dd850c0a32d4d5047d139077718ee7f1a8fe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Feb 2020 12:18:40 +0200 Subject: dmaengine: Drop redundant 'else' keyword It's obvious that 'else' keyword is redundant in the code like if (foo) return bar; else if (baz) ... Drop it for good. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200226101842.29426-3-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index ae56a91c2a05..1bb5477ef7ec 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1230,9 +1230,9 @@ static inline int dma_maxpq(struct dma_device *dma, enum dma_ctrl_flags flags) { if (dma_dev_has_pq_continue(dma) || !dmaf_continue(flags)) return dma_dev_to_maxpq(dma); - else if (dmaf_p_disabled_continue(flags)) + if (dmaf_p_disabled_continue(flags)) return dma_dev_to_maxpq(dma) - 1; - else if (dmaf_continue(flags)) + if (dmaf_continue(flags)) return dma_dev_to_maxpq(dma) - 3; BUG(); } @@ -1243,7 +1243,7 @@ static inline size_t dmaengine_get_icg(bool inc, bool sgl, size_t icg, if (inc) { if (dir_icg) return dir_icg; - else if (sgl) + if (sgl) return icg; } -- cgit v1.2.3 From 1873300afa6147a1882aeba1e8bc9a13c5487571 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 26 Feb 2020 12:18:41 +0200 Subject: dmaengine: consistently return string literal from switch-case There is no need to have 'break;' statement in the default case followed by return certain string literal when all other cases have returned the string literals. So, refactor it accordingly. Signed-off-by: Andy Shevchenko Reviewed-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200226101842.29426-4-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 1bb5477ef7ec..d3672f065a64 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -1560,9 +1560,7 @@ dmaengine_get_direction_text(enum dma_transfer_direction dir) case DMA_DEV_TO_DEV: return "DEV_TO_DEV"; default: - break; + return "invalid"; } - - return "invalid"; } #endif /* DMAENGINE_H */ -- cgit v1.2.3 From 5e815fe05d0b3e4e4fcde2e09d5f362b3d65010f Mon Sep 17 00:00:00 2001 From: Jonas Karlman Date: Thu, 20 Feb 2020 17:30:11 +0100 Subject: media: uapi: h264: Add DPB entry field reference flags Using the field information attached to v4l2 buffers is not enough to determine the type of field referenced by a DPB entry: the decoded frame might contain the full picture (both top and bottom fields) but the reference only point to one of them. Let's add new V4L2_H264_DPB_ENTRY_FLAG_ flags to express that. [Keep only 2 flags and add some details about they mean] Signed-off-by: Jonas Karlman Signed-off-by: Boris Brezillon Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/v4l/ext-ctrls-codec.rst | 16 ++++++++++++++++ include/media/h264-ctrls.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst index 28313c0f4e7c..d4fc5f25aa14 100644 --- a/Documentation/media/uapi/v4l/ext-ctrls-codec.rst +++ b/Documentation/media/uapi/v4l/ext-ctrls-codec.rst @@ -2028,6 +2028,22 @@ enum v4l2_mpeg_video_h264_hierarchical_coding_type - * - ``V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM`` - 0x00000004 - The DPB entry is a long term reference frame + * - ``V4L2_H264_DPB_ENTRY_FLAG_FIELD`` + - 0x00000008 + - The DPB entry is a field reference, which means only one of the field + will be used when decoding the new frame/field. When not set the DPB + entry is a frame reference (both fields will be used). Note that this + flag does not say anything about the number of fields contained in the + reference frame, it just describes the one used to decode the new + field/frame + * - ``V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD`` + - 0x00000010 + - The DPB entry is a bottom field reference (only the bottom field of the + reference frame is needed to decode the new frame/field). Only valid if + V4L2_H264_DPB_ENTRY_FLAG_FIELD is set. When + V4L2_H264_DPB_ENTRY_FLAG_FIELD is set but + V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD is not, that means the + DPB entry is a top field reference ``V4L2_CID_MPEG_VIDEO_H264_DECODE_MODE (enum)`` Specifies the decoding mode to use. Currently exposes slice-based and diff --git a/include/media/h264-ctrls.h b/include/media/h264-ctrls.h index e877bf1d537c..1c6ff7d63bca 100644 --- a/include/media/h264-ctrls.h +++ b/include/media/h264-ctrls.h @@ -185,6 +185,8 @@ struct v4l2_ctrl_h264_slice_params { #define V4L2_H264_DPB_ENTRY_FLAG_VALID 0x01 #define V4L2_H264_DPB_ENTRY_FLAG_ACTIVE 0x02 #define V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM 0x04 +#define V4L2_H264_DPB_ENTRY_FLAG_FIELD 0x08 +#define V4L2_H264_DPB_ENTRY_FLAG_BOTTOM_FIELD 0x10 struct v4l2_h264_dpb_entry { __u64 reference_ts; -- cgit v1.2.3 From 8fd390b89cc8ca786bc1b66d8a76512a6068b081 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Fri, 24 Jan 2020 21:35:43 +0100 Subject: media: Split v4l2_pipeline_pm_use into v4l2_pipeline_pm_{get, put} Currently, v4l2_pipeline_pm_use() prototype is: int v4l2_pipeline_pm_use(struct media_entity *entity, int use) Where the 'use' argument shall only be set to '1' for enable/power-on, or to '0' for disable/power-off. The integer return is specified as only meaningful when 'use' is set to '1'. Let's enforce this semantic by splitting the function in two: v4l2_pipeline_pm_get and v4l2_pipeline_pm_put. This is done for several reasons. It makes the API easier to use (or harder to misuse). It removes the constraint on the values the 'use' argument shall take. Also, it removes the need to constraint the return value, by making v4l2_pipeline_pm_put void return. And last, it's more consistent with other kernel APIs, such as the runtime pm APIs, which makes the code more symmetric. Signed-off-by: Ezequiel Garcia Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/kapi/csi2.rst | 2 +- drivers/media/platform/omap3isp/ispvideo.c | 4 +-- drivers/media/platform/qcom/camss/camss-video.c | 4 +-- drivers/media/platform/rcar-vin/rcar-v4l2.c | 6 ++-- .../media/platform/sunxi/sun4i-csi/sun4i_v4l2.c | 6 ++-- .../media/platform/sunxi/sun6i-csi/sun6i_video.c | 4 +-- drivers/media/v4l2-core/v4l2-mc.c | 18 ++++++++-- drivers/staging/media/imx/imx-media-capture.c | 4 +-- drivers/staging/media/omap4iss/iss_video.c | 4 +-- drivers/staging/media/rkisp1/rkisp1-capture.c | 9 ++--- include/media/v4l2-mc.h | 40 ++++++++++++++-------- 11 files changed, 60 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/Documentation/media/kapi/csi2.rst b/Documentation/media/kapi/csi2.rst index 030a5c41ec75..e111ff7bfd3d 100644 --- a/Documentation/media/kapi/csi2.rst +++ b/Documentation/media/kapi/csi2.rst @@ -74,7 +74,7 @@ Before the receiver driver may enable the CSI-2 transmitter by using the :c:type:`v4l2_subdev_video_ops`->s_stream(), it must have powered the transmitter up by using the :c:type:`v4l2_subdev_core_ops`->s_power() callback. This may take -place either indirectly by using :c:func:`v4l2_pipeline_pm_use` or +place either indirectly by using :c:func:`v4l2_pipeline_pm_get` or directly. Formats diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index 0ed250ffd95b..6f769c527fae 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -1311,7 +1311,7 @@ static int isp_video_open(struct file *file) goto done; } - ret = v4l2_pipeline_pm_use(&video->video.entity, 1); + ret = v4l2_pipeline_pm_get(&video->video.entity); if (ret < 0) { omap3isp_put(video->isp); goto done; @@ -1363,7 +1363,7 @@ static int isp_video_release(struct file *file) vb2_queue_release(&handle->queue); mutex_unlock(&video->queue_lock); - v4l2_pipeline_pm_use(&video->video.entity, 0); + v4l2_pipeline_pm_put(&video->video.entity); /* Release the file handle. */ v4l2_fh_del(vfh); diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index c4220778ad47..cdbd6dba1122 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -745,7 +745,7 @@ static int video_open(struct file *file) file->private_data = vfh; - ret = v4l2_pipeline_pm_use(&vdev->entity, 1); + ret = v4l2_pipeline_pm_get(&vdev->entity); if (ret < 0) { dev_err(video->camss->dev, "Failed to power up pipeline: %d\n", ret); @@ -771,7 +771,7 @@ static int video_release(struct file *file) vb2_fop_release(file); - v4l2_pipeline_pm_use(&vdev->entity, 0); + v4l2_pipeline_pm_put(&vdev->entity); file->private_data = NULL; diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/rcar-vin/rcar-v4l2.c index b12b3f88836a..5151a3cd8a6e 100644 --- a/drivers/media/platform/rcar-vin/rcar-v4l2.c +++ b/drivers/media/platform/rcar-vin/rcar-v4l2.c @@ -842,7 +842,7 @@ static int rvin_open(struct file *file) goto err_unlock; if (vin->info->use_mc) - ret = v4l2_pipeline_pm_use(&vin->vdev.entity, 1); + ret = v4l2_pipeline_pm_get(&vin->vdev.entity); else if (v4l2_fh_is_singular_file(file)) ret = rvin_power_parallel(vin, true); @@ -858,7 +858,7 @@ static int rvin_open(struct file *file) return 0; err_power: if (vin->info->use_mc) - v4l2_pipeline_pm_use(&vin->vdev.entity, 0); + v4l2_pipeline_pm_put(&vin->vdev.entity); else if (v4l2_fh_is_singular_file(file)) rvin_power_parallel(vin, false); err_open: @@ -886,7 +886,7 @@ static int rvin_release(struct file *file) ret = _vb2_fop_release(file, NULL); if (vin->info->use_mc) { - v4l2_pipeline_pm_use(&vin->vdev.entity, 0); + v4l2_pipeline_pm_put(&vin->vdev.entity); } else { if (fh_singular) rvin_power_parallel(vin, false); diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c index 5aee3fd875ad..1721e5aee9c6 100644 --- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c +++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c @@ -214,7 +214,7 @@ static int sun4i_csi_open(struct file *file) if (ret < 0) goto err_pm_put; - ret = v4l2_pipeline_pm_use(&csi->vdev.entity, 1); + ret = v4l2_pipeline_pm_get(&csi->vdev.entity); if (ret) goto err_pm_put; @@ -227,7 +227,7 @@ static int sun4i_csi_open(struct file *file) return 0; err_pipeline_pm_put: - v4l2_pipeline_pm_use(&csi->vdev.entity, 0); + v4l2_pipeline_pm_put(&csi->vdev.entity); err_pm_put: pm_runtime_put(csi->dev); @@ -243,7 +243,7 @@ static int sun4i_csi_release(struct file *file) mutex_lock(&csi->lock); v4l2_fh_release(file); - v4l2_pipeline_pm_use(&csi->vdev.entity, 0); + v4l2_pipeline_pm_put(&csi->vdev.entity); pm_runtime_put(csi->dev); mutex_unlock(&csi->lock); diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c index cb30efb5d1f1..d9648b2810b9 100644 --- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c +++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c @@ -474,7 +474,7 @@ static int sun6i_video_open(struct file *file) if (ret < 0) goto unlock; - ret = v4l2_pipeline_pm_use(&video->vdev.entity, 1); + ret = v4l2_pipeline_pm_get(&video->vdev.entity); if (ret < 0) goto fh_release; @@ -507,7 +507,7 @@ static int sun6i_video_close(struct file *file) _vb2_fop_release(file, NULL); - v4l2_pipeline_pm_use(&video->vdev.entity, 0); + v4l2_pipeline_pm_put(&video->vdev.entity); if (last_fh) sun6i_csi_set_power(video->csi, false); diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c index 014a2a97cadd..0fffdd3ce6a4 100644 --- a/drivers/media/v4l2-core/v4l2-mc.c +++ b/drivers/media/v4l2-core/v4l2-mc.c @@ -321,7 +321,7 @@ EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source); * use_count field stores the total number of users of all video device nodes * in the pipeline. * - * The v4l2_pipeline_pm_use() function must be called in the open() and + * The v4l2_pipeline_pm_{get, put}() functions must be called in the open() and * close() handlers of video device nodes. It increments or decrements the use * count of all subdev entities in the pipeline. * @@ -423,7 +423,7 @@ static int pipeline_pm_power(struct media_entity *entity, int change, return ret; } -int v4l2_pipeline_pm_use(struct media_entity *entity, int use) +static int v4l2_pipeline_pm_use(struct media_entity *entity, unsigned int use) { struct media_device *mdev = entity->graph_obj.mdev; int change = use ? 1 : -1; @@ -444,7 +444,19 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use) return ret; } -EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_use); + +int v4l2_pipeline_pm_get(struct media_entity *entity) +{ + return v4l2_pipeline_pm_use(entity, 1); +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_get); + +void v4l2_pipeline_pm_put(struct media_entity *entity) +{ + /* Powering off entities shouldn't fail. */ + WARN_ON(v4l2_pipeline_pm_use(entity, 0)); +} +EXPORT_SYMBOL_GPL(v4l2_pipeline_pm_put); int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c index 0dd6a2fafd89..d37b776ff86d 100644 --- a/drivers/staging/media/imx/imx-media-capture.c +++ b/drivers/staging/media/imx/imx-media-capture.c @@ -643,7 +643,7 @@ static int capture_open(struct file *file) if (ret) v4l2_err(priv->src_sd, "v4l2_fh_open failed\n"); - ret = v4l2_pipeline_pm_use(&vfd->entity, 1); + ret = v4l2_pipeline_pm_get(&vfd->entity); if (ret) v4l2_fh_release(file); @@ -664,7 +664,7 @@ static int capture_release(struct file *file) vq->owner = NULL; } - v4l2_pipeline_pm_use(&vfd->entity, 0); + v4l2_pipeline_pm_put(&vfd->entity); v4l2_fh_release(file); mutex_unlock(&priv->mutex); diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index bf7e999998c3..66975a37dc85 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -1111,7 +1111,7 @@ static int iss_video_open(struct file *file) goto done; } - ret = v4l2_pipeline_pm_use(&video->video.entity, 1); + ret = v4l2_pipeline_pm_get(&video->video.entity); if (ret < 0) { omap4iss_put(video->iss); goto done; @@ -1160,7 +1160,7 @@ static int iss_video_release(struct file *file) /* Disable streaming and free the buffers queue resources. */ iss_video_streamoff(file, vfh, video->type); - v4l2_pipeline_pm_use(&video->video.entity, 0); + v4l2_pipeline_pm_put(&video->video.entity); /* Release the videobuf2 queue */ vb2_queue_release(&handle->queue); diff --git a/drivers/staging/media/rkisp1/rkisp1-capture.c b/drivers/staging/media/rkisp1/rkisp1-capture.c index 7cc2ace193d1..026470c7b43d 100644 --- a/drivers/staging/media/rkisp1/rkisp1-capture.c +++ b/drivers/staging/media/rkisp1/rkisp1-capture.c @@ -937,10 +937,7 @@ static void rkisp1_vb2_stop_streaming(struct vb2_queue *queue) rkisp1_return_all_buffers(cap, VB2_BUF_STATE_ERROR); - ret = v4l2_pipeline_pm_use(&node->vdev.entity, 0); - if (ret) - dev_err(rkisp1->dev, "pipeline close failed error:%d\n", ret); - + v4l2_pipeline_pm_put(&node->vdev.entity); ret = pm_runtime_put(rkisp1->dev); if (ret) dev_err(rkisp1->dev, "power down failed error:%d\n", ret); @@ -999,7 +996,7 @@ rkisp1_vb2_start_streaming(struct vb2_queue *queue, unsigned int count) dev_err(cap->rkisp1->dev, "power up failed %d\n", ret); goto err_destroy_dummy; } - ret = v4l2_pipeline_pm_use(entity, 1); + ret = v4l2_pipeline_pm_get(entity); if (ret) { dev_err(cap->rkisp1->dev, "open cif pipeline failed %d\n", ret); goto err_pipe_pm_put; @@ -1025,7 +1022,7 @@ err_pipe_disable: rkisp1_pipeline_sink_walk(entity, NULL, rkisp1_pipeline_disable_cb); err_stop_stream: rkisp1_stream_stop(cap); - v4l2_pipeline_pm_use(entity, 0); + v4l2_pipeline_pm_put(entity); err_pipe_pm_put: pm_runtime_put(cap->rkisp1->dev); err_destroy_dummy: diff --git a/include/media/v4l2-mc.h b/include/media/v4l2-mc.h index 384960249f01..5e73eb8e28f6 100644 --- a/include/media/v4l2-mc.h +++ b/include/media/v4l2-mc.h @@ -86,23 +86,30 @@ int v4l_vb2q_enable_media_source(struct vb2_queue *q); /** - * v4l2_pipeline_pm_use - Update the use count of an entity - * @entity: The entity - * @use: Use (1) or stop using (0) the entity + * v4l2_pipeline_pm_get - Increase the use count of a pipeline + * @entity: The root entity of a pipeline * - * Update the use count of all entities in the pipeline and power entities on or - * off accordingly. + * Update the use count of all entities in the pipeline and power entities on. * - * This function is intended to be called in video node open (use == - * 1) and release (use == 0). It uses struct media_entity.use_count to - * track the power status. The use of this function should be paired - * with v4l2_pipeline_link_notify(). + * This function is intended to be called in video node open. It uses + * struct media_entity.use_count to track the power status. The use + * of this function should be paired with v4l2_pipeline_link_notify(). * - * Return 0 on success or a negative error code on failure. Powering entities - * off is assumed to never fail. No failure can occur when the use parameter is - * set to 0. + * Return 0 on success or a negative error code on failure. */ -int v4l2_pipeline_pm_use(struct media_entity *entity, int use); +int v4l2_pipeline_pm_get(struct media_entity *entity); + +/** + * v4l2_pipeline_pm_put - Decrease the use count of a pipeline + * @entity: The root entity of a pipeline + * + * Update the use count of all entities in the pipeline and power entities off. + * + * This function is intended to be called in video node release. It uses + * struct media_entity.use_count to track the power status. The use + * of this function should be paired with v4l2_pipeline_link_notify(). + */ +void v4l2_pipeline_pm_put(struct media_entity *entity); /** @@ -114,7 +121,7 @@ int v4l2_pipeline_pm_use(struct media_entity *entity, int use); * React to link management on powered pipelines by updating the use count of * all entities in the source and sink sides of the link. Entities are powered * on or off accordingly. The use of this function should be paired - * with v4l2_pipeline_pm_use(). + * with v4l2_pipeline_pm_{get,put}(). * * Return 0 on success or a negative error code on failure. Powering entities * off is assumed to never fail. This function will not fail for disconnection @@ -144,11 +151,14 @@ static inline int v4l_vb2q_enable_media_source(struct vb2_queue *q) return 0; } -static inline int v4l2_pipeline_pm_use(struct media_entity *entity, int use) +static inline int v4l2_pipeline_pm_get(struct media_entity *entity) { return 0; } +static inline void v4l2_pipeline_pm_put(struct media_entity *entity) +{} + static inline int v4l2_pipeline_link_notify(struct media_link *link, u32 flags, unsigned int notification) { -- cgit v1.2.3 From 1a2289fdf678b780b2d68f408523c09b7074982e Mon Sep 17 00:00:00 2001 From: Tomasz Lauda Date: Fri, 28 Feb 2020 17:18:50 -0600 Subject: ASoC: SOF: add core id to sof_ipc_comp Adds core id to sof_ipc_comp. The intention of this change is to inform FW on which core that particular component should run. Right now core id is only passed when pipeline is created, which is not flexible enough and doesn't allow for FW to handle this the right way. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Tomasz Lauda Link: https://lore.kernel.org/r/20200228231850.9226-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/topology.h | 3 ++- include/uapi/sound/sof/abi.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/sof/topology.h b/include/sound/sof/topology.h index 8e76178fedf0..402e0250c508 100644 --- a/include/sound/sof/topology.h +++ b/include/sound/sof/topology.h @@ -53,9 +53,10 @@ struct sof_ipc_comp { uint32_t id; enum sof_comp_type type; uint32_t pipeline_id; + uint32_t core; /* reserved for future use */ - uint32_t reserved[2]; + uint32_t reserved[1]; } __packed; /* diff --git a/include/uapi/sound/sof/abi.h b/include/uapi/sound/sof/abi.h index c0ef1643c753..5995b79d6df1 100644 --- a/include/uapi/sound/sof/abi.h +++ b/include/uapi/sound/sof/abi.h @@ -26,7 +26,7 @@ /* SOF ABI version major, minor and patch numbers */ #define SOF_ABI_MAJOR 3 -#define SOF_ABI_MINOR 12 +#define SOF_ABI_MINOR 13 #define SOF_ABI_PATCH 0 /* SOF ABI version number. Format within 32bit word is MMmmmppp */ -- cgit v1.2.3 From 1583c793af736c8d33bf4aa6054ddc5c86e5a6c7 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 20 Feb 2020 10:34:26 +0100 Subject: media: videobuf2-core.h: improve buf_struct_size documentation Explicitly document that the driver-specific buffer structure must start with the subsystem-specific struct (vb2_v4l2_buffer in the case of V4L2). Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/videobuf2-core.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index a2b2208b02da..f11b96514cf7 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -509,8 +509,11 @@ struct vb2_buf_ops { * by the vb2 core. * @buf_struct_size: size of the driver-specific buffer structure; * "0" indicates the driver doesn't want to use a custom buffer - * structure type. for example, ``sizeof(struct vb2_v4l2_buffer)`` - * will be used for v4l2. + * structure type. In that case a subsystem-specific struct + * will be used (in the case of V4L2 that is + * ``sizeof(struct vb2_v4l2_buffer)``). The first field of the + * driver-specific buffer structure must be the subsystem-specific + * struct (vb2_v4l2_buffer in the case of V4L2). * @timestamp_flags: Timestamp flags; ``V4L2_BUF_FLAG_TIMESTAMP_*`` and * ``V4L2_BUF_FLAG_TSTAMP_SRC_*`` * @gfp_flags: additional gfp flags used when allocating the buffers. -- cgit v1.2.3 From 02d715b4a8182f4887d82df82a7b83aced647760 Mon Sep 17 00:00:00 2001 From: Amol Grover Date: Sun, 23 Feb 2020 22:25:39 +0530 Subject: iommu/vt-d: Fix RCU list debugging warnings dmar_drhd_units is traversed using list_for_each_entry_rcu() outside of an RCU read side critical section but under the protection of dmar_global_lock. Hence add corresponding lockdep expression to silence the following false-positive warnings: [ 1.603975] ============================= [ 1.603976] WARNING: suspicious RCU usage [ 1.603977] 5.5.4-stable #17 Not tainted [ 1.603978] ----------------------------- [ 1.603980] drivers/iommu/intel-iommu.c:4769 RCU-list traversed in non-reader section!! [ 1.603869] ============================= [ 1.603870] WARNING: suspicious RCU usage [ 1.603872] 5.5.4-stable #17 Not tainted [ 1.603874] ----------------------------- [ 1.603875] drivers/iommu/dmar.c:293 RCU-list traversed in non-reader section!! Tested-by: Madhuparna Bhowmik Signed-off-by: Amol Grover Cc: stable@vger.kernel.org Acked-by: Lu Baolu Signed-off-by: Joerg Roedel --- include/linux/dmar.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/dmar.h b/include/linux/dmar.h index f64ca27dc210..712be8bc6a7c 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -69,8 +69,9 @@ struct dmar_pci_notify_info { extern struct rw_semaphore dmar_global_lock; extern struct list_head dmar_drhd_units; -#define for_each_drhd_unit(drhd) \ - list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) +#define for_each_drhd_unit(drhd) \ + list_for_each_entry_rcu(drhd, &dmar_drhd_units, list, \ + dmar_rcu_check()) #define for_each_active_drhd_unit(drhd) \ list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \ @@ -81,7 +82,8 @@ extern struct list_head dmar_drhd_units; if (i=drhd->iommu, drhd->ignored) {} else #define for_each_iommu(i, drhd) \ - list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \ + list_for_each_entry_rcu(drhd, &dmar_drhd_units, list, \ + dmar_rcu_check()) \ if (i=drhd->iommu, 0) {} else static inline bool dmar_rcu_check(void) -- cgit v1.2.3 From bb4cf02d4c74f0db60f58c406ddfdfed16d14f84 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 2 Mar 2020 05:59:33 -0600 Subject: netdevice: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6c3f7032e8d9..b6fedd54cd8e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -664,7 +664,7 @@ static inline void netdev_queue_numa_node_write(struct netdev_queue *q, int node struct rps_map { unsigned int len; struct rcu_head rcu; - u16 cpus[0]; + u16 cpus[]; }; #define RPS_MAP_SIZE(_num) (sizeof(struct rps_map) + ((_num) * sizeof(u16))) @@ -686,7 +686,7 @@ struct rps_dev_flow { struct rps_dev_flow_table { unsigned int mask; struct rcu_head rcu; - struct rps_dev_flow flows[0]; + struct rps_dev_flow flows[]; }; #define RPS_DEV_FLOW_TABLE_SIZE(_num) (sizeof(struct rps_dev_flow_table) + \ ((_num) * sizeof(struct rps_dev_flow))) @@ -704,7 +704,7 @@ struct rps_dev_flow_table { struct rps_sock_flow_table { u32 mask; - u32 ents[0] ____cacheline_aligned_in_smp; + u32 ents[] ____cacheline_aligned_in_smp; }; #define RPS_SOCK_FLOW_TABLE_SIZE(_num) (offsetof(struct rps_sock_flow_table, ents[_num])) @@ -767,7 +767,7 @@ struct xps_map { unsigned int len; unsigned int alloc_len; struct rcu_head rcu; - u16 queues[0]; + u16 queues[]; }; #define XPS_MAP_SIZE(_num) (sizeof(struct xps_map) + ((_num) * sizeof(u16))) #define XPS_MIN_MAP_ALLOC ((L1_CACHE_ALIGN(offsetof(struct xps_map, queues[1])) \ @@ -778,7 +778,7 @@ struct xps_map { */ struct xps_dev_maps { struct rcu_head rcu; - struct xps_map __rcu *attr_map[0]; /* Either CPUs map or RXQs map */ + struct xps_map __rcu *attr_map[]; /* Either CPUs map or RXQs map */ }; #define XPS_CPU_DEV_MAPS_SIZE(_tcs) (sizeof(struct xps_dev_maps) + \ -- cgit v1.2.3 From 2e83abdcb30e3881e9569fd8f4a600beef6cfadc Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 2 Mar 2020 06:00:48 -0600 Subject: net: mip6: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/mip6.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/mip6.h b/include/net/mip6.h index f1c28971c362..67cd7e50804c 100644 --- a/include/net/mip6.h +++ b/include/net/mip6.h @@ -25,7 +25,7 @@ struct ip6_mh { __u8 ip6mh_reserved; __u16 ip6mh_cksum; /* Followed by type specific messages */ - __u8 data[0]; + __u8 data[]; } __packed; #define IP6_MH_TYPE_BRR 0 /* Binding Refresh Request */ -- cgit v1.2.3 From 1776658da830eb024b63ac34167dfd4bd6c21cf9 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 2 Mar 2020 06:02:09 -0600 Subject: drop_monitor: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/uapi/linux/net_dropmon.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/net_dropmon.h b/include/uapi/linux/net_dropmon.h index 66048cc5d7b3..67e31f329190 100644 --- a/include/uapi/linux/net_dropmon.h +++ b/include/uapi/linux/net_dropmon.h @@ -29,12 +29,12 @@ struct net_dm_config_entry { struct net_dm_config_msg { __u32 entries; - struct net_dm_config_entry options[0]; + struct net_dm_config_entry options[]; }; struct net_dm_alert_msg { __u32 entries; - struct net_dm_drop_point points[0]; + struct net_dm_drop_point points[]; }; struct net_dm_user_msg { -- cgit v1.2.3 From a53110609c728fc0316cc09fee7ea17ebf93c684 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 2 Mar 2020 06:03:52 -0600 Subject: net: ip_fib: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/ip_fib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 6a1ae49809de..dabe398bee4c 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -153,7 +153,7 @@ struct fib_info { bool nh_updated; struct nexthop *nh; struct rcu_head rcu; - struct fib_nh fib_nh[0]; + struct fib_nh fib_nh[]; }; @@ -250,7 +250,7 @@ struct fib_table { int tb_num_default; struct rcu_head rcu; unsigned long *tb_data; - unsigned long __data[0]; + unsigned long __data[]; }; struct fib_dump_filter { -- cgit v1.2.3 From 6e68f499e93429e521af40cb25129f27c6c80a10 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 2 Mar 2020 06:06:07 -0600 Subject: net: ip6_fib: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index fd60a8ac02ee..6ec26e4d7f11 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -198,7 +198,7 @@ struct fib6_info { struct rcu_head rcu; struct nexthop *nh; - struct fib6_nh fib6_nh[0]; + struct fib6_nh fib6_nh[]; }; struct rt6_info { -- cgit v1.2.3 From 48b77df665e0a268b61a795208e647b6b7bb7e00 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 2 Mar 2020 06:07:42 -0600 Subject: net: inet_sock: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Signed-off-by: David S. Miller --- include/net/inet_sock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 34c4436fd18f..a7ce00af6c44 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -52,7 +52,7 @@ struct ip_options { unsigned char router_alert; unsigned char cipso; unsigned char __pad2; - unsigned char __data[0]; + unsigned char __data[]; }; struct ip_options_rcu { -- cgit v1.2.3 From 444ebb5768c5c43aadfc60111fecd6c4f946e77b Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Mon, 24 Feb 2020 11:32:43 +0300 Subject: splice: make do_splice public Make do_splice(), so other kernel parts can reuse it Signed-off-by: Pavel Begunkov Signed-off-by: Jens Axboe --- fs/splice.c | 6 +++--- include/linux/splice.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/splice.c b/fs/splice.c index d671936d0aad..4735defc46ee 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -1109,9 +1109,9 @@ static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, /* * Determine where to splice to/from. */ -static long do_splice(struct file *in, loff_t __user *off_in, - struct file *out, loff_t __user *off_out, - size_t len, unsigned int flags) +long do_splice(struct file *in, loff_t __user *off_in, + struct file *out, loff_t __user *off_out, + size_t len, unsigned int flags) { struct pipe_inode_info *ipipe; struct pipe_inode_info *opipe; diff --git a/include/linux/splice.h b/include/linux/splice.h index 74b4911ac16d..ebbbfea48aa0 100644 --- a/include/linux/splice.h +++ b/include/linux/splice.h @@ -78,6 +78,9 @@ extern ssize_t add_to_pipe(struct pipe_inode_info *, struct pipe_buffer *); extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *, splice_direct_actor *); +extern long do_splice(struct file *in, loff_t __user *off_in, + struct file *out, loff_t __user *off_out, + size_t len, unsigned int flags); /* * for dynamic pipe sizing -- cgit v1.2.3 From 7d67af2c013402537385dae343a2d0f6a4cb3bfd Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Mon, 24 Feb 2020 11:32:45 +0300 Subject: io_uring: add splice(2) support Add support for splice(2). - output file is specified as sqe->fd, so it's handled by generic code - hash_reg_file handled by generic code as well - len is 32bit, but should be fine - the fd_in is registered file, when SPLICE_F_FD_IN_FIXED is set, which is a splice flag (i.e. sqe->splice_flags). Signed-off-by: Pavel Begunkov Signed-off-by: Jens Axboe --- fs/io_uring.c | 109 ++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/io_uring.h | 14 +++++- 2 files changed, 122 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/io_uring.c b/fs/io_uring.c index 1a3de7337274..1ef20a2af10b 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -76,6 +76,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -428,6 +429,15 @@ struct io_epoll { struct epoll_event event; }; +struct io_splice { + struct file *file_out; + struct file *file_in; + loff_t off_out; + loff_t off_in; + u64 len; + unsigned int flags; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -544,6 +554,7 @@ struct io_kiocb { struct io_fadvise fadvise; struct io_madvise madvise; struct io_epoll epoll; + struct io_splice splice; }; struct io_async_ctx *io; @@ -744,6 +755,11 @@ static const struct io_op_def io_op_defs[] = { .unbound_nonreg_file = 1, .file_table = 1, }, + [IORING_OP_SPLICE] = { + .needs_file = 1, + .hash_reg_file = 1, + .unbound_nonreg_file = 1, + } }; static void io_wq_submit_work(struct io_wq_work **workptr); @@ -758,6 +774,10 @@ static int __io_sqe_files_update(struct io_ring_ctx *ctx, static int io_grab_files(struct io_kiocb *req); static void io_ring_file_ref_flush(struct fixed_file_data *data); static void io_cleanup_req(struct io_kiocb *req); +static int io_file_get(struct io_submit_state *state, + struct io_kiocb *req, + int fd, struct file **out_file, + bool fixed); static struct kmem_cache *req_cachep; @@ -2404,6 +2424,77 @@ out_free: return ret; } +static int io_splice_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) +{ + struct io_splice* sp = &req->splice; + unsigned int valid_flags = SPLICE_F_FD_IN_FIXED | SPLICE_F_ALL; + int ret; + + if (req->flags & REQ_F_NEED_CLEANUP) + return 0; + + sp->file_in = NULL; + sp->off_in = READ_ONCE(sqe->splice_off_in); + sp->off_out = READ_ONCE(sqe->off); + sp->len = READ_ONCE(sqe->len); + sp->flags = READ_ONCE(sqe->splice_flags); + + if (unlikely(sp->flags & ~valid_flags)) + return -EINVAL; + + ret = io_file_get(NULL, req, READ_ONCE(sqe->splice_fd_in), &sp->file_in, + (sp->flags & SPLICE_F_FD_IN_FIXED)); + if (ret) + return ret; + req->flags |= REQ_F_NEED_CLEANUP; + + if (!S_ISREG(file_inode(sp->file_in)->i_mode)) + req->work.flags |= IO_WQ_WORK_UNBOUND; + + return 0; +} + +static bool io_splice_punt(struct file *file) +{ + if (get_pipe_info(file)) + return false; + if (!io_file_supports_async(file)) + return true; + return !(file->f_mode & O_NONBLOCK); +} + +static int io_splice(struct io_kiocb *req, struct io_kiocb **nxt, + bool force_nonblock) +{ + struct io_splice *sp = &req->splice; + struct file *in = sp->file_in; + struct file *out = sp->file_out; + unsigned int flags = sp->flags & ~SPLICE_F_FD_IN_FIXED; + loff_t *poff_in, *poff_out; + long ret; + + if (force_nonblock) { + if (io_splice_punt(in) || io_splice_punt(out)) + return -EAGAIN; + flags |= SPLICE_F_NONBLOCK; + } + + poff_in = (sp->off_in == -1) ? NULL : &sp->off_in; + poff_out = (sp->off_out == -1) ? NULL : &sp->off_out; + ret = do_splice(in, poff_in, out, poff_out, sp->len, flags); + if (force_nonblock && ret == -EAGAIN) + return -EAGAIN; + + io_put_file(req, in, (sp->flags & SPLICE_F_FD_IN_FIXED)); + req->flags &= ~REQ_F_NEED_CLEANUP; + + io_cqring_add_event(req, ret); + if (ret != sp->len) + req_set_fail_links(req); + io_put_req_find_next(req, nxt); + return 0; +} + /* * IORING_OP_NOP just posts a completion event, nothing else. */ @@ -4230,6 +4321,9 @@ static int io_req_defer_prep(struct io_kiocb *req, case IORING_OP_EPOLL_CTL: ret = io_epoll_ctl_prep(req, sqe); break; + case IORING_OP_SPLICE: + ret = io_splice_prep(req, sqe); + break; default: printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", req->opcode); @@ -4292,6 +4386,10 @@ static void io_cleanup_req(struct io_kiocb *req) case IORING_OP_STATX: putname(req->open.filename); break; + case IORING_OP_SPLICE: + io_put_file(req, req->splice.file_in, + (req->splice.flags & SPLICE_F_FD_IN_FIXED)); + break; } req->flags &= ~REQ_F_NEED_CLEANUP; @@ -4495,6 +4593,14 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, } ret = io_epoll_ctl(req, nxt, force_nonblock); break; + case IORING_OP_SPLICE: + if (sqe) { + ret = io_splice_prep(req, sqe); + if (ret < 0) + break; + } + ret = io_splice(req, nxt, force_nonblock); + break; default: ret = -EINVAL; break; @@ -7230,6 +7336,7 @@ static int __init io_uring_init(void) BUILD_BUG_SQE_ELEM(8, __u64, off); BUILD_BUG_SQE_ELEM(8, __u64, addr2); BUILD_BUG_SQE_ELEM(16, __u64, addr); + BUILD_BUG_SQE_ELEM(16, __u64, splice_off_in); BUILD_BUG_SQE_ELEM(24, __u32, len); BUILD_BUG_SQE_ELEM(28, __kernel_rwf_t, rw_flags); BUILD_BUG_SQE_ELEM(28, /* compat */ int, rw_flags); @@ -7244,9 +7351,11 @@ static int __init io_uring_init(void) BUILD_BUG_SQE_ELEM(28, __u32, open_flags); BUILD_BUG_SQE_ELEM(28, __u32, statx_flags); BUILD_BUG_SQE_ELEM(28, __u32, fadvise_advice); + BUILD_BUG_SQE_ELEM(28, __u32, splice_flags); BUILD_BUG_SQE_ELEM(32, __u64, user_data); BUILD_BUG_SQE_ELEM(40, __u16, buf_index); BUILD_BUG_SQE_ELEM(42, __u16, personality); + BUILD_BUG_SQE_ELEM(44, __s32, splice_fd_in); BUILD_BUG_ON(ARRAY_SIZE(io_op_defs) != IORING_OP_LAST); req_cachep = KMEM_CACHE(io_kiocb, SLAB_HWCACHE_ALIGN | SLAB_PANIC); diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 3f7961c1c243..08891cc1c1e7 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -23,7 +23,10 @@ struct io_uring_sqe { __u64 off; /* offset into file */ __u64 addr2; }; - __u64 addr; /* pointer to buffer or iovecs */ + union { + __u64 addr; /* pointer to buffer or iovecs */ + __u64 splice_off_in; + }; __u32 len; /* buffer size or number of iovecs */ union { __kernel_rwf_t rw_flags; @@ -37,6 +40,7 @@ struct io_uring_sqe { __u32 open_flags; __u32 statx_flags; __u32 fadvise_advice; + __u32 splice_flags; }; __u64 user_data; /* data to be passed back at completion time */ union { @@ -45,6 +49,7 @@ struct io_uring_sqe { __u16 buf_index; /* personality to use, if used */ __u16 personality; + __s32 splice_fd_in; }; __u64 __pad2[3]; }; @@ -113,6 +118,7 @@ enum { IORING_OP_RECV, IORING_OP_OPENAT2, IORING_OP_EPOLL_CTL, + IORING_OP_SPLICE, /* this goes last, obviously */ IORING_OP_LAST, @@ -128,6 +134,12 @@ enum { */ #define IORING_TIMEOUT_ABS (1U << 0) +/* + * sqe->splice_flags + * extends splice(2) flags + */ +#define SPLICE_F_FD_IN_FIXED (1U << 31) /* the last bit of __u32 */ + /* * IO completion data structure (Completion Queue Entry) */ -- cgit v1.2.3 From d7718a9d25a61442da8ee8aeeff6a0097f0ccfd6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 14 Feb 2020 22:23:12 -0700 Subject: io_uring: use poll driven retry for files that support it Currently io_uring tries any request in a non-blocking manner, if it can, and then retries from a worker thread if we get -EAGAIN. Now that we have a new and fancy poll based retry backend, use that to retry requests if the file supports it. This means that, for example, an IORING_OP_RECVMSG on a socket no longer requires an async thread to complete the IO. If we get -EAGAIN reading from the socket in a non-blocking manner, we arm a poll handler for notification on when the socket becomes readable. When it does, the pending read is executed directly by the task again, through the io_uring task work handlers. Not only is this faster and more efficient, it also means we're not generating potentially tons of async threads that just sit and block, waiting for the IO to complete. The feature is marked with IORING_FEAT_FAST_POLL, meaning that async pollable IO is fast, and that pollother_op is fast as well. Signed-off-by: Jens Axboe --- fs/io_uring.c | 354 ++++++++++++++++++++++++++++++---------- include/trace/events/io_uring.h | 103 ++++++++++++ include/uapi/linux/io_uring.h | 1 + 3 files changed, 375 insertions(+), 83 deletions(-) (limited to 'include') diff --git a/fs/io_uring.c b/fs/io_uring.c index 0d973de75127..8c976fde40bd 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -487,6 +487,7 @@ enum { REQ_F_COMP_LOCKED_BIT, REQ_F_NEED_CLEANUP_BIT, REQ_F_OVERFLOW_BIT, + REQ_F_POLLED_BIT, }; enum { @@ -529,6 +530,13 @@ enum { REQ_F_NEED_CLEANUP = BIT(REQ_F_NEED_CLEANUP_BIT), /* in overflow list */ REQ_F_OVERFLOW = BIT(REQ_F_OVERFLOW_BIT), + /* already went through poll handler */ + REQ_F_POLLED = BIT(REQ_F_POLLED_BIT), +}; + +struct async_poll { + struct io_poll_iocb poll; + struct io_wq_work work; }; /* @@ -562,27 +570,29 @@ struct io_kiocb { u8 opcode; struct io_ring_ctx *ctx; - union { - struct list_head list; - struct hlist_node hash_node; - }; - struct list_head link_list; + struct list_head list; unsigned int flags; refcount_t refs; + struct task_struct *task; u64 user_data; u32 result; u32 sequence; + struct list_head link_list; + struct list_head inflight_entry; union { /* * Only commands that never go async can use the below fields, - * obviously. Right now only IORING_OP_POLL_ADD uses them. + * obviously. Right now only IORING_OP_POLL_ADD uses them, and + * async armed poll handlers for regular commands. The latter + * restore the work, if needed. */ struct { - struct task_struct *task; struct callback_head task_work; + struct hlist_node hash_node; + struct async_poll *apoll; }; struct io_wq_work work; }; @@ -3563,9 +3573,209 @@ out: #endif } -static bool io_poll_remove_one(struct io_kiocb *req) +struct io_poll_table { + struct poll_table_struct pt; + struct io_kiocb *req; + int error; +}; + +static void __io_queue_proc(struct io_poll_iocb *poll, struct io_poll_table *pt, + struct wait_queue_head *head) +{ + if (unlikely(poll->head)) { + pt->error = -EINVAL; + return; + } + + pt->error = 0; + poll->head = head; + add_wait_queue(head, &poll->wait); +} + +static void io_async_queue_proc(struct file *file, struct wait_queue_head *head, + struct poll_table_struct *p) +{ + struct io_poll_table *pt = container_of(p, struct io_poll_table, pt); + + __io_queue_proc(&pt->req->apoll->poll, pt, head); +} + +static int __io_async_wake(struct io_kiocb *req, struct io_poll_iocb *poll, + __poll_t mask, task_work_func_t func) +{ + struct task_struct *tsk; + + /* for instances that support it check for an event match first: */ + if (mask && !(mask & poll->events)) + return 0; + + trace_io_uring_task_add(req->ctx, req->opcode, req->user_data, mask); + + list_del_init(&poll->wait.entry); + + tsk = req->task; + req->result = mask; + init_task_work(&req->task_work, func); + /* + * If this fails, then the task is exiting. If that is the case, then + * the exit check will ultimately cancel these work items. Hence we + * don't need to check here and handle it specifically. + */ + task_work_add(tsk, &req->task_work, true); + wake_up_process(tsk); + return 1; +} + +static void io_async_task_func(struct callback_head *cb) +{ + struct io_kiocb *req = container_of(cb, struct io_kiocb, task_work); + struct async_poll *apoll = req->apoll; + struct io_ring_ctx *ctx = req->ctx; + + trace_io_uring_task_run(req->ctx, req->opcode, req->user_data); + + WARN_ON_ONCE(!list_empty(&req->apoll->poll.wait.entry)); + + if (hash_hashed(&req->hash_node)) { + spin_lock_irq(&ctx->completion_lock); + hash_del(&req->hash_node); + spin_unlock_irq(&ctx->completion_lock); + } + + /* restore ->work in case we need to retry again */ + memcpy(&req->work, &apoll->work, sizeof(req->work)); + + __set_current_state(TASK_RUNNING); + mutex_lock(&ctx->uring_lock); + __io_queue_sqe(req, NULL); + mutex_unlock(&ctx->uring_lock); + + kfree(apoll); +} + +static int io_async_wake(struct wait_queue_entry *wait, unsigned mode, int sync, + void *key) +{ + struct io_kiocb *req = wait->private; + struct io_poll_iocb *poll = &req->apoll->poll; + + trace_io_uring_poll_wake(req->ctx, req->opcode, req->user_data, + key_to_poll(key)); + + return __io_async_wake(req, poll, key_to_poll(key), io_async_task_func); +} + +static void io_poll_req_insert(struct io_kiocb *req) +{ + struct io_ring_ctx *ctx = req->ctx; + struct hlist_head *list; + + list = &ctx->cancel_hash[hash_long(req->user_data, ctx->cancel_hash_bits)]; + hlist_add_head(&req->hash_node, list); +} + +static __poll_t __io_arm_poll_handler(struct io_kiocb *req, + struct io_poll_iocb *poll, + struct io_poll_table *ipt, __poll_t mask, + wait_queue_func_t wake_func) + __acquires(&ctx->completion_lock) +{ + struct io_ring_ctx *ctx = req->ctx; + bool cancel = false; + + poll->file = req->file; + poll->head = NULL; + poll->done = poll->canceled = false; + poll->events = mask; + + ipt->pt._key = mask; + ipt->req = req; + ipt->error = -EINVAL; + + INIT_LIST_HEAD(&poll->wait.entry); + init_waitqueue_func_entry(&poll->wait, wake_func); + poll->wait.private = req; + + mask = vfs_poll(req->file, &ipt->pt) & poll->events; + + spin_lock_irq(&ctx->completion_lock); + if (likely(poll->head)) { + spin_lock(&poll->head->lock); + if (unlikely(list_empty(&poll->wait.entry))) { + if (ipt->error) + cancel = true; + ipt->error = 0; + mask = 0; + } + if (mask || ipt->error) + list_del_init(&poll->wait.entry); + else if (cancel) + WRITE_ONCE(poll->canceled, true); + else if (!poll->done) /* actually waiting for an event */ + io_poll_req_insert(req); + spin_unlock(&poll->head->lock); + } + + return mask; +} + +static bool io_arm_poll_handler(struct io_kiocb *req) +{ + const struct io_op_def *def = &io_op_defs[req->opcode]; + struct io_ring_ctx *ctx = req->ctx; + struct async_poll *apoll; + struct io_poll_table ipt; + __poll_t mask, ret; + + if (!req->file || !file_can_poll(req->file)) + return false; + if (req->flags & (REQ_F_MUST_PUNT | REQ_F_POLLED)) + return false; + if (!def->pollin && !def->pollout) + return false; + + apoll = kmalloc(sizeof(*apoll), GFP_ATOMIC); + if (unlikely(!apoll)) + return false; + + req->flags |= REQ_F_POLLED; + memcpy(&apoll->work, &req->work, sizeof(req->work)); + + /* + * Don't need a reference here, as we're adding it to the task + * task_works list. If the task exits, the list is pruned. + */ + req->task = current; + req->apoll = apoll; + INIT_HLIST_NODE(&req->hash_node); + + if (def->pollin) + mask = POLLIN | POLLRDNORM; + if (def->pollout) + mask |= POLLOUT | POLLWRNORM; + mask |= POLLERR | POLLPRI; + + ipt.pt._qproc = io_async_queue_proc; + + ret = __io_arm_poll_handler(req, &apoll->poll, &ipt, mask, + io_async_wake); + if (ret) { + ipt.error = 0; + apoll->poll.done = true; + spin_unlock_irq(&ctx->completion_lock); + memcpy(&req->work, &apoll->work, sizeof(req->work)); + kfree(apoll); + return false; + } + spin_unlock_irq(&ctx->completion_lock); + trace_io_uring_poll_arm(ctx, req->opcode, req->user_data, mask, + apoll->poll.events); + return true; +} + +static bool __io_poll_remove_one(struct io_kiocb *req, + struct io_poll_iocb *poll) { - struct io_poll_iocb *poll = &req->poll; bool do_complete = false; spin_lock(&poll->head->lock); @@ -3575,7 +3785,24 @@ static bool io_poll_remove_one(struct io_kiocb *req) do_complete = true; } spin_unlock(&poll->head->lock); + return do_complete; +} + +static bool io_poll_remove_one(struct io_kiocb *req) +{ + bool do_complete; + + if (req->opcode == IORING_OP_POLL_ADD) { + do_complete = __io_poll_remove_one(req, &req->poll); + } else { + /* non-poll requests have submit ref still */ + do_complete = __io_poll_remove_one(req, &req->apoll->poll); + if (do_complete) + io_put_req(req); + } + hash_del(&req->hash_node); + if (do_complete) { io_cqring_fill_event(req, -ECANCELED); io_commit_cqring(req->ctx); @@ -3686,8 +3913,13 @@ static void io_poll_task_func(struct callback_head *cb) struct io_kiocb *nxt = NULL; io_poll_task_handler(req, &nxt); - if (nxt) + if (nxt) { + struct io_ring_ctx *ctx = nxt->ctx; + + mutex_lock(&ctx->uring_lock); __io_queue_sqe(nxt, NULL); + mutex_unlock(&ctx->uring_lock); + } } static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, @@ -3695,51 +3927,16 @@ static int io_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, { struct io_kiocb *req = wait->private; struct io_poll_iocb *poll = &req->poll; - __poll_t mask = key_to_poll(key); - struct task_struct *tsk; - /* for instances that support it check for an event match first: */ - if (mask && !(mask & poll->events)) - return 0; - - list_del_init(&poll->wait.entry); - - tsk = req->task; - req->result = mask; - init_task_work(&req->task_work, io_poll_task_func); - task_work_add(tsk, &req->task_work, true); - wake_up_process(tsk); - return 1; + return __io_async_wake(req, poll, key_to_poll(key), io_poll_task_func); } -struct io_poll_table { - struct poll_table_struct pt; - struct io_kiocb *req; - int error; -}; - static void io_poll_queue_proc(struct file *file, struct wait_queue_head *head, struct poll_table_struct *p) { struct io_poll_table *pt = container_of(p, struct io_poll_table, pt); - if (unlikely(pt->req->poll.head)) { - pt->error = -EINVAL; - return; - } - - pt->error = 0; - pt->req->poll.head = head; - add_wait_queue(head, &pt->req->poll.wait); -} - -static void io_poll_req_insert(struct io_kiocb *req) -{ - struct io_ring_ctx *ctx = req->ctx; - struct hlist_head *list; - - list = &ctx->cancel_hash[hash_long(req->user_data, ctx->cancel_hash_bits)]; - hlist_add_head(&req->hash_node, list); + __io_queue_proc(&pt->req->poll, pt, head); } static int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) @@ -3757,7 +3954,10 @@ static int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe events = READ_ONCE(sqe->poll_events); poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP; - /* task will wait for requests on exit, don't need a ref */ + /* + * Don't need a reference here, as we're adding it to the task + * task_works list. If the task exits, the list is pruned. + */ req->task = current; return 0; } @@ -3767,46 +3967,15 @@ static int io_poll_add(struct io_kiocb *req, struct io_kiocb **nxt) struct io_poll_iocb *poll = &req->poll; struct io_ring_ctx *ctx = req->ctx; struct io_poll_table ipt; - bool cancel = false; __poll_t mask; INIT_HLIST_NODE(&req->hash_node); - - poll->head = NULL; - poll->done = false; - poll->canceled = false; - - ipt.pt._qproc = io_poll_queue_proc; - ipt.pt._key = poll->events; - ipt.req = req; - ipt.error = -EINVAL; /* same as no support for IOCB_CMD_POLL */ - - /* initialized the list so that we can do list_empty checks */ - INIT_LIST_HEAD(&poll->wait.entry); - init_waitqueue_func_entry(&poll->wait, io_poll_wake); - poll->wait.private = req; - INIT_LIST_HEAD(&req->list); + ipt.pt._qproc = io_poll_queue_proc; - mask = vfs_poll(poll->file, &ipt.pt) & poll->events; + mask = __io_arm_poll_handler(req, &req->poll, &ipt, poll->events, + io_poll_wake); - spin_lock_irq(&ctx->completion_lock); - if (likely(poll->head)) { - spin_lock(&poll->head->lock); - if (unlikely(list_empty(&poll->wait.entry))) { - if (ipt.error) - cancel = true; - ipt.error = 0; - mask = 0; - } - if (mask || ipt.error) - list_del_init(&poll->wait.entry); - else if (cancel) - WRITE_ONCE(poll->canceled, true); - else if (!poll->done) /* actually waiting for an event */ - io_poll_req_insert(req); - spin_unlock(&poll->head->lock); - } if (mask) { /* no async, we'd stolen it */ ipt.error = 0; io_poll_complete(req, mask, 0); @@ -4751,6 +4920,9 @@ static struct io_kiocb *io_prep_linked_timeout(struct io_kiocb *req) if (!(req->flags & REQ_F_LINK)) return NULL; + /* for polled retry, if flag is set, we already went through here */ + if (req->flags & REQ_F_POLLED) + return NULL; nxt = list_first_entry_or_null(&req->link_list, struct io_kiocb, link_list); @@ -4788,6 +4960,11 @@ again: */ if (ret == -EAGAIN && (!(req->flags & REQ_F_NOWAIT) || (req->flags & REQ_F_MUST_PUNT))) { + if (io_arm_poll_handler(req)) { + if (linked_timeout) + io_queue_linked_timeout(linked_timeout); + goto done_req; + } punt: if (io_op_defs[req->opcode].file_table) { ret = io_grab_files(req); @@ -6782,6 +6959,17 @@ static void __io_uring_show_fdinfo(struct io_ring_ctx *ctx, struct seq_file *m) seq_printf(m, "Personalities:\n"); idr_for_each(&ctx->personality_idr, io_uring_show_cred, m); } + seq_printf(m, "PollList:\n"); + spin_lock_irq(&ctx->completion_lock); + for (i = 0; i < (1U << ctx->cancel_hash_bits); i++) { + struct hlist_head *list = &ctx->cancel_hash[i]; + struct io_kiocb *req; + + hlist_for_each_entry(req, list, hash_node) + seq_printf(m, " op=%d, task_works=%d\n", req->opcode, + req->task->task_works != NULL); + } + spin_unlock_irq(&ctx->completion_lock); mutex_unlock(&ctx->uring_lock); } @@ -6998,7 +7186,7 @@ static int io_uring_create(unsigned entries, struct io_uring_params *p) p->features = IORING_FEAT_SINGLE_MMAP | IORING_FEAT_NODROP | IORING_FEAT_SUBMIT_STABLE | IORING_FEAT_RW_CUR_POS | - IORING_FEAT_CUR_PERSONALITY; + IORING_FEAT_CUR_PERSONALITY | IORING_FEAT_FAST_POLL; trace_io_uring_create(ret, ctx, p->sq_entries, p->cq_entries, p->flags); return ret; err: diff --git a/include/trace/events/io_uring.h b/include/trace/events/io_uring.h index 27bd9e4f927b..9f0d3b7d56b0 100644 --- a/include/trace/events/io_uring.h +++ b/include/trace/events/io_uring.h @@ -357,6 +357,109 @@ TRACE_EVENT(io_uring_submit_sqe, __entry->force_nonblock, __entry->sq_thread) ); +TRACE_EVENT(io_uring_poll_arm, + + TP_PROTO(void *ctx, u8 opcode, u64 user_data, int mask, int events), + + TP_ARGS(ctx, opcode, user_data, mask, events), + + TP_STRUCT__entry ( + __field( void *, ctx ) + __field( u8, opcode ) + __field( u64, user_data ) + __field( int, mask ) + __field( int, events ) + ), + + TP_fast_assign( + __entry->ctx = ctx; + __entry->opcode = opcode; + __entry->user_data = user_data; + __entry->mask = mask; + __entry->events = events; + ), + + TP_printk("ring %p, op %d, data 0x%llx, mask 0x%x, events 0x%x", + __entry->ctx, __entry->opcode, + (unsigned long long) __entry->user_data, + __entry->mask, __entry->events) +); + +TRACE_EVENT(io_uring_poll_wake, + + TP_PROTO(void *ctx, u8 opcode, u64 user_data, int mask), + + TP_ARGS(ctx, opcode, user_data, mask), + + TP_STRUCT__entry ( + __field( void *, ctx ) + __field( u8, opcode ) + __field( u64, user_data ) + __field( int, mask ) + ), + + TP_fast_assign( + __entry->ctx = ctx; + __entry->opcode = opcode; + __entry->user_data = user_data; + __entry->mask = mask; + ), + + TP_printk("ring %p, op %d, data 0x%llx, mask 0x%x", + __entry->ctx, __entry->opcode, + (unsigned long long) __entry->user_data, + __entry->mask) +); + +TRACE_EVENT(io_uring_task_add, + + TP_PROTO(void *ctx, u8 opcode, u64 user_data, int mask), + + TP_ARGS(ctx, opcode, user_data, mask), + + TP_STRUCT__entry ( + __field( void *, ctx ) + __field( u8, opcode ) + __field( u64, user_data ) + __field( int, mask ) + ), + + TP_fast_assign( + __entry->ctx = ctx; + __entry->opcode = opcode; + __entry->user_data = user_data; + __entry->mask = mask; + ), + + TP_printk("ring %p, op %d, data 0x%llx, mask %x", + __entry->ctx, __entry->opcode, + (unsigned long long) __entry->user_data, + __entry->mask) +); + +TRACE_EVENT(io_uring_task_run, + + TP_PROTO(void *ctx, u8 opcode, u64 user_data), + + TP_ARGS(ctx, opcode, user_data), + + TP_STRUCT__entry ( + __field( void *, ctx ) + __field( u8, opcode ) + __field( u64, user_data ) + ), + + TP_fast_assign( + __entry->ctx = ctx; + __entry->opcode = opcode; + __entry->user_data = user_data; + ), + + TP_printk("ring %p, op %d, data 0x%llx", + __entry->ctx, __entry->opcode, + (unsigned long long) __entry->user_data) +); + #endif /* _TRACE_IO_URING_H */ /* This part must be outside protection */ diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 08891cc1c1e7..53b36311cdac 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -216,6 +216,7 @@ struct io_uring_params { #define IORING_FEAT_SUBMIT_STABLE (1U << 2) #define IORING_FEAT_RW_CUR_POS (1U << 3) #define IORING_FEAT_CUR_PERSONALITY (1U << 4) +#define IORING_FEAT_FAST_POLL (1U << 5) /* * io_uring_register(2) opcodes and arguments -- cgit v1.2.3 From 441420a1f0b3031f228453697406c86f110e59d4 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Sun, 1 Mar 2020 00:10:43 -0800 Subject: bpf: Reliably preserve btf_trace_xxx types btf_trace_xxx types, crucial for tp_btf BPF programs (raw tracepoint with verifier-checked direct memory access), have to be preserved in kernel BTF to allow verifier do its job and enforce type/memory safety. It was reported ([0]) that for kernels built with Clang current type-casting approach doesn't preserve these types. This patch fixes it by declaring an anonymous union for each registered tracepoint, capturing both struct bpf_raw_event_map information, as well as recording btf_trace_##call type reliably. Structurally, it's still the same content as for a plain struct bpf_raw_event_map, so no other changes are necessary. [0] https://github.com/iovisor/bcc/issues/2770#issuecomment-591007692 Fixes: e8c423fb31fa ("bpf: Add typecast to raw_tracepoints to help BTF generation") Reported-by: Wenbo Zhang Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20200301081045.3491005-2-andriin@fb.com --- include/trace/bpf_probe.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/trace/bpf_probe.h b/include/trace/bpf_probe.h index b04c29270973..1ce3be63add1 100644 --- a/include/trace/bpf_probe.h +++ b/include/trace/bpf_probe.h @@ -75,13 +75,17 @@ static inline void bpf_test_probe_##call(void) \ check_trace_callback_type_##call(__bpf_trace_##template); \ } \ typedef void (*btf_trace_##call)(void *__data, proto); \ -static struct bpf_raw_event_map __used \ - __attribute__((section("__bpf_raw_tp_map"))) \ -__bpf_trace_tp_map_##call = { \ - .tp = &__tracepoint_##call, \ - .bpf_func = (void *)(btf_trace_##call)__bpf_trace_##template, \ - .num_args = COUNT_ARGS(args), \ - .writable_size = size, \ +static union { \ + struct bpf_raw_event_map event; \ + btf_trace_##call handler; \ +} __bpf_trace_tp_map_##call __used \ +__attribute__((section("__bpf_raw_tp_map"))) = { \ + .event = { \ + .tp = &__tracepoint_##call, \ + .bpf_func = __bpf_trace_##template, \ + .num_args = COUNT_ARGS(args), \ + .writable_size = size, \ + }, \ }; #define FIRST(x, ...) x -- cgit v1.2.3 From 70ed506c3bbcfa846d4636b23051ca79fa4781f7 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 2 Mar 2020 20:31:57 -0800 Subject: bpf: Introduce pinnable bpf_link abstraction Introduce bpf_link abstraction, representing an attachment of BPF program to a BPF hook point (e.g., tracepoint, perf event, etc). bpf_link encapsulates ownership of attached BPF program, reference counting of a link itself, when reference from multiple anonymous inodes, as well as ensures that release callback will be called from a process context, so that users can safely take mutex locks and sleep. Additionally, with a new abstraction it's now possible to generalize pinning of a link object in BPF FS, allowing to explicitly prevent BPF program detachment on process exit by pinning it in a BPF FS and let it open from independent other process to keep working with it. Convert two existing bpf_link-like objects (raw tracepoint and tracing BPF program attachments) into utilizing bpf_link framework, making them pinnable in BPF FS. More FD-based bpf_links will be added in follow up patches. Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200303043159.323675-2-andriin@fb.com --- include/linux/bpf.h | 13 +++ kernel/bpf/inode.c | 42 +++++++++- kernel/bpf/syscall.c | 223 +++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 232 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 6015a4daf118..f13c78c6f29d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1056,6 +1056,19 @@ extern int sysctl_unprivileged_bpf_disabled; int bpf_map_new_fd(struct bpf_map *map, int flags); int bpf_prog_new_fd(struct bpf_prog *prog); +struct bpf_link; + +struct bpf_link_ops { + void (*release)(struct bpf_link *link); +}; + +void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, + struct bpf_prog *prog); +void bpf_link_inc(struct bpf_link *link); +void bpf_link_put(struct bpf_link *link); +int bpf_link_new_fd(struct bpf_link *link); +struct bpf_link *bpf_link_get_from_fd(u32 ufd); + int bpf_obj_pin_user(u32 ufd, const char __user *pathname); int bpf_obj_get_user(const char __user *pathname, int flags); diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 5e40e7fccc21..95087d9f4ed3 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -25,6 +25,7 @@ enum bpf_type { BPF_TYPE_UNSPEC = 0, BPF_TYPE_PROG, BPF_TYPE_MAP, + BPF_TYPE_LINK, }; static void *bpf_any_get(void *raw, enum bpf_type type) @@ -36,6 +37,9 @@ static void *bpf_any_get(void *raw, enum bpf_type type) case BPF_TYPE_MAP: bpf_map_inc_with_uref(raw); break; + case BPF_TYPE_LINK: + bpf_link_inc(raw); + break; default: WARN_ON_ONCE(1); break; @@ -53,6 +57,9 @@ static void bpf_any_put(void *raw, enum bpf_type type) case BPF_TYPE_MAP: bpf_map_put_with_uref(raw); break; + case BPF_TYPE_LINK: + bpf_link_put(raw); + break; default: WARN_ON_ONCE(1); break; @@ -63,20 +70,32 @@ static void *bpf_fd_probe_obj(u32 ufd, enum bpf_type *type) { void *raw; - *type = BPF_TYPE_MAP; raw = bpf_map_get_with_uref(ufd); - if (IS_ERR(raw)) { + if (!IS_ERR(raw)) { + *type = BPF_TYPE_MAP; + return raw; + } + + raw = bpf_prog_get(ufd); + if (!IS_ERR(raw)) { *type = BPF_TYPE_PROG; - raw = bpf_prog_get(ufd); + return raw; } - return raw; + raw = bpf_link_get_from_fd(ufd); + if (!IS_ERR(raw)) { + *type = BPF_TYPE_LINK; + return raw; + } + + return ERR_PTR(-EINVAL); } static const struct inode_operations bpf_dir_iops; static const struct inode_operations bpf_prog_iops = { }; static const struct inode_operations bpf_map_iops = { }; +static const struct inode_operations bpf_link_iops = { }; static struct inode *bpf_get_inode(struct super_block *sb, const struct inode *dir, @@ -114,6 +133,8 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type) *type = BPF_TYPE_PROG; else if (inode->i_op == &bpf_map_iops) *type = BPF_TYPE_MAP; + else if (inode->i_op == &bpf_link_iops) + *type = BPF_TYPE_LINK; else return -EACCES; @@ -335,6 +356,12 @@ static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg) &bpffs_map_fops : &bpffs_obj_fops); } +static int bpf_mklink(struct dentry *dentry, umode_t mode, void *arg) +{ + return bpf_mkobj_ops(dentry, mode, arg, &bpf_link_iops, + &bpffs_obj_fops); +} + static struct dentry * bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags) { @@ -411,6 +438,9 @@ static int bpf_obj_do_pin(const char __user *pathname, void *raw, case BPF_TYPE_MAP: ret = vfs_mkobj(dentry, mode, bpf_mkmap, raw); break; + case BPF_TYPE_LINK: + ret = vfs_mkobj(dentry, mode, bpf_mklink, raw); + break; default: ret = -EPERM; } @@ -487,6 +517,8 @@ int bpf_obj_get_user(const char __user *pathname, int flags) ret = bpf_prog_new_fd(raw); else if (type == BPF_TYPE_MAP) ret = bpf_map_new_fd(raw, f_flags); + else if (type == BPF_TYPE_LINK) + ret = bpf_link_new_fd(raw); else return -ENOENT; @@ -504,6 +536,8 @@ static struct bpf_prog *__get_prog_inode(struct inode *inode, enum bpf_prog_type if (inode->i_op == &bpf_map_iops) return ERR_PTR(-EINVAL); + if (inode->i_op == &bpf_link_iops) + return ERR_PTR(-EINVAL); if (inode->i_op != &bpf_prog_iops) return ERR_PTR(-EACCES); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index c536c65256ad..13de65363ba2 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2173,24 +2173,154 @@ static int bpf_obj_get(const union bpf_attr *attr) attr->file_flags); } -static int bpf_tracing_prog_release(struct inode *inode, struct file *filp) +struct bpf_link { + atomic64_t refcnt; + const struct bpf_link_ops *ops; + struct bpf_prog *prog; + struct work_struct work; +}; + +void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, + struct bpf_prog *prog) { - struct bpf_prog *prog = filp->private_data; + atomic64_set(&link->refcnt, 1); + link->ops = ops; + link->prog = prog; +} + +void bpf_link_inc(struct bpf_link *link) +{ + atomic64_inc(&link->refcnt); +} + +/* bpf_link_free is guaranteed to be called from process context */ +static void bpf_link_free(struct bpf_link *link) +{ + struct bpf_prog *prog; - WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog)); + /* remember prog locally, because release below will free link memory */ + prog = link->prog; + /* extra clean up and kfree of container link struct */ + link->ops->release(link); + /* no more accesing of link members after this point */ bpf_prog_put(prog); +} + +static void bpf_link_put_deferred(struct work_struct *work) +{ + struct bpf_link *link = container_of(work, struct bpf_link, work); + + bpf_link_free(link); +} + +/* bpf_link_put can be called from atomic context, but ensures that resources + * are freed from process context + */ +void bpf_link_put(struct bpf_link *link) +{ + if (!atomic64_dec_and_test(&link->refcnt)) + return; + + if (in_atomic()) { + INIT_WORK(&link->work, bpf_link_put_deferred); + schedule_work(&link->work); + } else { + bpf_link_free(link); + } +} + +static int bpf_link_release(struct inode *inode, struct file *filp) +{ + struct bpf_link *link = filp->private_data; + + bpf_link_put(link); return 0; } -static const struct file_operations bpf_tracing_prog_fops = { - .release = bpf_tracing_prog_release, +#ifdef CONFIG_PROC_FS +static const struct bpf_link_ops bpf_raw_tp_lops; +static const struct bpf_link_ops bpf_tracing_link_lops; +static const struct bpf_link_ops bpf_xdp_link_lops; + +static void bpf_link_show_fdinfo(struct seq_file *m, struct file *filp) +{ + const struct bpf_link *link = filp->private_data; + const struct bpf_prog *prog = link->prog; + char prog_tag[sizeof(prog->tag) * 2 + 1] = { }; + const char *link_type; + + if (link->ops == &bpf_raw_tp_lops) + link_type = "raw_tracepoint"; + else if (link->ops == &bpf_tracing_link_lops) + link_type = "tracing"; + else + link_type = "unknown"; + + bin2hex(prog_tag, prog->tag, sizeof(prog->tag)); + seq_printf(m, + "link_type:\t%s\n" + "prog_tag:\t%s\n" + "prog_id:\t%u\n", + link_type, + prog_tag, + prog->aux->id); +} +#endif + +const struct file_operations bpf_link_fops = { +#ifdef CONFIG_PROC_FS + .show_fdinfo = bpf_link_show_fdinfo, +#endif + .release = bpf_link_release, .read = bpf_dummy_read, .write = bpf_dummy_write, }; +int bpf_link_new_fd(struct bpf_link *link) +{ + return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC); +} + +struct bpf_link *bpf_link_get_from_fd(u32 ufd) +{ + struct fd f = fdget(ufd); + struct bpf_link *link; + + if (!f.file) + return ERR_PTR(-EBADF); + if (f.file->f_op != &bpf_link_fops) { + fdput(f); + return ERR_PTR(-EINVAL); + } + + link = f.file->private_data; + bpf_link_inc(link); + fdput(f); + + return link; +} + +struct bpf_tracing_link { + struct bpf_link link; +}; + +static void bpf_tracing_link_release(struct bpf_link *link) +{ + struct bpf_tracing_link *tr_link = + container_of(link, struct bpf_tracing_link, link); + + WARN_ON_ONCE(bpf_trampoline_unlink_prog(link->prog)); + kfree(tr_link); +} + +static const struct bpf_link_ops bpf_tracing_link_lops = { + .release = bpf_tracing_link_release, +}; + static int bpf_tracing_prog_attach(struct bpf_prog *prog) { - int tr_fd, err; + struct bpf_tracing_link *link; + int link_fd, err; if (prog->expected_attach_type != BPF_TRACE_FENTRY && prog->expected_attach_type != BPF_TRACE_FEXIT && @@ -2199,58 +2329,61 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) goto out_put_prog; } + link = kzalloc(sizeof(*link), GFP_USER); + if (!link) { + err = -ENOMEM; + goto out_put_prog; + } + bpf_link_init(&link->link, &bpf_tracing_link_lops, prog); + err = bpf_trampoline_link_prog(prog); if (err) - goto out_put_prog; + goto out_free_link; - tr_fd = anon_inode_getfd("bpf-tracing-prog", &bpf_tracing_prog_fops, - prog, O_CLOEXEC); - if (tr_fd < 0) { + link_fd = bpf_link_new_fd(&link->link); + if (link_fd < 0) { WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog)); - err = tr_fd; - goto out_put_prog; + err = link_fd; + goto out_free_link; } - return tr_fd; + return link_fd; +out_free_link: + kfree(link); out_put_prog: bpf_prog_put(prog); return err; } -struct bpf_raw_tracepoint { +struct bpf_raw_tp_link { + struct bpf_link link; struct bpf_raw_event_map *btp; - struct bpf_prog *prog; }; -static int bpf_raw_tracepoint_release(struct inode *inode, struct file *filp) +static void bpf_raw_tp_link_release(struct bpf_link *link) { - struct bpf_raw_tracepoint *raw_tp = filp->private_data; + struct bpf_raw_tp_link *raw_tp = + container_of(link, struct bpf_raw_tp_link, link); - if (raw_tp->prog) { - bpf_probe_unregister(raw_tp->btp, raw_tp->prog); - bpf_prog_put(raw_tp->prog); - } + bpf_probe_unregister(raw_tp->btp, raw_tp->link.prog); bpf_put_raw_tracepoint(raw_tp->btp); kfree(raw_tp); - return 0; } -static const struct file_operations bpf_raw_tp_fops = { - .release = bpf_raw_tracepoint_release, - .read = bpf_dummy_read, - .write = bpf_dummy_write, +static const struct bpf_link_ops bpf_raw_tp_lops = { + .release = bpf_raw_tp_link_release, }; #define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd static int bpf_raw_tracepoint_open(const union bpf_attr *attr) { - struct bpf_raw_tracepoint *raw_tp; + struct bpf_raw_tp_link *raw_tp; struct bpf_raw_event_map *btp; struct bpf_prog *prog; const char *tp_name; char buf[128]; - int tp_fd, err; + int link_fd, err; if (CHECK_ATTR(BPF_RAW_TRACEPOINT_OPEN)) return -EINVAL; @@ -2302,21 +2435,20 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) err = -ENOMEM; goto out_put_btp; } + bpf_link_init(&raw_tp->link, &bpf_raw_tp_lops, prog); raw_tp->btp = btp; - raw_tp->prog = prog; err = bpf_probe_register(raw_tp->btp, prog); if (err) goto out_free_tp; - tp_fd = anon_inode_getfd("bpf-raw-tracepoint", &bpf_raw_tp_fops, raw_tp, - O_CLOEXEC); - if (tp_fd < 0) { + link_fd = bpf_link_new_fd(&raw_tp->link); + if (link_fd < 0) { bpf_probe_unregister(raw_tp->btp, prog); - err = tp_fd; + err = link_fd; goto out_free_tp; } - return tp_fd; + return link_fd; out_free_tp: kfree(raw_tp); @@ -3266,15 +3398,21 @@ static int bpf_task_fd_query(const union bpf_attr *attr, if (err) goto out; - if (file->f_op == &bpf_raw_tp_fops) { - struct bpf_raw_tracepoint *raw_tp = file->private_data; - struct bpf_raw_event_map *btp = raw_tp->btp; + if (file->f_op == &bpf_link_fops) { + struct bpf_link *link = file->private_data; - err = bpf_task_fd_query_copy(attr, uattr, - raw_tp->prog->aux->id, - BPF_FD_TYPE_RAW_TRACEPOINT, - btp->tp->name, 0, 0); - goto put_file; + if (link->ops == &bpf_raw_tp_lops) { + struct bpf_raw_tp_link *raw_tp = + container_of(link, struct bpf_raw_tp_link, link); + struct bpf_raw_event_map *btp = raw_tp->btp; + + err = bpf_task_fd_query_copy(attr, uattr, + raw_tp->link.prog->aux->id, + BPF_FD_TYPE_RAW_TRACEPOINT, + btp->tp->name, 0, 0); + goto put_file; + } + goto out_not_supp; } event = perf_get_event(file); @@ -3294,6 +3432,7 @@ static int bpf_task_fd_query(const union bpf_attr *attr, goto put_file; } +out_not_supp: err = -ENOTSUPP; put_file: fput(file); -- cgit v1.2.3 From 0e72a6a3cfc3a32273f5e99bfaa4407f4917d343 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 15 Jan 2020 17:35:45 +0100 Subject: efi: Export boot-services code and data as debugfs-blobs Sometimes it is useful to be able to dump the efi boot-services code and data. This commit adds these as debugfs-blobs to /sys/kernel/debug/efi, but only if efi=debug is passed on the kernel-commandline as this requires not freeing those memory-regions, which costs 20+ MB of RAM. Reviewed-by: Greg Kroah-Hartman Acked-by: Ard Biesheuvel Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200115163554.101315-2-hdegoede@redhat.com Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 1 + arch/x86/platform/efi/quirks.c | 4 +++ drivers/firmware/efi/efi.c | 57 ++++++++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 1 + 4 files changed, 63 insertions(+) (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index ae923ee8e2b4..8391d3c658b4 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -243,6 +243,7 @@ int __init efi_memblock_x86_reserve_range(void) efi.memmap.desc_version); memblock_reserve(pmap, efi.memmap.nr_map * efi.memmap.desc_size); + set_bit(EFI_PRESERVE_BS_REGIONS, &efi.flags); return 0; } diff --git a/arch/x86/platform/efi/quirks.c b/arch/x86/platform/efi/quirks.c index 88d32c06cffa..bada1037b711 100644 --- a/arch/x86/platform/efi/quirks.c +++ b/arch/x86/platform/efi/quirks.c @@ -410,6 +410,10 @@ void __init efi_free_boot_services(void) int num_entries = 0; void *new, *new_md; + /* Keep all regions for /sys/kernel/debug/efi */ + if (efi_enabled(EFI_DBG)) + return; + for_each_efi_memory_desc(md) { unsigned long long start = md->phys_addr; unsigned long long size = md->num_pages << EFI_PAGE_SHIFT; diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 621220ab3d0e..201a6799671d 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -325,6 +326,59 @@ free_entry: static inline int efivar_ssdt_load(void) { return 0; } #endif +#ifdef CONFIG_DEBUG_FS + +#define EFI_DEBUGFS_MAX_BLOBS 32 + +static struct debugfs_blob_wrapper debugfs_blob[EFI_DEBUGFS_MAX_BLOBS]; + +static void __init efi_debugfs_init(void) +{ + struct dentry *efi_debugfs; + efi_memory_desc_t *md; + char name[32]; + int type_count[EFI_BOOT_SERVICES_DATA + 1] = {}; + int i = 0; + + efi_debugfs = debugfs_create_dir("efi", NULL); + if (IS_ERR_OR_NULL(efi_debugfs)) + return; + + for_each_efi_memory_desc(md) { + switch (md->type) { + case EFI_BOOT_SERVICES_CODE: + snprintf(name, sizeof(name), "boot_services_code%d", + type_count[md->type]++); + break; + case EFI_BOOT_SERVICES_DATA: + snprintf(name, sizeof(name), "boot_services_data%d", + type_count[md->type]++); + break; + default: + continue; + } + + if (i >= EFI_DEBUGFS_MAX_BLOBS) { + pr_warn("More then %d EFI boot service segments, only showing first %d in debugfs\n", + EFI_DEBUGFS_MAX_BLOBS, EFI_DEBUGFS_MAX_BLOBS); + break; + } + + debugfs_blob[i].size = md->num_pages << EFI_PAGE_SHIFT; + debugfs_blob[i].data = memremap(md->phys_addr, + debugfs_blob[i].size, + MEMREMAP_WB); + if (!debugfs_blob[i].data) + continue; + + debugfs_create_blob(name, 0400, efi_debugfs, &debugfs_blob[i]); + i++; + } +} +#else +static inline void efi_debugfs_init(void) {} +#endif + /* * We register the efi subsystem with the firmware subsystem and the * efivars subsystem with the efi subsystem, if the system was booted with @@ -381,6 +435,9 @@ static int __init efisubsys_init(void) goto err_remove_group; } + if (efi_enabled(EFI_DBG) && efi_enabled(EFI_PRESERVE_BS_REGIONS)) + efi_debugfs_init(); + return 0; err_remove_group: diff --git a/include/linux/efi.h b/include/linux/efi.h index 7efd7072cca5..a6e1a2d8511e 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1124,6 +1124,7 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */ #define EFI_MEM_ATTR 10 /* Did firmware publish an EFI_MEMORY_ATTRIBUTES table? */ #define EFI_MEM_NO_SOFT_RESERVE 11 /* Is the kernel configured to ignore soft reservations? */ +#define EFI_PRESERVE_BS_REGIONS 12 /* Are EFI boot-services memory segments available? */ #ifdef CONFIG_EFI /* -- cgit v1.2.3 From f0df68d5bae8825ee5b62f00af237ae82247f045 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 15 Jan 2020 17:35:46 +0100 Subject: efi: Add embedded peripheral firmware support Just like with PCI options ROMs, which we save in the setup_efi_pci* functions from arch/x86/boot/compressed/eboot.c, the EFI code / ROM itself sometimes may contain data which is useful/necessary for peripheral drivers to have access to. Specifically the EFI code may contain an embedded copy of firmware which needs to be (re)loaded into the peripheral. Normally such firmware would be part of linux-firmware, but in some cases this is not feasible, for 2 reasons: 1) The firmware is customized for a specific use-case of the chipset / use with a specific hardware model, so we cannot have a single firmware file for the chipset. E.g. touchscreen controller firmwares are compiled specifically for the hardware model they are used with, as they are calibrated for a specific model digitizer. 2) Despite repeated attempts we have failed to get permission to redistribute the firmware. This is especially a problem with customized firmwares, these get created by the chip vendor for a specific ODM and the copyright may partially belong with the ODM, so the chip vendor cannot give a blanket permission to distribute these. This commit adds support for finding peripheral firmware embedded in the EFI code and makes the found firmware available through the new efi_get_embedded_fw() function. Support for loading these firmwares through the standard firmware loading mechanism is added in a follow-up commit in this patch-series. Note we check the EFI_BOOT_SERVICES_CODE for embedded firmware near the end of start_kernel(), just before calling rest_init(), this is on purpose because the typical EFI_BOOT_SERVICES_CODE memory-segment is too large for early_memremap(), so the check must be done after mm_init(). This relies on EFI_BOOT_SERVICES_CODE not being free-ed until efi_free_boot_services() is called, which means that this will only work on x86 for now. Reported-by: Dave Olsthoorn Suggested-by: Peter Jones Acked-by: Ard Biesheuvel Signed-off-by: Hans de Goede Link: https://lore.kernel.org/r/20200115163554.101315-3-hdegoede@redhat.com Signed-off-by: Ard Biesheuvel --- arch/x86/platform/efi/efi.c | 1 + drivers/firmware/efi/Kconfig | 5 ++ drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/embedded-firmware.c | 147 +++++++++++++++++++++++++++++++ include/linux/efi.h | 6 ++ include/linux/efi_embedded_fw.h | 41 +++++++++ 6 files changed, 201 insertions(+) create mode 100644 drivers/firmware/efi/embedded-firmware.c create mode 100644 include/linux/efi_embedded_fw.h (limited to 'include') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 8391d3c658b4..a4ea04603af3 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -944,6 +944,7 @@ static void __init __efi_enter_virtual_mode(void) goto err; } + efi_check_for_embedded_firmwares(); efi_free_boot_services(); /* diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index ecc83e2f032c..613828d3f106 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -239,6 +239,11 @@ config EFI_DISABLE_PCI_DMA endmenu +config EFI_EMBEDDED_FIRMWARE + bool + depends on EFI + select CRYPTO_LIB_SHA256 + config UEFI_CPER bool diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 554d795270d9..256d6121b2b3 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o obj-$(CONFIG_APPLE_PROPERTIES) += apple-properties.o obj-$(CONFIG_EFI_RCI2_TABLE) += rci2-table.o +obj-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += embedded-firmware.o fake_map-y += fake_mem.o fake_map-$(CONFIG_X86) += x86_fake_mem.o diff --git a/drivers/firmware/efi/embedded-firmware.c b/drivers/firmware/efi/embedded-firmware.c new file mode 100644 index 000000000000..1bc9cdae2eed --- /dev/null +++ b/drivers/firmware/efi/embedded-firmware.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Support for extracting embedded firmware for peripherals from EFI code, + * + * Copyright (c) 2018 Hans de Goede + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Exported for use by lib/test_firmware.c only */ +LIST_HEAD(efi_embedded_fw_list); +EXPORT_SYMBOL_GPL(efi_embedded_fw_list); + +static bool checked_for_fw; + +static const struct dmi_system_id * const embedded_fw_table[] = { + NULL +}; + +/* + * Note the efi_check_for_embedded_firmwares() code currently makes the + * following 2 assumptions. This may needs to be revisited if embedded firmware + * is found where this is not true: + * 1) The firmware is only found in EFI_BOOT_SERVICES_CODE memory segments + * 2) The firmware always starts at an offset which is a multiple of 8 bytes + */ +static int __init efi_check_md_for_embedded_firmware( + efi_memory_desc_t *md, const struct efi_embedded_fw_desc *desc) +{ + struct sha256_state sctx; + struct efi_embedded_fw *fw; + u8 sha256[32]; + u64 i, size; + u8 *map; + + size = md->num_pages << EFI_PAGE_SHIFT; + map = memremap(md->phys_addr, size, MEMREMAP_WB); + if (!map) { + pr_err("Error mapping EFI mem at %#llx\n", md->phys_addr); + return -ENOMEM; + } + + for (i = 0; (i + desc->length) <= size; i += 8) { + if (memcmp(map + i, desc->prefix, EFI_EMBEDDED_FW_PREFIX_LEN)) + continue; + + sha256_init(&sctx); + sha256_update(&sctx, map + i, desc->length); + sha256_final(&sctx, sha256); + if (memcmp(sha256, desc->sha256, 32) == 0) + break; + } + if ((i + desc->length) > size) { + memunmap(map); + return -ENOENT; + } + + pr_info("Found EFI embedded fw '%s'\n", desc->name); + + fw = kmalloc(sizeof(*fw), GFP_KERNEL); + if (!fw) { + memunmap(map); + return -ENOMEM; + } + + fw->data = kmemdup(map + i, desc->length, GFP_KERNEL); + memunmap(map); + if (!fw->data) { + kfree(fw); + return -ENOMEM; + } + + fw->name = desc->name; + fw->length = desc->length; + list_add(&fw->list, &efi_embedded_fw_list); + + return 0; +} + +void __init efi_check_for_embedded_firmwares(void) +{ + const struct efi_embedded_fw_desc *fw_desc; + const struct dmi_system_id *dmi_id; + efi_memory_desc_t *md; + int i, r; + + for (i = 0; embedded_fw_table[i]; i++) { + dmi_id = dmi_first_match(embedded_fw_table[i]); + if (!dmi_id) + continue; + + fw_desc = dmi_id->driver_data; + + /* + * In some drivers the struct driver_data contains may contain + * other driver specific data after the fw_desc struct; and + * the fw_desc struct itself may be empty, skip these. + */ + if (!fw_desc->name) + continue; + + for_each_efi_memory_desc(md) { + if (md->type != EFI_BOOT_SERVICES_CODE) + continue; + + r = efi_check_md_for_embedded_firmware(md, fw_desc); + if (r == 0) + break; + } + } + + checked_for_fw = true; +} + +int efi_get_embedded_fw(const char *name, const u8 **data, size_t *size) +{ + struct efi_embedded_fw *iter, *fw = NULL; + + if (!checked_for_fw) { + pr_warn("Warning %s called while we did not check for embedded fw\n", + __func__); + return -ENOENT; + } + + list_for_each_entry(iter, &efi_embedded_fw_list, list) { + if (strcmp(name, iter->name) == 0) { + fw = iter; + break; + } + } + + if (!fw) + return -ENOENT; + + *data = fw->data; + *size = fw->length; + + return 0; +} +EXPORT_SYMBOL_GPL(efi_get_embedded_fw); diff --git a/include/linux/efi.h b/include/linux/efi.h index a6e1a2d8511e..23392b88bcc0 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1554,6 +1554,12 @@ static inline void efi_enable_reset_attack_mitigation(void) { } #endif +#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE +void efi_check_for_embedded_firmwares(void); +#else +static inline void efi_check_for_embedded_firmwares(void) { } +#endif + efi_status_t efi_random_get_seed(void); void efi_retrieve_tpm2_eventlog(void); diff --git a/include/linux/efi_embedded_fw.h b/include/linux/efi_embedded_fw.h new file mode 100644 index 000000000000..3d066c6370c6 --- /dev/null +++ b/include/linux/efi_embedded_fw.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_EFI_EMBEDDED_FW_H +#define _LINUX_EFI_EMBEDDED_FW_H + +#include +#include + +#define EFI_EMBEDDED_FW_PREFIX_LEN 8 + +/* + * This struct and efi_embedded_fw_list are private to the efi-embedded fw + * implementation they are in this header for use by lib/test_firmware.c only! + */ +struct efi_embedded_fw { + struct list_head list; + const char *name; + const u8 *data; + size_t length; +}; + +extern struct list_head efi_embedded_fw_list; + +/** + * struct efi_embedded_fw_desc - This struct is used by the EFI embedded-fw + * code to search for embedded firmwares. + * + * @name: Name to register the firmware with if found + * @prefix: First 8 bytes of the firmware + * @length: Length of the firmware in bytes including prefix + * @sha256: SHA256 of the firmware + */ +struct efi_embedded_fw_desc { + const char *name; + u8 prefix[EFI_EMBEDDED_FW_PREFIX_LEN]; + u32 length; + u8 sha256[32]; +}; + +int efi_get_embedded_fw(const char *name, const u8 **dat, size_t *sz); + +#endif -- cgit v1.2.3 From c77af39bdb8b0f0605b484428a65ec0d97ba18dc Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Tue, 3 Mar 2020 19:02:52 +0200 Subject: dt-bindings: interconnect: Add Qualcomm SC7180 DT bindings The Qualcomm SC7180 platform has several bus fabrics that could be controlled and tuned dynamically according to the bandwidth demand. Signed-off-by: Odelu Kukatla Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1583241493-21212-2-git-send-email-okukatla@codeaurora.org Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,sc7180.yaml | 85 +++++++++++ include/dt-bindings/interconnect/qcom,sc7180.h | 161 +++++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,sc7180.yaml create mode 100644 include/dt-bindings/interconnect/qcom,sc7180.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sc7180.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sc7180.yaml new file mode 100644 index 000000000000..50f78f87f3fb --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,sc7180.yaml @@ -0,0 +1,85 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,sc7180.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SC7180 Network-On-Chip Interconnect + +maintainers: + - Odelu Kukatla + +description: | + SC7180 interconnect providers support system bandwidth requirements through + RPMh hardware accelerators known as Bus Clock Manager (BCM). The provider is + able to communicate with the BCM through the Resource State Coordinator (RSC) + associated with each execution environment. Provider nodes must point to at + least one RPMh device child node pertaining to their RSC and each provider + can map to multiple RPMh resources. + +properties: + reg: + maxItems: 1 + + compatible: + enum: + - qcom,sc7180-aggre1-noc + - qcom,sc7180-aggre2-noc + - qcom,sc7180-camnoc-virt + - qcom,sc7180-compute-noc + - qcom,sc7180-config-noc + - qcom,sc7180-dc-noc + - qcom,sc7180-gem-noc + - qcom,sc7180-ipa-virt + - qcom,sc7180-mc-virt + - qcom,sc7180-mmss-noc + - qcom,sc7180-npu-noc + - qcom,sc7180-qup-virt + - qcom,sc7180-system-noc + + '#interconnect-cells': + const: 1 + + qcom,bcm-voters: + $ref: /schemas/types.yaml#/definitions/phandle-array + description: | + List of phandles to qcom,bcm-voter nodes that are required by + this interconnect to send RPMh commands. + + qcom,bcm-voter-names: + $ref: /schemas/types.yaml#/definitions/string-array + description: | + Names for each of the qcom,bcm-voters specified. + +required: + - compatible + - reg + - '#interconnect-cells' + - qcom,bcm-voters + +additionalProperties: false + +examples: + - | + #include + + config_noc: interconnect@1500000 { + compatible = "qcom,sc7180-config-noc"; + reg = <0 0x01500000 0 0x28000>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + system_noc: interconnect@1620000 { + compatible = "qcom,sc7180-system-noc"; + reg = <0 0x01620000 0 0x17080>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; + + mmss_noc: interconnect@1740000 { + compatible = "qcom,sc7180-mmss-noc"; + reg = <0 0x01740000 0 0x1c100>; + #interconnect-cells = <1>; + qcom,bcm-voters = <&apps_bcm_voter>; + }; diff --git a/include/dt-bindings/interconnect/qcom,sc7180.h b/include/dt-bindings/interconnect/qcom,sc7180.h new file mode 100644 index 000000000000..f9970f6032eb --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,sc7180.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Qualcomm SC7180 interconnect IDs + * + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_SC7180_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_SC7180_H + +#define MASTER_A1NOC_CFG 0 +#define MASTER_QSPI 1 +#define MASTER_QUP_0 2 +#define MASTER_SDCC_2 3 +#define MASTER_EMMC 4 +#define MASTER_UFS_MEM 5 +#define SLAVE_A1NOC_SNOC 6 +#define SLAVE_SERVICE_A1NOC 7 + +#define MASTER_A2NOC_CFG 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_QUP_1 2 +#define MASTER_USB3 3 +#define MASTER_CRYPTO 4 +#define MASTER_IPA 5 +#define MASTER_QDSS_ETR 6 +#define SLAVE_A2NOC_SNOC 7 +#define SLAVE_SERVICE_A2NOC 8 + +#define MASTER_CAMNOC_HF0_UNCOMP 0 +#define MASTER_CAMNOC_HF1_UNCOMP 1 +#define MASTER_CAMNOC_SF_UNCOMP 2 +#define SLAVE_CAMNOC_UNCOMP 3 + +#define MASTER_NPU 0 +#define MASTER_NPU_PROC 1 +#define SLAVE_CDSP_GEM_NOC 2 + +#define MASTER_SNOC_CNOC 0 +#define MASTER_QDSS_DAP 1 +#define SLAVE_A1NOC_CFG 2 +#define SLAVE_A2NOC_CFG 3 +#define SLAVE_AHB2PHY_SOUTH 4 +#define SLAVE_AHB2PHY_CENTER 5 +#define SLAVE_AOP 6 +#define SLAVE_AOSS 7 +#define SLAVE_BOOT_ROM 8 +#define SLAVE_CAMERA_CFG 9 +#define SLAVE_CAMERA_NRT_THROTTLE_CFG 10 +#define SLAVE_CAMERA_RT_THROTTLE_CFG 11 +#define SLAVE_CLK_CTL 12 +#define SLAVE_RBCPR_CX_CFG 13 +#define SLAVE_RBCPR_MX_CFG 14 +#define SLAVE_CRYPTO_0_CFG 15 +#define SLAVE_DCC_CFG 16 +#define SLAVE_CNOC_DDRSS 17 +#define SLAVE_DISPLAY_CFG 18 +#define SLAVE_DISPLAY_RT_THROTTLE_CFG 19 +#define SLAVE_DISPLAY_THROTTLE_CFG 20 +#define SLAVE_EMMC_CFG 21 +#define SLAVE_GLM 22 +#define SLAVE_GFX3D_CFG 23 +#define SLAVE_IMEM_CFG 24 +#define SLAVE_IPA_CFG 25 +#define SLAVE_CNOC_MNOC_CFG 26 +#define SLAVE_CNOC_MSS 27 +#define SLAVE_NPU_CFG 28 +#define SLAVE_NPU_DMA_BWMON_CFG 29 +#define SLAVE_NPU_PROC_BWMON_CFG 30 +#define SLAVE_PDM 31 +#define SLAVE_PIMEM_CFG 32 +#define SLAVE_PRNG 33 +#define SLAVE_QDSS_CFG 34 +#define SLAVE_QM_CFG 35 +#define SLAVE_QM_MPU_CFG 36 +#define SLAVE_QSPI_0 37 +#define SLAVE_QUP_0 38 +#define SLAVE_QUP_1 39 +#define SLAVE_SDCC_2 40 +#define SLAVE_SECURITY 41 +#define SLAVE_SNOC_CFG 42 +#define SLAVE_TCSR 43 +#define SLAVE_TLMM_WEST 44 +#define SLAVE_TLMM_NORTH 45 +#define SLAVE_TLMM_SOUTH 46 +#define SLAVE_UFS_MEM_CFG 47 +#define SLAVE_USB3 48 +#define SLAVE_VENUS_CFG 49 +#define SLAVE_VENUS_THROTTLE_CFG 50 +#define SLAVE_VSENSE_CTRL_CFG 51 +#define SLAVE_SERVICE_CNOC 52 + +#define MASTER_CNOC_DC_NOC 0 +#define SLAVE_GEM_NOC_CFG 1 +#define SLAVE_LLCC_CFG 2 + +#define MASTER_APPSS_PROC 0 +#define MASTER_SYS_TCU 1 +#define MASTER_GEM_NOC_CFG 2 +#define MASTER_COMPUTE_NOC 3 +#define MASTER_MNOC_HF_MEM_NOC 4 +#define MASTER_MNOC_SF_MEM_NOC 5 +#define MASTER_SNOC_GC_MEM_NOC 6 +#define MASTER_SNOC_SF_MEM_NOC 7 +#define MASTER_GFX3D 8 +#define SLAVE_MSS_PROC_MS_MPU_CFG 9 +#define SLAVE_GEM_NOC_SNOC 10 +#define SLAVE_LLCC 11 +#define SLAVE_SERVICE_GEM_NOC 12 + +#define MASTER_IPA_CORE 0 +#define SLAVE_IPA_CORE 1 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 + +#define MASTER_CNOC_MNOC_CFG 0 +#define MASTER_CAMNOC_HF0 1 +#define MASTER_CAMNOC_HF1 2 +#define MASTER_CAMNOC_SF 3 +#define MASTER_MDP0 4 +#define MASTER_ROTATOR 5 +#define MASTER_VIDEO_P0 6 +#define MASTER_VIDEO_PROC 7 +#define SLAVE_MNOC_HF_MEM_NOC 8 +#define SLAVE_MNOC_SF_MEM_NOC 9 +#define SLAVE_SERVICE_MNOC 10 + +#define MASTER_NPU_SYS 0 +#define MASTER_NPU_NOC_CFG 1 +#define SLAVE_NPU_CAL_DP0 2 +#define SLAVE_NPU_CP 3 +#define SLAVE_NPU_INT_DMA_BWMON_CFG 4 +#define SLAVE_NPU_DPM 5 +#define SLAVE_ISENSE_CFG 6 +#define SLAVE_NPU_LLM_CFG 7 +#define SLAVE_NPU_TCM 8 +#define SLAVE_NPU_COMPUTE_NOC 9 +#define SLAVE_SERVICE_NPU_NOC 10 + +#define MASTER_QUP_CORE_0 0 +#define MASTER_QUP_CORE_1 1 +#define SLAVE_QUP_CORE_0 2 +#define SLAVE_QUP_CORE_1 3 + +#define MASTER_SNOC_CFG 0 +#define MASTER_A1NOC_SNOC 1 +#define MASTER_A2NOC_SNOC 2 +#define MASTER_GEM_NOC_SNOC 3 +#define MASTER_PIMEM 4 +#define SLAVE_APPSS 5 +#define SLAVE_SNOC_CNOC 6 +#define SLAVE_SNOC_GEM_NOC_GC 7 +#define SLAVE_SNOC_GEM_NOC_SF 8 +#define SLAVE_IMEM 9 +#define SLAVE_PIMEM 10 +#define SLAVE_SERVICE_SNOC 11 +#define SLAVE_QDSS_STM 12 +#define SLAVE_TCU 13 + +#endif -- cgit v1.2.3 From 7a077f7fdaa4fa641c43a554db2811c62048d7f4 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Tue, 3 Mar 2020 19:02:52 +0200 Subject: dt-bindings: interconnect: Add OSM L3 DT bindings Add bindings for Operating State Manager (OSM) L3 interconnect provider on SDM845 SoCs. Reviewed-by: Rob Herring Signed-off-by: Sibi Sankar Link: https://lore.kernel.org/r/20200227105632.15041-3-sibis@codeaurora.org Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,osm-l3.yaml | 61 ++++++++++++++++++++++ include/dt-bindings/interconnect/qcom,osm-l3.h | 12 +++++ 2 files changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml create mode 100644 include/dt-bindings/interconnect/qcom,osm-l3.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml new file mode 100644 index 000000000000..b4d46a1e9257 --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,osm-l3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Operating State Manager (OSM) L3 Interconnect Provider + +maintainers: + - Sibi Sankar + +description: + L3 cache bandwidth requirements on Qualcomm SoCs is serviced by the OSM. + The OSM L3 interconnect provider aggregates the L3 bandwidth requests + from CPU/GPU and relays it to the OSM. + +properties: + compatible: + enum: + - qcom,sdm845-osm-l3 + + reg: + maxItems: 1 + + clocks: + items: + - description: xo clock + - description: alternate clock + + clock-names: + items: + - const: xo + - const: alternate + + '#interconnect-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - '#interconnect-cells' + +additionalProperties: false + +examples: + - | + + #define GPLL0 165 + #define RPMH_CXO_CLK 0 + + osm_l3: interconnect@17d41000 { + compatible = "qcom,sdm845-osm-l3"; + reg = <0x17d41000 0x1400>; + + clocks = <&rpmhcc RPMH_CXO_CLK>, <&gcc GPLL0>; + clock-names = "xo", "alternate"; + + #interconnect-cells = <1>; + }; diff --git a/include/dt-bindings/interconnect/qcom,osm-l3.h b/include/dt-bindings/interconnect/qcom,osm-l3.h new file mode 100644 index 000000000000..54858ff7674d --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,osm-l3.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2019 The Linux Foundation. All rights reserved. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_OSM_L3_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_OSM_L3_H + +#define MASTER_OSM_L3_APPS 0 +#define SLAVE_OSM_L3 1 + +#endif -- cgit v1.2.3 From 4c16d64ea04056f1b1b324ab6916019f6a064114 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 2 Mar 2020 21:05:13 -0800 Subject: fib: add missing attribute validation for tun_id Add missing netlink policy entry for FRA_TUN_ID. Fixes: e7030878fc84 ("fib: Add fib rule match on tunnel id") Signed-off-by: Jakub Kicinski Reviewed-by: David Ahern Signed-off-by: David S. Miller --- include/net/fib_rules.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 54e227e6b06a..a259050f84af 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -108,6 +108,7 @@ struct fib_rule_notifier_info { [FRA_OIFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, \ [FRA_PRIORITY] = { .type = NLA_U32 }, \ [FRA_FWMARK] = { .type = NLA_U32 }, \ + [FRA_TUN_ID] = { .type = NLA_U64 }, \ [FRA_FWMASK] = { .type = NLA_U32 }, \ [FRA_TABLE] = { .type = NLA_U32 }, \ [FRA_SUPPRESS_PREFIXLEN] = { .type = NLA_U32 }, \ -- cgit v1.2.3 From 89b74cac7834734d6b2733204c639917d3826083 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 3 Mar 2020 20:24:50 +0900 Subject: tools/bootconfig: Show line and column in parse error Show line and column when we got a parse error in bootconfig tool. Current lib/bootconfig shows the parse error with byte offset, but that is not human readable. This makes xbc_init() not showing error message itself but able to pass the error message and position to caller, so that the caller can decode it and show the error message with line number and columns. With this patch, bootconfig tool shows an error with line:column as below. $ cat samples/bad-dotword.bconf # do not start keyword with . key { .word = 1 } $ ./bootconfig -a samples/bad-dotword.bconf initrd Parse Error: Invalid keyword at 3:3 Link: http://lkml.kernel.org/r/158323469002.10560.4023923847704522760.stgit@devnote2 Signed-off-by: Masami Hiramatsu Signed-off-by: Steven Rostedt (VMware) --- include/linux/bootconfig.h | 3 ++- init/main.c | 14 ++++++++++---- lib/bootconfig.c | 35 ++++++++++++++++++++++++++--------- tools/bootconfig/main.c | 35 +++++++++++++++++++++++++++++++---- 4 files changed, 69 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h index d11e183fcb54..9903088891fa 100644 --- a/include/linux/bootconfig.h +++ b/include/linux/bootconfig.h @@ -216,7 +216,8 @@ static inline int __init xbc_node_compose_key(struct xbc_node *node, } /* XBC node initializer */ -int __init xbc_init(char *buf); +int __init xbc_init(char *buf, const char **emsg, int *epos); + /* XBC cleanup data structures */ void __init xbc_destroy_all(void); diff --git a/init/main.c b/init/main.c index ee4947af823f..e488213857e2 100644 --- a/init/main.c +++ b/init/main.c @@ -353,6 +353,8 @@ static int __init bootconfig_params(char *param, char *val, static void __init setup_boot_config(const char *cmdline) { static char tmp_cmdline[COMMAND_LINE_SIZE] __initdata; + const char *msg; + int pos; u32 size, csum; char *data, *copy; u32 *hdr; @@ -400,10 +402,14 @@ static void __init setup_boot_config(const char *cmdline) memcpy(copy, data, size); copy[size] = '\0'; - ret = xbc_init(copy); - if (ret < 0) - pr_err("Failed to parse bootconfig\n"); - else { + ret = xbc_init(copy, &msg, &pos); + if (ret < 0) { + if (pos < 0) + pr_err("Failed to init bootconfig: %s.\n", msg); + else + pr_err("Failed to parse bootconfig: %s at %d.\n", + msg, pos); + } else { pr_info("Load bootconfig: %d bytes %d nodes\n", size, ret); /* keys starting with "kernel." are passed via cmdline */ extra_command_line = xbc_make_cmdline("kernel"); diff --git a/lib/bootconfig.c b/lib/bootconfig.c index ec3ce7fd299f..912ef4921398 100644 --- a/lib/bootconfig.c +++ b/lib/bootconfig.c @@ -29,12 +29,14 @@ static int xbc_node_num __initdata; static char *xbc_data __initdata; static size_t xbc_data_size __initdata; static struct xbc_node *last_parent __initdata; +static const char *xbc_err_msg __initdata; +static int xbc_err_pos __initdata; static int __init xbc_parse_error(const char *msg, const char *p) { - int pos = p - xbc_data; + xbc_err_msg = msg; + xbc_err_pos = (int)(p - xbc_data); - pr_err("Parse error at pos %d: %s\n", pos, msg); return -EINVAL; } @@ -738,33 +740,44 @@ void __init xbc_destroy_all(void) /** * xbc_init() - Parse given XBC file and build XBC internal tree * @buf: boot config text + * @emsg: A pointer of const char * to store the error message + * @epos: A pointer of int to store the error position * * This parses the boot config text in @buf. @buf must be a * null terminated string and smaller than XBC_DATA_MAX. * Return the number of stored nodes (>0) if succeeded, or -errno * if there is any error. + * In error cases, @emsg will be updated with an error message and + * @epos will be updated with the error position which is the byte offset + * of @buf. If the error is not a parser error, @epos will be -1. */ -int __init xbc_init(char *buf) +int __init xbc_init(char *buf, const char **emsg, int *epos) { char *p, *q; int ret, c; + if (epos) + *epos = -1; + if (xbc_data) { - pr_err("Error: bootconfig is already initialized.\n"); + if (emsg) + *emsg = "Bootconfig is already initialized"; return -EBUSY; } ret = strlen(buf); if (ret > XBC_DATA_MAX - 1 || ret == 0) { - pr_err("Error: Config data is %s.\n", - ret ? "too big" : "empty"); + if (emsg) + *emsg = ret ? "Config data is too big" : + "Config data is empty"; return -ERANGE; } xbc_nodes = memblock_alloc(sizeof(struct xbc_node) * XBC_NODE_MAX, SMP_CACHE_BYTES); if (!xbc_nodes) { - pr_err("Failed to allocate memory for bootconfig nodes.\n"); + if (emsg) + *emsg = "Failed to allocate bootconfig nodes"; return -ENOMEM; } memset(xbc_nodes, 0, sizeof(struct xbc_node) * XBC_NODE_MAX); @@ -814,9 +827,13 @@ int __init xbc_init(char *buf) if (!ret) ret = xbc_verify_tree(); - if (ret < 0) + if (ret < 0) { + if (epos) + *epos = xbc_err_pos; + if (emsg) + *emsg = xbc_err_msg; xbc_destroy_all(); - else + } else ret = xbc_node_num; return ret; diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c index a9b97814d1a9..16b9a420e6fd 100644 --- a/tools/bootconfig/main.c +++ b/tools/bootconfig/main.c @@ -130,6 +130,7 @@ int load_xbc_from_initrd(int fd, char **buf) int ret; u32 size = 0, csum = 0, rcsum; char magic[BOOTCONFIG_MAGIC_LEN]; + const char *msg; ret = fstat(fd, &stat); if (ret < 0) @@ -182,10 +183,12 @@ int load_xbc_from_initrd(int fd, char **buf) return -EINVAL; } - ret = xbc_init(*buf); + ret = xbc_init(*buf, &msg, NULL); /* Wrong data */ - if (ret < 0) + if (ret < 0) { + pr_err("parse error: %s.\n", msg); return ret; + } return size; } @@ -244,11 +247,34 @@ int delete_xbc(const char *path) return ret; } +static void show_xbc_error(const char *data, const char *msg, int pos) +{ + int lin = 1, col, i; + + if (pos < 0) { + pr_err("Error: %s.\n", msg); + return; + } + + /* Note that pos starts from 0 but lin and col should start from 1. */ + col = pos + 1; + for (i = 0; i < pos; i++) { + if (data[i] == '\n') { + lin++; + col = pos - i; + } + } + pr_err("Parse Error: %s at %d:%d\n", msg, lin, col); + +} + int apply_xbc(const char *path, const char *xbc_path) { u32 size, csum; char *buf, *data; int ret, fd; + const char *msg; + int pos; ret = load_xbc_file(xbc_path, &buf); if (ret < 0) { @@ -267,11 +293,12 @@ int apply_xbc(const char *path, const char *xbc_path) *(u32 *)(data + size + 4) = csum; /* Check the data format */ - ret = xbc_init(buf); + ret = xbc_init(buf, &msg, &pos); if (ret < 0) { - pr_err("Failed to parse %s: %d\n", xbc_path, ret); + show_xbc_error(data, msg, pos); free(data); free(buf); + return ret; } printf("Apply %s to %s\n", xbc_path, path); -- cgit v1.2.3 From bbd9d05618a6d608c72640b1d3d651a75913456a Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Mon, 2 Mar 2020 15:50:44 -0800 Subject: gpu/trace: add a gpu total memory usage tracepoint This change adds the below gpu memory tracepoint: gpu_mem/gpu_mem_total: track global or proc gpu memory total usages Per process tracking of total gpu memory usage in the gem layer is not appropriate and hard to implement with trivial overhead. So for the gfx device driver layer to track total gpu memory usage both globally and per process in an easy and uniform way is to integrate the tracepoint in this patch to the underlying varied implementations of gpu memory tracking system from vendors. Putting this tracepoint in the common trace events can not only help wean the gfx drivers off of debugfs but also greatly help the downstream Android gpu vendors because debugfs is to be deprecated in the upcoming Android release. Then the gpu memory tracking of both Android kernel and the upstream linux kernel can stay closely, which can benefit the whole kernel eco-system in the long term. Link: http://lkml.kernel.org/r/20200302235044.59163-1-zzyiwei@google.com Acked-by: Greg Kroah-Hartman Signed-off-by: Yiwei Zhang Signed-off-by: Steven Rostedt (VMware) --- drivers/Kconfig | 2 ++ drivers/gpu/Makefile | 1 + drivers/gpu/trace/Kconfig | 4 +++ drivers/gpu/trace/Makefile | 3 +++ drivers/gpu/trace/trace_gpu_mem.c | 13 +++++++++ include/trace/events/gpu_mem.h | 57 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 80 insertions(+) create mode 100644 drivers/gpu/trace/Kconfig create mode 100644 drivers/gpu/trace/Makefile create mode 100644 drivers/gpu/trace/trace_gpu_mem.c create mode 100644 include/trace/events/gpu_mem.h (limited to 'include') diff --git a/drivers/Kconfig b/drivers/Kconfig index 8befa53f43be..e0eda1a5c3f9 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -200,6 +200,8 @@ source "drivers/thunderbolt/Kconfig" source "drivers/android/Kconfig" +source "drivers/gpu/trace/Kconfig" + source "drivers/nvdimm/Kconfig" source "drivers/dax/Kconfig" diff --git a/drivers/gpu/Makefile b/drivers/gpu/Makefile index f17d01f076c7..835c88318cec 100644 --- a/drivers/gpu/Makefile +++ b/drivers/gpu/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_TEGRA_HOST1X) += host1x/ obj-y += drm/ vga/ obj-$(CONFIG_IMX_IPUV3_CORE) += ipu-v3/ +obj-$(CONFIG_TRACE_GPU_MEM) += trace/ diff --git a/drivers/gpu/trace/Kconfig b/drivers/gpu/trace/Kconfig new file mode 100644 index 000000000000..c24e9edd022e --- /dev/null +++ b/drivers/gpu/trace/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config TRACE_GPU_MEM + bool diff --git a/drivers/gpu/trace/Makefile b/drivers/gpu/trace/Makefile new file mode 100644 index 000000000000..b70fbdc5847f --- /dev/null +++ b/drivers/gpu/trace/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_TRACE_GPU_MEM) += trace_gpu_mem.o diff --git a/drivers/gpu/trace/trace_gpu_mem.c b/drivers/gpu/trace/trace_gpu_mem.c new file mode 100644 index 000000000000..01e855897b6d --- /dev/null +++ b/drivers/gpu/trace/trace_gpu_mem.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * GPU memory trace points + * + * Copyright (C) 2020 Google, Inc. + */ + +#include + +#define CREATE_TRACE_POINTS +#include + +EXPORT_TRACEPOINT_SYMBOL(gpu_mem_total); diff --git a/include/trace/events/gpu_mem.h b/include/trace/events/gpu_mem.h new file mode 100644 index 000000000000..1897822a9150 --- /dev/null +++ b/include/trace/events/gpu_mem.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * GPU memory trace points + * + * Copyright (C) 2020 Google, Inc. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gpu_mem + +#if !defined(_TRACE_GPU_MEM_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_GPU_MEM_H + +#include + +/* + * The gpu_memory_total event indicates that there's an update to either the + * global or process total gpu memory counters. + * + * This event should be emitted whenever the kernel device driver allocates, + * frees, imports, unimports memory in the GPU addressable space. + * + * @gpu_id: This is the gpu id. + * + * @pid: Put 0 for global total, while positive pid for process total. + * + * @size: Virtual size of the allocation in bytes. + * + */ +TRACE_EVENT(gpu_mem_total, + + TP_PROTO(uint32_t gpu_id, uint32_t pid, uint64_t size), + + TP_ARGS(gpu_id, pid, size), + + TP_STRUCT__entry( + __field(uint32_t, gpu_id) + __field(uint32_t, pid) + __field(uint64_t, size) + ), + + TP_fast_assign( + __entry->gpu_id = gpu_id; + __entry->pid = pid; + __entry->size = size; + ), + + TP_printk("gpu_id=%u pid=%u size=%llu", + __entry->gpu_id, + __entry->pid, + __entry->size) +); + +#endif /* _TRACE_GPU_MEM_H */ + +/* This part must be outside protection */ +#include -- cgit v1.2.3 From c34b961a249211bdb08d03bdecfb31ff22eb002f Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Tue, 3 Mar 2020 15:07:49 +0200 Subject: net/sched: act_ct: Create nf flow table per zone Use the NF flow tables infrastructure for CT offload. Create a nf flow table per zone. Next patches will add FT entries to this table, and do the software offload. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/tc_act/tc_ct.h | 2 + net/sched/Kconfig | 2 +- net/sched/act_ct.c | 134 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 136 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h index a8b156402873..cf3492e2a6a4 100644 --- a/include/net/tc_act/tc_ct.h +++ b/include/net/tc_act/tc_ct.h @@ -25,6 +25,8 @@ struct tcf_ct_params { u16 ct_action; struct rcu_head rcu; + + struct tcf_ct_flow_table *ct_ft; }; struct tcf_ct { diff --git a/net/sched/Kconfig b/net/sched/Kconfig index edde0e519438..bfbefb7bff9d 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -972,7 +972,7 @@ config NET_ACT_TUNNEL_KEY config NET_ACT_CT tristate "connection tracking tc action" - depends on NET_CLS_ACT && NF_CONNTRACK && NF_NAT + depends on NET_CLS_ACT && NF_CONNTRACK && NF_NAT && NF_FLOW_TABLE help Say Y here to allow sending the packets to conntrack module. diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index f685c0d73708..3321087cb93f 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include +#include #include #include #include @@ -31,6 +33,108 @@ #include #include +static struct workqueue_struct *act_ct_wq; +static struct rhashtable zones_ht; +static DEFINE_SPINLOCK(zones_lock); + +struct tcf_ct_flow_table { + struct rhash_head node; /* In zones tables */ + + struct rcu_work rwork; + struct nf_flowtable nf_ft; + u16 zone; + u32 ref; + + bool dying; +}; + +static const struct rhashtable_params zones_params = { + .head_offset = offsetof(struct tcf_ct_flow_table, node), + .key_offset = offsetof(struct tcf_ct_flow_table, zone), + .key_len = sizeof_field(struct tcf_ct_flow_table, zone), + .automatic_shrinking = true, +}; + +static struct nf_flowtable_type flowtable_ct = { + .owner = THIS_MODULE, +}; + +static int tcf_ct_flow_table_get(struct tcf_ct_params *params) +{ + struct tcf_ct_flow_table *ct_ft; + int err = -ENOMEM; + + spin_lock_bh(&zones_lock); + ct_ft = rhashtable_lookup_fast(&zones_ht, ¶ms->zone, zones_params); + if (ct_ft) + goto take_ref; + + ct_ft = kzalloc(sizeof(*ct_ft), GFP_ATOMIC); + if (!ct_ft) + goto err_alloc; + + ct_ft->zone = params->zone; + err = rhashtable_insert_fast(&zones_ht, &ct_ft->node, zones_params); + if (err) + goto err_insert; + + ct_ft->nf_ft.type = &flowtable_ct; + err = nf_flow_table_init(&ct_ft->nf_ft); + if (err) + goto err_init; + + __module_get(THIS_MODULE); +take_ref: + params->ct_ft = ct_ft; + ct_ft->ref++; + spin_unlock_bh(&zones_lock); + + return 0; + +err_init: + rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params); +err_insert: + kfree(ct_ft); +err_alloc: + spin_unlock_bh(&zones_lock); + return err; +} + +static void tcf_ct_flow_table_cleanup_work(struct work_struct *work) +{ + struct tcf_ct_flow_table *ct_ft; + + ct_ft = container_of(to_rcu_work(work), struct tcf_ct_flow_table, + rwork); + nf_flow_table_free(&ct_ft->nf_ft); + kfree(ct_ft); + + module_put(THIS_MODULE); +} + +static void tcf_ct_flow_table_put(struct tcf_ct_params *params) +{ + struct tcf_ct_flow_table *ct_ft = params->ct_ft; + + spin_lock_bh(&zones_lock); + if (--params->ct_ft->ref == 0) { + rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params); + INIT_RCU_WORK(&ct_ft->rwork, tcf_ct_flow_table_cleanup_work); + queue_rcu_work(act_ct_wq, &ct_ft->rwork); + } + spin_unlock_bh(&zones_lock); +} + +static int tcf_ct_flow_tables_init(void) +{ + return rhashtable_init(&zones_ht, &zones_params); +} + +static void tcf_ct_flow_tables_uninit(void) +{ + rhashtable_destroy(&zones_ht); +} + static struct tc_action_ops act_ct_ops; static unsigned int ct_net_id; @@ -207,6 +311,8 @@ static void tcf_ct_params_free(struct rcu_head *head) struct tcf_ct_params *params = container_of(head, struct tcf_ct_params, rcu); + tcf_ct_flow_table_put(params); + if (params->tmpl) nf_conntrack_put(¶ms->tmpl->ct_general); kfree(params); @@ -730,6 +836,10 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla, if (err) goto cleanup; + err = tcf_ct_flow_table_get(params); + if (err) + goto cleanup; + spin_lock_bh(&c->tcf_lock); goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch); params = rcu_replace_pointer(c->params, params, @@ -974,12 +1084,34 @@ static struct pernet_operations ct_net_ops = { static int __init ct_init_module(void) { - return tcf_register_action(&act_ct_ops, &ct_net_ops); + int err; + + act_ct_wq = alloc_ordered_workqueue("act_ct_workqueue", 0); + if (!act_ct_wq) + return -ENOMEM; + + err = tcf_ct_flow_tables_init(); + if (err) + goto err_tbl_init; + + err = tcf_register_action(&act_ct_ops, &ct_net_ops); + if (err) + goto err_register; + + return 0; + +err_tbl_init: + destroy_workqueue(act_ct_wq); +err_register: + tcf_ct_flow_tables_uninit(); + return err; } static void __exit ct_cleanup_module(void) { tcf_unregister_action(&act_ct_ops, &ct_net_ops); + tcf_ct_flow_tables_uninit(); + destroy_workqueue(act_ct_wq); } module_init(ct_init_module); -- cgit v1.2.3 From acf1ee44ca5da39755d2aa9080392eae46a0eb34 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Tue, 3 Mar 2020 08:12:42 -0600 Subject: devlink: Introduce devlink port flavour virtual Currently mlx5 PCI PF and VF devlink devices register their ports as physical port in non-representors mode. Introduce a new port flavour as virtual so that virtual devices can register 'virtual' flavour to make it more clear to users. An example of one PCI PF and 2 PCI virtual functions, each having one devlink port. $ devlink port show pci/0000:06:00.0/1: type eth netdev ens2f0 flavour physical port 0 pci/0000:06:00.2/1: type eth netdev ens2f2 flavour virtual port 0 pci/0000:06:00.3/1: type eth netdev ens2f3 flavour virtual port 0 Reviewed-by: Jiri Pirko Signed-off-by: Parav Pandit Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/uapi/linux/devlink.h | 1 + net/core/devlink.c | 2 ++ 2 files changed, 3 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index be2a2948f452..dfdffc42e87d 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -187,6 +187,7 @@ enum devlink_port_flavour { * for the PCI VF. It is an internal * port that faces the PCI VF. */ + DEVLINK_PORT_FLAVOUR_VIRTUAL, /* Any virtual port facing the user. */ }; enum devlink_param_cmode { diff --git a/net/core/devlink.c b/net/core/devlink.c index 295d761cbfb1..e8ccea9035c8 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -545,6 +545,7 @@ static int devlink_nl_port_attrs_put(struct sk_buff *msg, case DEVLINK_PORT_FLAVOUR_PHYSICAL: case DEVLINK_PORT_FLAVOUR_CPU: case DEVLINK_PORT_FLAVOUR_DSA: + case DEVLINK_PORT_FLAVOUR_VIRTUAL: if (nla_put_u32(msg, DEVLINK_ATTR_PORT_NUMBER, attrs->phys.port_number)) return -EMSGSIZE; @@ -6806,6 +6807,7 @@ static int __devlink_port_phys_port_name_get(struct devlink_port *devlink_port, switch (attrs->flavour) { case DEVLINK_PORT_FLAVOUR_PHYSICAL: + case DEVLINK_PORT_FLAVOUR_VIRTUAL: if (!attrs->split) n = snprintf(name, len, "p%u", attrs->phys.port_number); else -- cgit v1.2.3 From cf62089b0edd7e74a1f474844b4d9f7b5697fb5c Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 3 Mar 2020 15:05:01 -0500 Subject: bpf: Add gso_size to __sk_buff BPF programs may want to know whether an skb is gso. The canonical answer is skb_is_gso(skb), which tests that gso_size != 0. Expose this field in the same manner as gso_segs. That field itself is not a sufficient signal, as the comment in skb_shared_info makes clear: gso_segs may be zero, e.g., from dodgy sources. Also prepare net/bpf/test_run for upcoming BPF_PROG_TEST_RUN tests of the feature. Signed-off-by: Willem de Bruijn Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200303200503.226217-2-willemdebruijn.kernel@gmail.com --- include/uapi/linux/bpf.h | 1 + net/bpf/test_run.c | 7 +++++++ net/core/filter.c | 44 ++++++++++++++++++++++++++++++-------------- 3 files changed, 38 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 8e98ced0963b..180337fae97e 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -3176,6 +3176,7 @@ struct __sk_buff { __u32 wire_len; __u32 gso_segs; __bpf_md_ptr(struct bpf_sock *, sk); + __u32 gso_size; }; struct bpf_tunnel_key { diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 562443f94133..1cd7a1c2f8b2 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -277,6 +277,12 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) /* gso_segs is allowed */ if (!range_is_zero(__skb, offsetofend(struct __sk_buff, gso_segs), + offsetof(struct __sk_buff, gso_size))) + return -EINVAL; + + /* gso_size is allowed */ + + if (!range_is_zero(__skb, offsetofend(struct __sk_buff, gso_size), sizeof(struct __sk_buff))) return -EINVAL; @@ -297,6 +303,7 @@ static int convert___skb_to_skb(struct sk_buff *skb, struct __sk_buff *__skb) if (__skb->gso_segs > GSO_MAX_SEGS) return -EINVAL; skb_shinfo(skb)->gso_segs = __skb->gso_segs; + skb_shinfo(skb)->gso_size = __skb->gso_size; return 0; } diff --git a/net/core/filter.c b/net/core/filter.c index 4a08c9fb2be7..cd0a532db4e7 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -7139,6 +7139,27 @@ static u32 flow_dissector_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } +static struct bpf_insn *bpf_convert_shinfo_access(const struct bpf_insn *si, + struct bpf_insn *insn) +{ + /* si->dst_reg = skb_shinfo(SKB); */ +#ifdef NET_SKBUFF_DATA_USES_OFFSET + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end), + BPF_REG_AX, si->src_reg, + offsetof(struct sk_buff, end)); + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, head), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, head)); + *insn++ = BPF_ALU64_REG(BPF_ADD, si->dst_reg, BPF_REG_AX); +#else + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, end)); +#endif + + return insn; +} + static u32 bpf_convert_ctx_access(enum bpf_access_type type, const struct bpf_insn *si, struct bpf_insn *insn_buf, @@ -7461,26 +7482,21 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, break; case offsetof(struct __sk_buff, gso_segs): - /* si->dst_reg = skb_shinfo(SKB); */ -#ifdef NET_SKBUFF_DATA_USES_OFFSET - *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end), - BPF_REG_AX, si->src_reg, - offsetof(struct sk_buff, end)); - *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, head), - si->dst_reg, si->src_reg, - offsetof(struct sk_buff, head)); - *insn++ = BPF_ALU64_REG(BPF_ADD, si->dst_reg, BPF_REG_AX); -#else - *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, end), - si->dst_reg, si->src_reg, - offsetof(struct sk_buff, end)); -#endif + insn = bpf_convert_shinfo_access(si, insn); *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct skb_shared_info, gso_segs), si->dst_reg, si->dst_reg, bpf_target_off(struct skb_shared_info, gso_segs, 2, target_size)); break; + case offsetof(struct __sk_buff, gso_size): + insn = bpf_convert_shinfo_access(si, insn); + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct skb_shared_info, gso_size), + si->dst_reg, si->dst_reg, + bpf_target_off(struct skb_shared_info, + gso_size, 2, + target_size)); + break; case offsetof(struct __sk_buff, wire_len): BUILD_BUG_ON(sizeof_field(struct qdisc_skb_cb, pkt_len) != 4); -- cgit v1.2.3 From a56d7a345dd67995ba415a26a0164a72780f2d02 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:31:06 +0200 Subject: net: mscc: ocelot: simplify tc-flower offload structures The ocelot tc-flower offload binds a second flow block callback (apart from the one for matchall) just because it uses a different block private structure (ocelot_port_private for matchall, ocelot_port_block for flower). But ocelot_port_block just appears to be boilerplate, and doesn't help with anything in particular at all, it's just useless glue between the (global!) struct ocelot_acl_block *block pointer, and a per-netdevice struct ocelot_port_private *priv. So let's just simplify that, and make struct ocelot_port_private be the private structure for the block offload. This makes us able to use the same flow callback as in the case of matchall. This also reveals that the struct ocelot_acl_block *block is used rather strangely, as mentioned above: it is defined globally, allocated at probe time, and freed at unbind time. So just move the structure to the main ocelot structure, which gives further opportunity for simplification. Also get rid of backpointers from struct ocelot_acl_block and struct ocelot_ace_rule back to struct ocelot, by reworking the function prototypes, where necessary, to use a more DSA-friendly "struct ocelot *ocelot, int port" format. And finally, remove the debugging prints that were added during development, since they provide no useful information at this point. Signed-off-by: Vladimir Oltean Tested-by: Horatiu Vultur Reviewed-by: Allan W. Nielsen Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 1 - drivers/net/ethernet/mscc/ocelot_ace.c | 81 ++++++---------- drivers/net/ethernet/mscc/ocelot_ace.h | 24 ++--- drivers/net/ethernet/mscc/ocelot_flower.c | 155 ++++++------------------------ drivers/net/ethernet/mscc/ocelot_tc.c | 22 +---- include/soc/mscc/ocelot.h | 7 ++ 6 files changed, 75 insertions(+), 215 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 86d543ab1ab9..fe5a0ff4ee2f 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2493,7 +2493,6 @@ void ocelot_deinit(struct ocelot *ocelot) cancel_delayed_work(&ocelot->stats_work); destroy_workqueue(ocelot->stats_queue); mutex_destroy(&ocelot->stats_lock); - ocelot_ace_deinit(); if (ocelot->ptp_clock) ptp_clock_unregister(ocelot->ptp_clock); diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c index 18670645d47f..375c7c6aa7d5 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.c +++ b/drivers/net/ethernet/mscc/ocelot_ace.c @@ -12,8 +12,6 @@ #define OCELOT_POLICER_DISCARD 0x17f -static struct ocelot_acl_block *acl_block; - struct vcap_props { const char *name; /* Symbolic name */ u16 tg_width; /* Type-group width (in bits) */ @@ -574,15 +572,15 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, vcap_row_cmd(ocelot, row, VCAP_CMD_WRITE, VCAP_SEL_ALL); } -static void is2_entry_get(struct ocelot_ace_rule *rule, int ix) +static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, + int ix) { - struct ocelot *op = rule->ocelot; struct vcap_data data; int row = (ix / 2); u32 cnt; - vcap_row_cmd(op, row, VCAP_CMD_READ, VCAP_SEL_COUNTER); - vcap_cache2action(op, &data); + vcap_row_cmd(ocelot, row, VCAP_CMD_READ, VCAP_SEL_COUNTER); + vcap_cache2action(ocelot, &data); data.tg_sw = VCAP_TG_HALF; is2_data_get(&data, ix); cnt = vcap_data_get(data.counter, data.counter_offset, @@ -641,25 +639,27 @@ ocelot_ace_rule_get_rule_index(struct ocelot_acl_block *block, int index) return NULL; } -int ocelot_ace_rule_offload_add(struct ocelot_ace_rule *rule) +int ocelot_ace_rule_offload_add(struct ocelot *ocelot, + struct ocelot_ace_rule *rule) { + struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_ace_rule *ace; int i, index; /* Add rule to the linked list */ - ocelot_ace_rule_add(acl_block, rule); + ocelot_ace_rule_add(block, rule); /* Get the index of the inserted rule */ - index = ocelot_ace_rule_get_index_id(acl_block, rule); + index = ocelot_ace_rule_get_index_id(block, rule); /* Move down the rules to make place for the new rule */ - for (i = acl_block->count - 1; i > index; i--) { - ace = ocelot_ace_rule_get_rule_index(acl_block, i); - is2_entry_set(rule->ocelot, i, ace); + for (i = block->count - 1; i > index; i--) { + ace = ocelot_ace_rule_get_rule_index(block, i); + is2_entry_set(ocelot, i, ace); } /* Now insert the new rule */ - is2_entry_set(rule->ocelot, index, rule); + is2_entry_set(ocelot, index, rule); return 0; } @@ -680,8 +680,10 @@ static void ocelot_ace_rule_del(struct ocelot_acl_block *block, block->count--; } -int ocelot_ace_rule_offload_del(struct ocelot_ace_rule *rule) +int ocelot_ace_rule_offload_del(struct ocelot *ocelot, + struct ocelot_ace_rule *rule) { + struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_ace_rule del_ace; struct ocelot_ace_rule *ace; int i, index; @@ -689,59 +691,41 @@ int ocelot_ace_rule_offload_del(struct ocelot_ace_rule *rule) memset(&del_ace, 0, sizeof(del_ace)); /* Gets index of the rule */ - index = ocelot_ace_rule_get_index_id(acl_block, rule); + index = ocelot_ace_rule_get_index_id(block, rule); /* Delete rule */ - ocelot_ace_rule_del(acl_block, rule); + ocelot_ace_rule_del(block, rule); /* Move up all the blocks over the deleted rule */ - for (i = index; i < acl_block->count; i++) { - ace = ocelot_ace_rule_get_rule_index(acl_block, i); - is2_entry_set(rule->ocelot, i, ace); + for (i = index; i < block->count; i++) { + ace = ocelot_ace_rule_get_rule_index(block, i); + is2_entry_set(ocelot, i, ace); } /* Now delete the last rule, because it is duplicated */ - is2_entry_set(rule->ocelot, acl_block->count, &del_ace); + is2_entry_set(ocelot, block->count, &del_ace); return 0; } -int ocelot_ace_rule_stats_update(struct ocelot_ace_rule *rule) +int ocelot_ace_rule_stats_update(struct ocelot *ocelot, + struct ocelot_ace_rule *rule) { + struct ocelot_acl_block *block = &ocelot->acl_block; struct ocelot_ace_rule *tmp; int index; - index = ocelot_ace_rule_get_index_id(acl_block, rule); - is2_entry_get(rule, index); + index = ocelot_ace_rule_get_index_id(block, rule); + is2_entry_get(ocelot, rule, index); /* After we get the result we need to clear the counters */ - tmp = ocelot_ace_rule_get_rule_index(acl_block, index); + tmp = ocelot_ace_rule_get_rule_index(block, index); tmp->stats.pkts = 0; - is2_entry_set(rule->ocelot, index, tmp); + is2_entry_set(ocelot, index, tmp); return 0; } -static struct ocelot_acl_block *ocelot_acl_block_create(struct ocelot *ocelot) -{ - struct ocelot_acl_block *block; - - block = kzalloc(sizeof(*block), GFP_KERNEL); - if (!block) - return NULL; - - INIT_LIST_HEAD(&block->rules); - block->count = 0; - block->ocelot = ocelot; - - return block; -} - -static void ocelot_acl_block_destroy(struct ocelot_acl_block *block) -{ - kfree(block); -} - int ocelot_ace_init(struct ocelot *ocelot) { struct vcap_data data; @@ -771,12 +755,7 @@ int ocelot_ace_init(struct ocelot *ocelot) ocelot_write_gix(ocelot, 0x3fffff, ANA_POL_CIR_STATE, OCELOT_POLICER_DISCARD); - acl_block = ocelot_acl_block_create(ocelot); + INIT_LIST_HEAD(&ocelot->acl_block.rules); return 0; } - -void ocelot_ace_deinit(void) -{ - ocelot_acl_block_destroy(acl_block); -} diff --git a/drivers/net/ethernet/mscc/ocelot_ace.h b/drivers/net/ethernet/mscc/ocelot_ace.h index 2927ac83741b..b9a5868e3f15 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.h +++ b/drivers/net/ethernet/mscc/ocelot_ace.h @@ -186,7 +186,6 @@ struct ocelot_ace_stats { struct ocelot_ace_rule { struct list_head list; - struct ocelot *ocelot; u16 prio; u32 id; @@ -211,22 +210,17 @@ struct ocelot_ace_rule { } frame; }; -struct ocelot_acl_block { - struct list_head rules; - struct ocelot *ocelot; - int count; -}; - -int ocelot_ace_rule_offload_add(struct ocelot_ace_rule *rule); -int ocelot_ace_rule_offload_del(struct ocelot_ace_rule *rule); -int ocelot_ace_rule_stats_update(struct ocelot_ace_rule *rule); +int ocelot_ace_rule_offload_add(struct ocelot *ocelot, + struct ocelot_ace_rule *rule); +int ocelot_ace_rule_offload_del(struct ocelot *ocelot, + struct ocelot_ace_rule *rule); +int ocelot_ace_rule_stats_update(struct ocelot *ocelot, + struct ocelot_ace_rule *rule); int ocelot_ace_init(struct ocelot *ocelot); -void ocelot_ace_deinit(void); -int ocelot_setup_tc_block_flower_bind(struct ocelot_port_private *priv, - struct flow_block_offload *f); -void ocelot_setup_tc_block_flower_unbind(struct ocelot_port_private *priv, - struct flow_block_offload *f); +int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, + struct flow_cls_offload *f, + bool ingress); #endif /* _MSCC_OCELOT_ACE_H_ */ diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index ffd2bb50cfc3..b9673df6dbc5 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -8,11 +8,6 @@ #include "ocelot_ace.h" -struct ocelot_port_block { - struct ocelot_acl_block *block; - struct ocelot_port_private *priv; -}; - static int ocelot_flower_parse_action(struct flow_cls_offload *f, struct ocelot_ace_rule *rule) { @@ -168,8 +163,8 @@ finished_key_parsing: } static -struct ocelot_ace_rule *ocelot_ace_rule_create(struct flow_cls_offload *f, - struct ocelot_port_block *block) +struct ocelot_ace_rule *ocelot_ace_rule_create(struct ocelot *ocelot, int port, + struct flow_cls_offload *f) { struct ocelot_ace_rule *rule; @@ -177,18 +172,17 @@ struct ocelot_ace_rule *ocelot_ace_rule_create(struct flow_cls_offload *f, if (!rule) return NULL; - rule->ocelot = block->priv->port.ocelot; - rule->ingress_port_mask = BIT(block->priv->chip_port); + rule->ingress_port_mask = BIT(port); return rule; } -static int ocelot_flower_replace(struct flow_cls_offload *f, - struct ocelot_port_block *port_block) +int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, + struct flow_cls_offload *f, bool ingress) { struct ocelot_ace_rule *rule; int ret; - rule = ocelot_ace_rule_create(f, port_block); + rule = ocelot_ace_rule_create(ocelot, port, f); if (!rule) return -ENOMEM; @@ -198,159 +192,66 @@ static int ocelot_flower_replace(struct flow_cls_offload *f, return ret; } - ret = ocelot_ace_rule_offload_add(rule); + ret = ocelot_ace_rule_offload_add(ocelot, rule); if (ret) return ret; - port_block->priv->tc.offload_cnt++; return 0; } +EXPORT_SYMBOL_GPL(ocelot_cls_flower_replace); -static int ocelot_flower_destroy(struct flow_cls_offload *f, - struct ocelot_port_block *port_block) +int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, + struct flow_cls_offload *f, bool ingress) { struct ocelot_ace_rule rule; int ret; rule.prio = f->common.prio; - rule.ocelot = port_block->priv->port.ocelot; rule.id = f->cookie; - ret = ocelot_ace_rule_offload_del(&rule); + ret = ocelot_ace_rule_offload_del(ocelot, &rule); if (ret) return ret; - port_block->priv->tc.offload_cnt--; return 0; } +EXPORT_SYMBOL_GPL(ocelot_cls_flower_destroy); -static int ocelot_flower_stats_update(struct flow_cls_offload *f, - struct ocelot_port_block *port_block) +int ocelot_cls_flower_stats(struct ocelot *ocelot, int port, + struct flow_cls_offload *f, bool ingress) { struct ocelot_ace_rule rule; int ret; rule.prio = f->common.prio; - rule.ocelot = port_block->priv->port.ocelot; rule.id = f->cookie; - ret = ocelot_ace_rule_stats_update(&rule); + ret = ocelot_ace_rule_stats_update(ocelot, &rule); if (ret) return ret; flow_stats_update(&f->stats, 0x0, rule.stats.pkts, 0x0); return 0; } +EXPORT_SYMBOL_GPL(ocelot_cls_flower_stats); -static int ocelot_setup_tc_cls_flower(struct flow_cls_offload *f, - struct ocelot_port_block *port_block) +int ocelot_setup_tc_cls_flower(struct ocelot_port_private *priv, + struct flow_cls_offload *f, + bool ingress) { + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + if (!ingress) + return -EOPNOTSUPP; + switch (f->command) { case FLOW_CLS_REPLACE: - return ocelot_flower_replace(f, port_block); + return ocelot_cls_flower_replace(ocelot, port, f, ingress); case FLOW_CLS_DESTROY: - return ocelot_flower_destroy(f, port_block); + return ocelot_cls_flower_destroy(ocelot, port, f, ingress); case FLOW_CLS_STATS: - return ocelot_flower_stats_update(f, port_block); + return ocelot_cls_flower_stats(ocelot, port, f, ingress); default: return -EOPNOTSUPP; } } - -static int ocelot_setup_tc_block_cb_flower(enum tc_setup_type type, - void *type_data, void *cb_priv) -{ - struct ocelot_port_block *port_block = cb_priv; - - if (!tc_cls_can_offload_and_chain0(port_block->priv->dev, type_data)) - return -EOPNOTSUPP; - - switch (type) { - case TC_SETUP_CLSFLOWER: - return ocelot_setup_tc_cls_flower(type_data, cb_priv); - case TC_SETUP_CLSMATCHALL: - return 0; - default: - return -EOPNOTSUPP; - } -} - -static struct ocelot_port_block* -ocelot_port_block_create(struct ocelot_port_private *priv) -{ - struct ocelot_port_block *port_block; - - port_block = kzalloc(sizeof(*port_block), GFP_KERNEL); - if (!port_block) - return NULL; - - port_block->priv = priv; - - return port_block; -} - -static void ocelot_port_block_destroy(struct ocelot_port_block *block) -{ - kfree(block); -} - -static void ocelot_tc_block_unbind(void *cb_priv) -{ - struct ocelot_port_block *port_block = cb_priv; - - ocelot_port_block_destroy(port_block); -} - -int ocelot_setup_tc_block_flower_bind(struct ocelot_port_private *priv, - struct flow_block_offload *f) -{ - struct ocelot_port_block *port_block; - struct flow_block_cb *block_cb; - int ret; - - if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) - return -EOPNOTSUPP; - - block_cb = flow_block_cb_lookup(f->block, - ocelot_setup_tc_block_cb_flower, priv); - if (!block_cb) { - port_block = ocelot_port_block_create(priv); - if (!port_block) - return -ENOMEM; - - block_cb = flow_block_cb_alloc(ocelot_setup_tc_block_cb_flower, - priv, port_block, - ocelot_tc_block_unbind); - if (IS_ERR(block_cb)) { - ret = PTR_ERR(block_cb); - goto err_cb_register; - } - flow_block_cb_add(block_cb, f); - list_add_tail(&block_cb->driver_list, f->driver_block_list); - } else { - port_block = flow_block_cb_priv(block_cb); - } - - flow_block_cb_incref(block_cb); - return 0; - -err_cb_register: - ocelot_port_block_destroy(port_block); - - return ret; -} - -void ocelot_setup_tc_block_flower_unbind(struct ocelot_port_private *priv, - struct flow_block_offload *f) -{ - struct flow_block_cb *block_cb; - - block_cb = flow_block_cb_lookup(f->block, - ocelot_setup_tc_block_cb_flower, priv); - if (!block_cb) - return; - - if (!flow_block_cb_decref(block_cb)) { - flow_block_cb_remove(block_cb, f); - list_del(&block_cb->driver_list); - } -} diff --git a/drivers/net/ethernet/mscc/ocelot_tc.c b/drivers/net/ethernet/mscc/ocelot_tc.c index a4f7fbd76507..3ff5ef41eccf 100644 --- a/drivers/net/ethernet/mscc/ocelot_tc.c +++ b/drivers/net/ethernet/mscc/ocelot_tc.c @@ -20,9 +20,6 @@ static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv, int port = priv->chip_port; int err; - netdev_dbg(priv->dev, "%s: port %u command %d cookie %lu\n", - __func__, port, f->command, f->cookie); - if (!ingress) { NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); return -EOPNOTSUPP; @@ -99,17 +96,10 @@ static int ocelot_setup_tc_block_cb(enum tc_setup_type type, switch (type) { case TC_SETUP_CLSMATCHALL: - netdev_dbg(priv->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n", - ingress ? "ingress" : "egress"); - return ocelot_setup_tc_cls_matchall(priv, type_data, ingress); case TC_SETUP_CLSFLOWER: - return 0; + return ocelot_setup_tc_cls_flower(priv, type_data, ingress); default: - netdev_dbg(priv->dev, "tc_block_cb: type %d %s\n", - type, - ingress ? "ingress" : "egress"); - return -EOPNOTSUPP; } } @@ -137,10 +127,6 @@ static int ocelot_setup_tc_block(struct ocelot_port_private *priv, { struct flow_block_cb *block_cb; flow_setup_cb_t *cb; - int err; - - netdev_dbg(priv->dev, "tc_block command %d, binder_type %d\n", - f->command, f->binder_type); if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) { cb = ocelot_setup_tc_block_cb_ig; @@ -162,11 +148,6 @@ static int ocelot_setup_tc_block(struct ocelot_port_private *priv, if (IS_ERR(block_cb)) return PTR_ERR(block_cb); - err = ocelot_setup_tc_block_flower_bind(priv, f); - if (err < 0) { - flow_block_cb_free(block_cb); - return err; - } flow_block_cb_add(block_cb, f); list_add_tail(&block_cb->driver_list, f->driver_block_list); return 0; @@ -175,7 +156,6 @@ static int ocelot_setup_tc_block(struct ocelot_port_private *priv, if (!block_cb) return -ENOENT; - ocelot_setup_tc_block_flower_unbind(priv, f); flow_block_cb_remove(block_cb, f); list_del(&block_cb->driver_list); return 0; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 068f96b1a83e..74e7c63adad4 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -406,6 +406,11 @@ struct ocelot_ops { int (*reset)(struct ocelot *ocelot); }; +struct ocelot_acl_block { + struct list_head rules; + int count; +}; + struct ocelot_port { struct ocelot *ocelot; @@ -455,6 +460,8 @@ struct ocelot { struct list_head multicast; + struct ocelot_acl_block acl_block; + /* Workqueue to check statistics for overflow with its lock */ struct mutex stats_lock; u64 *stats; -- cgit v1.2.3 From e0632940bc4c986f2dc9c8ee6aba65c14e30c762 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:31:10 +0200 Subject: net: mscc: ocelot: don't rely on preprocessor for vcap key/action packing The IGR_PORT_MASK key width is different between the 11-port VSC7514 and the 6-port VSC9959 switches. And since IGR_PORT_MASK is one of the first fields of a VCAP key entry, it means that all further field offset/length pairs are shifted between the 2. The ocelot driver performs packing of VCAP half keys with the help of some preprocessor macros: - A set of macros for defining the HKO (Half Key Offset) and HKL (Half Key Length) of each possible key field. The offset of each field is defined as the sum between the offset and the sum of the previous field. - A set of accessors on top of vcap_key_set for shorter (aka less typing) access to the HKO and HKL of each key field. Since the field offsets and lengths are different between switches, defining them through the preprocessor isn't going to fly. So introduce a structure holding (offset, length) pairs and instantiate it in ocelot_board.c for VSC7514. In a future patch, a similar structure will be instantiated in felix_vsc9959.c for NXP LS1028A. The accessors also need to go. They are based on macro name concatenation, which is horrible to understand and follow. Signed-off-by: Vladimir Oltean Tested-by: Horatiu Vultur Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot_ace.c | 315 +++++++++++++++--------- drivers/net/ethernet/mscc/ocelot_board.c | 102 ++++++++ drivers/net/ethernet/mscc/ocelot_vcap.h | 403 ------------------------------- include/soc/mscc/ocelot.h | 3 + include/soc/mscc/ocelot_vcap.h | 184 ++++++++++++++ 5 files changed, 484 insertions(+), 523 deletions(-) delete mode 100644 drivers/net/ethernet/mscc/ocelot_vcap.h create mode 100644 include/soc/mscc/ocelot_vcap.h (limited to 'include') diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c index ec86d29d8be4..9922033a2aaf 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.c +++ b/drivers/net/ethernet/mscc/ocelot_ace.c @@ -6,8 +6,8 @@ #include #include +#include #include "ocelot_ace.h" -#include "ocelot_vcap.h" #include "ocelot_s2.h" #define OCELOT_POLICER_DISCARD 0x17f @@ -47,7 +47,7 @@ static const struct vcap_props vcap_is2 = { .action_type_width = 1, .action_table = { { - .width = (IS2_AO_ACL_ID + IS2_AL_ACL_ID), + .width = 49, .count = 2 }, { @@ -243,22 +243,39 @@ static u32 vcap_data_get(u32 *data, u32 offset, u32 len) return value; } -static void vcap_key_set(struct vcap_data *data, u32 offset, u32 width, - u32 value, u32 mask) +static void vcap_key_field_set(struct vcap_data *data, u32 offset, u32 width, + u32 value, u32 mask) { vcap_data_set(data->entry, offset + data->key_offset, width, value); vcap_data_set(data->mask, offset + data->key_offset, width, mask); } -static void vcap_key_bytes_set(struct vcap_data *data, u32 offset, u8 *val, - u8 *msk, u32 count) +static void vcap_key_set(struct ocelot *ocelot, struct vcap_data *data, + enum vcap_is2_half_key_field field, + u32 value, u32 mask) +{ + u32 offset = ocelot->vcap_is2_keys[field].offset; + u32 length = ocelot->vcap_is2_keys[field].length; + + vcap_key_field_set(data, offset, length, value, mask); +} + +static void vcap_key_bytes_set(struct ocelot *ocelot, struct vcap_data *data, + enum vcap_is2_half_key_field field, + u8 *val, u8 *msk) { + u32 offset = ocelot->vcap_is2_keys[field].offset; + u32 count = ocelot->vcap_is2_keys[field].length; u32 i, j, n = 0, value = 0, mask = 0; + WARN_ON(count % 8); + /* Data wider than 32 bits are split up in chunks of maximum 32 bits. * The 32 LSB of the data are written to the 32 MSB of the TCAM. */ - offset += (count * 8); + offset += count; + count /= 8; + for (i = 0; i < count; i++) { j = (count - i - 1); value += (val[j] << n); @@ -266,7 +283,7 @@ static void vcap_key_bytes_set(struct vcap_data *data, u32 offset, u8 *val, n += 8; if (n == ENTRY_WIDTH || (i + 1) == count) { offset -= n; - vcap_key_set(data, offset, n, value, mask); + vcap_key_field_set(data, offset, n, value, mask); n = 0; value = 0; mask = 0; @@ -274,55 +291,62 @@ static void vcap_key_bytes_set(struct vcap_data *data, u32 offset, u8 *val, } } -static void vcap_key_l4_port_set(struct vcap_data *data, u32 offset, +static void vcap_key_l4_port_set(struct ocelot *ocelot, struct vcap_data *data, + enum vcap_is2_half_key_field field, struct ocelot_vcap_udp_tcp *port) { - vcap_key_set(data, offset, 16, port->value, port->mask); + u32 offset = ocelot->vcap_is2_keys[field].offset; + u32 length = ocelot->vcap_is2_keys[field].length; + + WARN_ON(length != 16); + + vcap_key_field_set(data, offset, length, port->value, port->mask); } -static void vcap_key_bit_set(struct vcap_data *data, u32 offset, +static void vcap_key_bit_set(struct ocelot *ocelot, struct vcap_data *data, + enum vcap_is2_half_key_field field, enum ocelot_vcap_bit val) { - vcap_key_set(data, offset, 1, val == OCELOT_VCAP_BIT_1 ? 1 : 0, - val == OCELOT_VCAP_BIT_ANY ? 0 : 1); -} + u32 offset = ocelot->vcap_is2_keys[field].offset; + u32 length = ocelot->vcap_is2_keys[field].length; + u32 value = (val == OCELOT_VCAP_BIT_1 ? 1 : 0); + u32 msk = (val == OCELOT_VCAP_BIT_ANY ? 0 : 1); -#define VCAP_KEY_SET(fld, val, msk) \ - vcap_key_set(&data, IS2_HKO_##fld, IS2_HKL_##fld, val, msk) -#define VCAP_KEY_ANY_SET(fld) \ - vcap_key_set(&data, IS2_HKO_##fld, IS2_HKL_##fld, 0, 0) -#define VCAP_KEY_BIT_SET(fld, val) vcap_key_bit_set(&data, IS2_HKO_##fld, val) -#define VCAP_KEY_BYTES_SET(fld, val, msk) \ - vcap_key_bytes_set(&data, IS2_HKO_##fld, val, msk, IS2_HKL_##fld / 8) + WARN_ON(length != 1); -static void vcap_action_set(struct vcap_data *data, u32 offset, u32 width, - u32 value) -{ - vcap_data_set(data->action, offset + data->action_offset, width, value); + vcap_key_field_set(data, offset, length, value, msk); } -#define VCAP_ACT_SET(fld, val) \ - vcap_action_set(data, IS2_AO_##fld, IS2_AL_##fld, val) +static void vcap_action_set(struct ocelot *ocelot, struct vcap_data *data, + enum vcap_is2_action_field field, u32 value) +{ + int offset = ocelot->vcap_is2_actions[field].offset; + int length = ocelot->vcap_is2_actions[field].length; + + vcap_data_set(data->action, offset + data->action_offset, length, + value); +} -static void is2_action_set(struct vcap_data *data, +static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, enum ocelot_ace_action action) { switch (action) { case OCELOT_ACL_ACTION_DROP: - VCAP_ACT_SET(PORT_MASK, 0x0); - VCAP_ACT_SET(MASK_MODE, 0x1); - VCAP_ACT_SET(POLICE_ENA, 0x1); - VCAP_ACT_SET(POLICE_IDX, OCELOT_POLICER_DISCARD); - VCAP_ACT_SET(CPU_QU_NUM, 0x0); - VCAP_ACT_SET(CPU_COPY_ENA, 0x0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 1); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_IDX, + OCELOT_POLICER_DISCARD); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 0); break; case OCELOT_ACL_ACTION_TRAP: - VCAP_ACT_SET(PORT_MASK, 0x0); - VCAP_ACT_SET(MASK_MODE, 0x1); - VCAP_ACT_SET(POLICE_ENA, 0x0); - VCAP_ACT_SET(POLICE_IDX, 0x0); - VCAP_ACT_SET(CPU_QU_NUM, 0x0); - VCAP_ACT_SET(CPU_COPY_ENA, 0x1); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_PORT_MASK, 0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_MASK_MODE, 1); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_ENA, 0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_POLICE_IDX, 0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_QU_NUM, 0); + vcap_action_set(ocelot, data, VCAP_IS2_ACT_CPU_COPY_ENA, 1); break; } } @@ -352,53 +376,69 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, data.type = IS2_ACTION_TYPE_NORMAL; - VCAP_KEY_ANY_SET(PAG); - VCAP_KEY_SET(IGR_PORT_MASK, 0, ~ace->ingress_port_mask); - VCAP_KEY_BIT_SET(FIRST, OCELOT_VCAP_BIT_1); - VCAP_KEY_BIT_SET(HOST_MATCH, OCELOT_VCAP_BIT_ANY); - VCAP_KEY_BIT_SET(L2_MC, ace->dmac_mc); - VCAP_KEY_BIT_SET(L2_BC, ace->dmac_bc); - VCAP_KEY_BIT_SET(VLAN_TAGGED, tag->tagged); - VCAP_KEY_SET(VID, tag->vid.value, tag->vid.mask); - VCAP_KEY_SET(PCP, tag->pcp.value[0], tag->pcp.mask[0]); - VCAP_KEY_BIT_SET(DEI, tag->dei); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_PAG, 0, 0); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_IGR_PORT_MASK, 0, + ~ace->ingress_port_mask); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_FIRST, OCELOT_VCAP_BIT_1); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_HOST_MATCH, + OCELOT_VCAP_BIT_ANY); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_MC, ace->dmac_mc); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L2_BC, ace->dmac_bc); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_VLAN_TAGGED, tag->tagged); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_VID, + tag->vid.value, tag->vid.mask); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_PCP, + tag->pcp.value[0], tag->pcp.mask[0]); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_DEI, tag->dei); switch (ace->type) { case OCELOT_ACE_TYPE_ETYPE: { struct ocelot_ace_frame_etype *etype = &ace->frame.etype; type = IS2_TYPE_ETYPE; - VCAP_KEY_BYTES_SET(L2_DMAC, etype->dmac.value, - etype->dmac.mask); - VCAP_KEY_BYTES_SET(L2_SMAC, etype->smac.value, - etype->smac.mask); - VCAP_KEY_BYTES_SET(MAC_ETYPE_ETYPE, etype->etype.value, - etype->etype.mask); - VCAP_KEY_ANY_SET(MAC_ETYPE_L2_PAYLOAD); // Clear unused bits - vcap_key_bytes_set(&data, IS2_HKO_MAC_ETYPE_L2_PAYLOAD, - etype->data.value, etype->data.mask, 2); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, + etype->dmac.value, etype->dmac.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC, + etype->smac.value, etype->smac.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_ETYPE_ETYPE, + etype->etype.value, etype->etype.mask); + /* Clear unused bits */ + vcap_key_set(ocelot, &data, VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0, + 0, 0); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1, + 0, 0); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2, + 0, 0); + vcap_key_bytes_set(ocelot, &data, + VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0, + etype->data.value, etype->data.mask); break; } case OCELOT_ACE_TYPE_LLC: { struct ocelot_ace_frame_llc *llc = &ace->frame.llc; type = IS2_TYPE_LLC; - VCAP_KEY_BYTES_SET(L2_DMAC, llc->dmac.value, llc->dmac.mask); - VCAP_KEY_BYTES_SET(L2_SMAC, llc->smac.value, llc->smac.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, + llc->dmac.value, llc->dmac.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC, + llc->smac.value, llc->smac.mask); for (i = 0; i < 4; i++) { payload.value[i] = llc->llc.value[i]; payload.mask[i] = llc->llc.mask[i]; } - VCAP_KEY_BYTES_SET(MAC_LLC_L2_LLC, payload.value, payload.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_LLC_L2_LLC, + payload.value, payload.mask); break; } case OCELOT_ACE_TYPE_SNAP: { struct ocelot_ace_frame_snap *snap = &ace->frame.snap; type = IS2_TYPE_SNAP; - VCAP_KEY_BYTES_SET(L2_DMAC, snap->dmac.value, snap->dmac.mask); - VCAP_KEY_BYTES_SET(L2_SMAC, snap->smac.value, snap->smac.mask); - VCAP_KEY_BYTES_SET(MAC_SNAP_L2_SNAP, + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_DMAC, + snap->dmac.value, snap->dmac.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L2_SMAC, + snap->smac.value, snap->smac.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_SNAP_L2_SNAP, ace->frame.snap.snap.value, ace->frame.snap.snap.mask); break; @@ -407,26 +447,42 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, struct ocelot_ace_frame_arp *arp = &ace->frame.arp; type = IS2_TYPE_ARP; - VCAP_KEY_BYTES_SET(MAC_ARP_L2_SMAC, arp->smac.value, - arp->smac.mask); - VCAP_KEY_BIT_SET(MAC_ARP_ARP_ADDR_SPACE_OK, arp->ethernet); - VCAP_KEY_BIT_SET(MAC_ARP_ARP_PROTO_SPACE_OK, arp->ip); - VCAP_KEY_BIT_SET(MAC_ARP_ARP_LEN_OK, arp->length); - VCAP_KEY_BIT_SET(MAC_ARP_ARP_TGT_MATCH, arp->dmac_match); - VCAP_KEY_BIT_SET(MAC_ARP_ARP_SENDER_MATCH, arp->smac_match); - VCAP_KEY_BIT_SET(MAC_ARP_ARP_OPCODE_UNKNOWN, arp->unknown); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_MAC_ARP_SMAC, + arp->smac.value, arp->smac.mask); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK, + arp->ethernet); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK, + arp->ip); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_LEN_OK, + arp->length); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_TARGET_MATCH, + arp->dmac_match); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_SENDER_MATCH, + arp->smac_match); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN, + arp->unknown); /* OPCODE is inverse, bit 0 is reply flag, bit 1 is RARP flag */ val = ((arp->req == OCELOT_VCAP_BIT_0 ? 1 : 0) | (arp->arp == OCELOT_VCAP_BIT_0 ? 2 : 0)); msk = ((arp->req == OCELOT_VCAP_BIT_ANY ? 0 : 1) | (arp->arp == OCELOT_VCAP_BIT_ANY ? 0 : 2)); - VCAP_KEY_SET(MAC_ARP_ARP_OPCODE, val, msk); - vcap_key_bytes_set(&data, IS2_HKO_MAC_ARP_L3_IP4_DIP, - arp->dip.value.addr, arp->dip.mask.addr, 4); - vcap_key_bytes_set(&data, IS2_HKO_MAC_ARP_L3_IP4_SIP, - arp->sip.value.addr, arp->sip.mask.addr, 4); - VCAP_KEY_ANY_SET(MAC_ARP_DIP_EQ_SIP); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_MAC_ARP_OPCODE, + val, msk); + vcap_key_bytes_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP, + arp->dip.value.addr, arp->dip.mask.addr); + vcap_key_bytes_set(ocelot, &data, + VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP, + arp->sip.value.addr, arp->sip.mask.addr); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP, + 0, 0); break; } case OCELOT_ACE_TYPE_IPV4: @@ -494,18 +550,23 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, seq_zero = ipv6->seq_zero; } - VCAP_KEY_BIT_SET(IP4, + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_IP4, ipv4 ? OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); - VCAP_KEY_BIT_SET(L3_FRAGMENT, fragment); - VCAP_KEY_ANY_SET(L3_FRAG_OFS_GT0); - VCAP_KEY_BIT_SET(L3_OPTIONS, options); - VCAP_KEY_BIT_SET(L3_TTL_GT0, ttl); - VCAP_KEY_BYTES_SET(L3_TOS, ds.value, ds.mask); - vcap_key_bytes_set(&data, IS2_HKO_L3_IP4_DIP, dip.value.addr, - dip.mask.addr, 4); - vcap_key_bytes_set(&data, IS2_HKO_L3_IP4_SIP, sip.value.addr, - sip.mask.addr, 4); - VCAP_KEY_BIT_SET(DIP_EQ_SIP, sip_eq_dip); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L3_FRAGMENT, + fragment); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_L3_FRAG_OFS_GT0, 0, 0); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L3_OPTIONS, + options); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_IP4_L3_TTL_GT0, + ttl); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L3_TOS, + ds.value, ds.mask); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L3_IP4_DIP, + dip.value.addr, dip.mask.addr); + vcap_key_bytes_set(ocelot, &data, VCAP_IS2_HK_L3_IP4_SIP, + sip.value.addr, sip.mask.addr); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_DIP_EQ_SIP, + sip_eq_dip); val = proto.value[0]; msk = proto.mask[0]; type = IS2_TYPE_IP_UDP_TCP; @@ -513,25 +574,34 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, /* UDP/TCP protocol match */ tcp = (val == 6 ? OCELOT_VCAP_BIT_1 : OCELOT_VCAP_BIT_0); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_TCP, tcp); - vcap_key_l4_port_set(&data, - IS2_HKO_IP4_TCP_UDP_L4_DPORT, - dport); - vcap_key_l4_port_set(&data, - IS2_HKO_IP4_TCP_UDP_L4_SPORT, - sport); - VCAP_KEY_ANY_SET(IP4_TCP_UDP_L4_RNG); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_SPORT_EQ_DPORT, + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_TCP, tcp); + vcap_key_l4_port_set(ocelot, &data, + VCAP_IS2_HK_L4_DPORT, dport); + vcap_key_l4_port_set(ocelot, &data, + VCAP_IS2_HK_L4_SPORT, sport); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_L4_RNG, 0, 0); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_L4_SPORT_EQ_DPORT, sport_eq_dport); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_SEQUENCE_EQ0, seq_zero); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_L4_FIN, tcp_fin); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_L4_SYN, tcp_syn); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_L4_RST, tcp_rst); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_L4_PSH, tcp_psh); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_L4_ACK, tcp_ack); - VCAP_KEY_BIT_SET(IP4_TCP_UDP_L4_URG, tcp_urg); - VCAP_KEY_ANY_SET(IP4_TCP_UDP_L4_1588_DOM); - VCAP_KEY_ANY_SET(IP4_TCP_UDP_L4_1588_VER); + vcap_key_bit_set(ocelot, &data, + VCAP_IS2_HK_L4_SEQUENCE_EQ0, + seq_zero); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L4_FIN, + tcp_fin); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L4_SYN, + tcp_syn); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L4_RST, + tcp_rst); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L4_PSH, + tcp_psh); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L4_ACK, + tcp_ack); + vcap_key_bit_set(ocelot, &data, VCAP_IS2_HK_L4_URG, + tcp_urg); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_L4_1588_DOM, + 0, 0); + vcap_key_set(ocelot, &data, VCAP_IS2_HK_L4_1588_VER, + 0, 0); } else { if (msk == 0) { /* Any IP protocol match */ @@ -544,10 +614,12 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, payload.mask[i] = ip_data->mask[i]; } } - VCAP_KEY_BYTES_SET(IP4_OTHER_L3_PROTO, proto.value, - proto.mask); - VCAP_KEY_BYTES_SET(IP4_OTHER_L3_PAYLOAD, payload.value, - payload.mask); + vcap_key_bytes_set(ocelot, &data, + VCAP_IS2_HK_IP4_L3_PROTO, + proto.value, proto.mask); + vcap_key_bytes_set(ocelot, &data, + VCAP_IS2_HK_L3_PAYLOAD, + payload.value, payload.mask); } break; } @@ -556,18 +628,20 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, type = 0; type_mask = 0; count = (vcap_is2.entry_width / 2); - for (i = (IS2_HKO_PCP + IS2_HKL_PCP); i < count; - i += ENTRY_WIDTH) { - /* Clear entry data */ - vcap_key_set(&data, i, min(32u, count - i), 0, 0); + /* Iterate over the non-common part of the key and + * clear entry data + */ + for (i = ocelot->vcap_is2_keys[VCAP_IS2_HK_L2_DMAC].offset; + i < count; i += ENTRY_WIDTH) { + vcap_key_field_set(&data, i, min(32u, count - i), 0, 0); } break; } - VCAP_KEY_SET(TYPE, type, type_mask); - is2_action_set(&data, ace->action); - vcap_data_set(data.counter, data.counter_offset, vcap_is2.counter_width, - ace->stats.pkts); + vcap_key_set(ocelot, &data, VCAP_IS2_TYPE, type, type_mask); + is2_action_set(ocelot, &data, ace->action); + vcap_data_set(data.counter, data.counter_offset, + vcap_is2.counter_width, ace->stats.pkts); /* Write row */ vcap_entry2cache(ocelot, &data); @@ -734,6 +808,7 @@ int ocelot_ace_init(struct ocelot *ocelot) struct vcap_data data; memset(&data, 0, sizeof(data)); + vcap_entry2cache(ocelot, &data); ocelot_write(ocelot, vcap_is2.entry_count, S2_CORE_MV_CFG); vcap_cmd(ocelot, 0, VCAP_CMD_INITIALIZE, VCAP_SEL_ENTRY); diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c index 1135a18019c7..c236936dbde7 100644 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ b/drivers/net/ethernet/mscc/ocelot_board.c @@ -14,6 +14,7 @@ #include #include +#include #include "ocelot.h" #define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) @@ -262,6 +263,104 @@ static const struct ocelot_ops ocelot_ops = { .reset = ocelot_reset, }; +static const struct vcap_field vsc7514_vcap_is2_keys[] = { + /* Common: 46 bits */ + [VCAP_IS2_TYPE] = { 0, 4}, + [VCAP_IS2_HK_FIRST] = { 4, 1}, + [VCAP_IS2_HK_PAG] = { 5, 8}, + [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 12}, + [VCAP_IS2_HK_RSV2] = { 25, 1}, + [VCAP_IS2_HK_HOST_MATCH] = { 26, 1}, + [VCAP_IS2_HK_L2_MC] = { 27, 1}, + [VCAP_IS2_HK_L2_BC] = { 28, 1}, + [VCAP_IS2_HK_VLAN_TAGGED] = { 29, 1}, + [VCAP_IS2_HK_VID] = { 30, 12}, + [VCAP_IS2_HK_DEI] = { 42, 1}, + [VCAP_IS2_HK_PCP] = { 43, 3}, + /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ + [VCAP_IS2_HK_L2_DMAC] = { 46, 48}, + [VCAP_IS2_HK_L2_SMAC] = { 94, 48}, + /* MAC_ETYPE (TYPE=000) */ + [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {142, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {158, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {174, 8}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {182, 3}, + /* MAC_LLC (TYPE=001) */ + [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {142, 40}, + /* MAC_SNAP (TYPE=010) */ + [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {142, 40}, + /* MAC_ARP (TYPE=011) */ + [VCAP_IS2_HK_MAC_ARP_SMAC] = { 46, 48}, + [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 94, 1}, + [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 95, 1}, + [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 96, 1}, + [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 97, 1}, + [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 98, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 99, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE] = {100, 2}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = {102, 32}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {134, 32}, + [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {166, 1}, + /* IP4_TCP_UDP / IP4_OTHER common */ + [VCAP_IS2_HK_IP4] = { 46, 1}, + [VCAP_IS2_HK_L3_FRAGMENT] = { 47, 1}, + [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 48, 1}, + [VCAP_IS2_HK_L3_OPTIONS] = { 49, 1}, + [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 50, 1}, + [VCAP_IS2_HK_L3_TOS] = { 51, 8}, + [VCAP_IS2_HK_L3_IP4_DIP] = { 59, 32}, + [VCAP_IS2_HK_L3_IP4_SIP] = { 91, 32}, + [VCAP_IS2_HK_DIP_EQ_SIP] = {123, 1}, + /* IP4_TCP_UDP (TYPE=100) */ + [VCAP_IS2_HK_TCP] = {124, 1}, + [VCAP_IS2_HK_L4_SPORT] = {125, 16}, + [VCAP_IS2_HK_L4_DPORT] = {141, 16}, + [VCAP_IS2_HK_L4_RNG] = {157, 8}, + [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {165, 1}, + [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {166, 1}, + [VCAP_IS2_HK_L4_URG] = {167, 1}, + [VCAP_IS2_HK_L4_ACK] = {168, 1}, + [VCAP_IS2_HK_L4_PSH] = {169, 1}, + [VCAP_IS2_HK_L4_RST] = {170, 1}, + [VCAP_IS2_HK_L4_SYN] = {171, 1}, + [VCAP_IS2_HK_L4_FIN] = {172, 1}, + [VCAP_IS2_HK_L4_1588_DOM] = {173, 8}, + [VCAP_IS2_HK_L4_1588_VER] = {181, 4}, + /* IP4_OTHER (TYPE=101) */ + [VCAP_IS2_HK_IP4_L3_PROTO] = {124, 8}, + [VCAP_IS2_HK_L3_PAYLOAD] = {132, 56}, + /* IP6_STD (TYPE=110) */ + [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 46, 1}, + [VCAP_IS2_HK_L3_IP6_SIP] = { 47, 128}, + [VCAP_IS2_HK_IP6_L3_PROTO] = {175, 8}, + /* OAM (TYPE=111) */ + [VCAP_IS2_HK_OAM_MEL_FLAGS] = {142, 7}, + [VCAP_IS2_HK_OAM_VER] = {149, 5}, + [VCAP_IS2_HK_OAM_OPCODE] = {154, 8}, + [VCAP_IS2_HK_OAM_FLAGS] = {162, 8}, + [VCAP_IS2_HK_OAM_MEPID] = {170, 16}, + [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {186, 1}, + [VCAP_IS2_HK_OAM_IS_Y1731] = {187, 1}, +}; + +static const struct vcap_field vsc7514_vcap_is2_actions[] = { + [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, + [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, + [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, + [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, + [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, + [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, + [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, + [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9}, + [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1}, + [VCAP_IS2_ACT_PORT_MASK] = { 20, 11}, + [VCAP_IS2_ACT_REW_OP] = { 31, 9}, + [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1}, + [VCAP_IS2_ACT_RSV] = { 41, 2}, + [VCAP_IS2_ACT_ACL_ID] = { 43, 6}, + [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, +}; + static int mscc_ocelot_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -362,6 +461,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ocelot->ports = devm_kcalloc(&pdev->dev, ocelot->num_phys_ports, sizeof(struct ocelot_port *), GFP_KERNEL); + ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys; + ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions; + ocelot_init(ocelot); ocelot_set_cpu_port(ocelot, ocelot->num_phys_ports, OCELOT_TAG_PREFIX_NONE, OCELOT_TAG_PREFIX_NONE); diff --git a/drivers/net/ethernet/mscc/ocelot_vcap.h b/drivers/net/ethernet/mscc/ocelot_vcap.h deleted file mode 100644 index e22eac1da783..000000000000 --- a/drivers/net/ethernet/mscc/ocelot_vcap.h +++ /dev/null @@ -1,403 +0,0 @@ -/* SPDX-License-Identifier: (GPL-2.0 OR MIT) - * Microsemi Ocelot Switch driver - * Copyright (c) 2019 Microsemi Corporation - */ - -#ifndef _OCELOT_VCAP_H_ -#define _OCELOT_VCAP_H_ - -/* ================================================================= - * VCAP Common - * ================================================================= - */ - -/* VCAP Type-Group values */ -#define VCAP_TG_NONE 0 /* Entry is invalid */ -#define VCAP_TG_FULL 1 /* Full entry */ -#define VCAP_TG_HALF 2 /* Half entry */ -#define VCAP_TG_QUARTER 3 /* Quarter entry */ - -/* ================================================================= - * VCAP IS2 - * ================================================================= - */ - -#define VCAP_IS2_CNT 64 -#define VCAP_IS2_ENTRY_WIDTH 376 -#define VCAP_IS2_ACTION_WIDTH 99 -#define VCAP_PORT_CNT 11 - -/* IS2 half key types */ -#define IS2_TYPE_ETYPE 0 -#define IS2_TYPE_LLC 1 -#define IS2_TYPE_SNAP 2 -#define IS2_TYPE_ARP 3 -#define IS2_TYPE_IP_UDP_TCP 4 -#define IS2_TYPE_IP_OTHER 5 -#define IS2_TYPE_IPV6 6 -#define IS2_TYPE_OAM 7 -#define IS2_TYPE_SMAC_SIP6 8 -#define IS2_TYPE_ANY 100 /* Pseudo type */ - -/* IS2 half key type mask for matching any IP */ -#define IS2_TYPE_MASK_IP_ANY 0xe - -/* IS2 action types */ -#define IS2_ACTION_TYPE_NORMAL 0 -#define IS2_ACTION_TYPE_SMAC_SIP 1 - -/* IS2 MASK_MODE values */ -#define IS2_ACT_MASK_MODE_NONE 0 -#define IS2_ACT_MASK_MODE_FILTER 1 -#define IS2_ACT_MASK_MODE_POLICY 2 -#define IS2_ACT_MASK_MODE_REDIR 3 - -/* IS2 REW_OP values */ -#define IS2_ACT_REW_OP_NONE 0 -#define IS2_ACT_REW_OP_PTP_ONE 2 -#define IS2_ACT_REW_OP_PTP_TWO 3 -#define IS2_ACT_REW_OP_SPECIAL 8 -#define IS2_ACT_REW_OP_PTP_ORG 9 -#define IS2_ACT_REW_OP_PTP_ONE_SUB_DELAY_1 (IS2_ACT_REW_OP_PTP_ONE | (1 << 3)) -#define IS2_ACT_REW_OP_PTP_ONE_SUB_DELAY_2 (IS2_ACT_REW_OP_PTP_ONE | (2 << 3)) -#define IS2_ACT_REW_OP_PTP_ONE_ADD_DELAY (IS2_ACT_REW_OP_PTP_ONE | (1 << 5)) -#define IS2_ACT_REW_OP_PTP_ONE_ADD_SUB BIT(7) - -#define VCAP_PORT_WIDTH 4 - -/* IS2 quarter key - SMAC_SIP4 */ -#define IS2_QKO_IGR_PORT 0 -#define IS2_QKL_IGR_PORT VCAP_PORT_WIDTH -#define IS2_QKO_L2_SMAC (IS2_QKO_IGR_PORT + IS2_QKL_IGR_PORT) -#define IS2_QKL_L2_SMAC 48 -#define IS2_QKO_L3_IP4_SIP (IS2_QKO_L2_SMAC + IS2_QKL_L2_SMAC) -#define IS2_QKL_L3_IP4_SIP 32 - -/* IS2 half key - common */ -#define IS2_HKO_TYPE 0 -#define IS2_HKL_TYPE 4 -#define IS2_HKO_FIRST (IS2_HKO_TYPE + IS2_HKL_TYPE) -#define IS2_HKL_FIRST 1 -#define IS2_HKO_PAG (IS2_HKO_FIRST + IS2_HKL_FIRST) -#define IS2_HKL_PAG 8 -#define IS2_HKO_IGR_PORT_MASK (IS2_HKO_PAG + IS2_HKL_PAG) -#define IS2_HKL_IGR_PORT_MASK (VCAP_PORT_CNT + 1) -#define IS2_HKO_SERVICE_FRM (IS2_HKO_IGR_PORT_MASK + IS2_HKL_IGR_PORT_MASK) -#define IS2_HKL_SERVICE_FRM 1 -#define IS2_HKO_HOST_MATCH (IS2_HKO_SERVICE_FRM + IS2_HKL_SERVICE_FRM) -#define IS2_HKL_HOST_MATCH 1 -#define IS2_HKO_L2_MC (IS2_HKO_HOST_MATCH + IS2_HKL_HOST_MATCH) -#define IS2_HKL_L2_MC 1 -#define IS2_HKO_L2_BC (IS2_HKO_L2_MC + IS2_HKL_L2_MC) -#define IS2_HKL_L2_BC 1 -#define IS2_HKO_VLAN_TAGGED (IS2_HKO_L2_BC + IS2_HKL_L2_BC) -#define IS2_HKL_VLAN_TAGGED 1 -#define IS2_HKO_VID (IS2_HKO_VLAN_TAGGED + IS2_HKL_VLAN_TAGGED) -#define IS2_HKL_VID 12 -#define IS2_HKO_DEI (IS2_HKO_VID + IS2_HKL_VID) -#define IS2_HKL_DEI 1 -#define IS2_HKO_PCP (IS2_HKO_DEI + IS2_HKL_DEI) -#define IS2_HKL_PCP 3 - -/* IS2 half key - MAC_ETYPE/MAC_LLC/MAC_SNAP/OAM common */ -#define IS2_HKO_L2_DMAC (IS2_HKO_PCP + IS2_HKL_PCP) -#define IS2_HKL_L2_DMAC 48 -#define IS2_HKO_L2_SMAC (IS2_HKO_L2_DMAC + IS2_HKL_L2_DMAC) -#define IS2_HKL_L2_SMAC 48 - -/* IS2 half key - MAC_ETYPE */ -#define IS2_HKO_MAC_ETYPE_ETYPE (IS2_HKO_L2_SMAC + IS2_HKL_L2_SMAC) -#define IS2_HKL_MAC_ETYPE_ETYPE 16 -#define IS2_HKO_MAC_ETYPE_L2_PAYLOAD \ - (IS2_HKO_MAC_ETYPE_ETYPE + IS2_HKL_MAC_ETYPE_ETYPE) -#define IS2_HKL_MAC_ETYPE_L2_PAYLOAD 27 - -/* IS2 half key - MAC_LLC */ -#define IS2_HKO_MAC_LLC_L2_LLC IS2_HKO_MAC_ETYPE_ETYPE -#define IS2_HKL_MAC_LLC_L2_LLC 40 - -/* IS2 half key - MAC_SNAP */ -#define IS2_HKO_MAC_SNAP_L2_SNAP IS2_HKO_MAC_ETYPE_ETYPE -#define IS2_HKL_MAC_SNAP_L2_SNAP 40 - -/* IS2 half key - ARP */ -#define IS2_HKO_MAC_ARP_L2_SMAC IS2_HKO_L2_DMAC -#define IS2_HKL_MAC_ARP_L2_SMAC 48 -#define IS2_HKO_MAC_ARP_ARP_ADDR_SPACE_OK \ - (IS2_HKO_MAC_ARP_L2_SMAC + IS2_HKL_MAC_ARP_L2_SMAC) -#define IS2_HKL_MAC_ARP_ARP_ADDR_SPACE_OK 1 -#define IS2_HKO_MAC_ARP_ARP_PROTO_SPACE_OK \ - (IS2_HKO_MAC_ARP_ARP_ADDR_SPACE_OK + IS2_HKL_MAC_ARP_ARP_ADDR_SPACE_OK) -#define IS2_HKL_MAC_ARP_ARP_PROTO_SPACE_OK 1 -#define IS2_HKO_MAC_ARP_ARP_LEN_OK \ - (IS2_HKO_MAC_ARP_ARP_PROTO_SPACE_OK + \ - IS2_HKL_MAC_ARP_ARP_PROTO_SPACE_OK) -#define IS2_HKL_MAC_ARP_ARP_LEN_OK 1 -#define IS2_HKO_MAC_ARP_ARP_TGT_MATCH \ - (IS2_HKO_MAC_ARP_ARP_LEN_OK + IS2_HKL_MAC_ARP_ARP_LEN_OK) -#define IS2_HKL_MAC_ARP_ARP_TGT_MATCH 1 -#define IS2_HKO_MAC_ARP_ARP_SENDER_MATCH \ - (IS2_HKO_MAC_ARP_ARP_TGT_MATCH + IS2_HKL_MAC_ARP_ARP_TGT_MATCH) -#define IS2_HKL_MAC_ARP_ARP_SENDER_MATCH 1 -#define IS2_HKO_MAC_ARP_ARP_OPCODE_UNKNOWN \ - (IS2_HKO_MAC_ARP_ARP_SENDER_MATCH + IS2_HKL_MAC_ARP_ARP_SENDER_MATCH) -#define IS2_HKL_MAC_ARP_ARP_OPCODE_UNKNOWN 1 -#define IS2_HKO_MAC_ARP_ARP_OPCODE \ - (IS2_HKO_MAC_ARP_ARP_OPCODE_UNKNOWN + \ - IS2_HKL_MAC_ARP_ARP_OPCODE_UNKNOWN) -#define IS2_HKL_MAC_ARP_ARP_OPCODE 2 -#define IS2_HKO_MAC_ARP_L3_IP4_DIP \ - (IS2_HKO_MAC_ARP_ARP_OPCODE + IS2_HKL_MAC_ARP_ARP_OPCODE) -#define IS2_HKL_MAC_ARP_L3_IP4_DIP 32 -#define IS2_HKO_MAC_ARP_L3_IP4_SIP \ - (IS2_HKO_MAC_ARP_L3_IP4_DIP + IS2_HKL_MAC_ARP_L3_IP4_DIP) -#define IS2_HKL_MAC_ARP_L3_IP4_SIP 32 -#define IS2_HKO_MAC_ARP_DIP_EQ_SIP \ - (IS2_HKO_MAC_ARP_L3_IP4_SIP + IS2_HKL_MAC_ARP_L3_IP4_SIP) -#define IS2_HKL_MAC_ARP_DIP_EQ_SIP 1 - -/* IS2 half key - IP4_TCP_UDP/IP4_OTHER common */ -#define IS2_HKO_IP4 IS2_HKO_L2_DMAC -#define IS2_HKL_IP4 1 -#define IS2_HKO_L3_FRAGMENT (IS2_HKO_IP4 + IS2_HKL_IP4) -#define IS2_HKL_L3_FRAGMENT 1 -#define IS2_HKO_L3_FRAG_OFS_GT0 (IS2_HKO_L3_FRAGMENT + IS2_HKL_L3_FRAGMENT) -#define IS2_HKL_L3_FRAG_OFS_GT0 1 -#define IS2_HKO_L3_OPTIONS (IS2_HKO_L3_FRAG_OFS_GT0 + IS2_HKL_L3_FRAG_OFS_GT0) -#define IS2_HKL_L3_OPTIONS 1 -#define IS2_HKO_L3_TTL_GT0 (IS2_HKO_L3_OPTIONS + IS2_HKL_L3_OPTIONS) -#define IS2_HKL_L3_TTL_GT0 1 -#define IS2_HKO_L3_TOS (IS2_HKO_L3_TTL_GT0 + IS2_HKL_L3_TTL_GT0) -#define IS2_HKL_L3_TOS 8 -#define IS2_HKO_L3_IP4_DIP (IS2_HKO_L3_TOS + IS2_HKL_L3_TOS) -#define IS2_HKL_L3_IP4_DIP 32 -#define IS2_HKO_L3_IP4_SIP (IS2_HKO_L3_IP4_DIP + IS2_HKL_L3_IP4_DIP) -#define IS2_HKL_L3_IP4_SIP 32 -#define IS2_HKO_DIP_EQ_SIP (IS2_HKO_L3_IP4_SIP + IS2_HKL_L3_IP4_SIP) -#define IS2_HKL_DIP_EQ_SIP 1 - -/* IS2 half key - IP4_TCP_UDP */ -#define IS2_HKO_IP4_TCP_UDP_TCP (IS2_HKO_DIP_EQ_SIP + IS2_HKL_DIP_EQ_SIP) -#define IS2_HKL_IP4_TCP_UDP_TCP 1 -#define IS2_HKO_IP4_TCP_UDP_L4_DPORT \ - (IS2_HKO_IP4_TCP_UDP_TCP + IS2_HKL_IP4_TCP_UDP_TCP) -#define IS2_HKL_IP4_TCP_UDP_L4_DPORT 16 -#define IS2_HKO_IP4_TCP_UDP_L4_SPORT \ - (IS2_HKO_IP4_TCP_UDP_L4_DPORT + IS2_HKL_IP4_TCP_UDP_L4_DPORT) -#define IS2_HKL_IP4_TCP_UDP_L4_SPORT 16 -#define IS2_HKO_IP4_TCP_UDP_L4_RNG \ - (IS2_HKO_IP4_TCP_UDP_L4_SPORT + IS2_HKL_IP4_TCP_UDP_L4_SPORT) -#define IS2_HKL_IP4_TCP_UDP_L4_RNG 8 -#define IS2_HKO_IP4_TCP_UDP_SPORT_EQ_DPORT \ - (IS2_HKO_IP4_TCP_UDP_L4_RNG + IS2_HKL_IP4_TCP_UDP_L4_RNG) -#define IS2_HKL_IP4_TCP_UDP_SPORT_EQ_DPORT 1 -#define IS2_HKO_IP4_TCP_UDP_SEQUENCE_EQ0 \ - (IS2_HKO_IP4_TCP_UDP_SPORT_EQ_DPORT + \ - IS2_HKL_IP4_TCP_UDP_SPORT_EQ_DPORT) -#define IS2_HKL_IP4_TCP_UDP_SEQUENCE_EQ0 1 -#define IS2_HKO_IP4_TCP_UDP_L4_FIN \ - (IS2_HKO_IP4_TCP_UDP_SEQUENCE_EQ0 + IS2_HKL_IP4_TCP_UDP_SEQUENCE_EQ0) -#define IS2_HKL_IP4_TCP_UDP_L4_FIN 1 -#define IS2_HKO_IP4_TCP_UDP_L4_SYN \ - (IS2_HKO_IP4_TCP_UDP_L4_FIN + IS2_HKL_IP4_TCP_UDP_L4_FIN) -#define IS2_HKL_IP4_TCP_UDP_L4_SYN 1 -#define IS2_HKO_IP4_TCP_UDP_L4_RST \ - (IS2_HKO_IP4_TCP_UDP_L4_SYN + IS2_HKL_IP4_TCP_UDP_L4_SYN) -#define IS2_HKL_IP4_TCP_UDP_L4_RST 1 -#define IS2_HKO_IP4_TCP_UDP_L4_PSH \ - (IS2_HKO_IP4_TCP_UDP_L4_RST + IS2_HKL_IP4_TCP_UDP_L4_RST) -#define IS2_HKL_IP4_TCP_UDP_L4_PSH 1 -#define IS2_HKO_IP4_TCP_UDP_L4_ACK \ - (IS2_HKO_IP4_TCP_UDP_L4_PSH + IS2_HKL_IP4_TCP_UDP_L4_PSH) -#define IS2_HKL_IP4_TCP_UDP_L4_ACK 1 -#define IS2_HKO_IP4_TCP_UDP_L4_URG \ - (IS2_HKO_IP4_TCP_UDP_L4_ACK + IS2_HKL_IP4_TCP_UDP_L4_ACK) -#define IS2_HKL_IP4_TCP_UDP_L4_URG 1 -#define IS2_HKO_IP4_TCP_UDP_L4_1588_DOM \ - (IS2_HKO_IP4_TCP_UDP_L4_URG + IS2_HKL_IP4_TCP_UDP_L4_URG) -#define IS2_HKL_IP4_TCP_UDP_L4_1588_DOM 8 -#define IS2_HKO_IP4_TCP_UDP_L4_1588_VER \ - (IS2_HKO_IP4_TCP_UDP_L4_1588_DOM + IS2_HKL_IP4_TCP_UDP_L4_1588_DOM) -#define IS2_HKL_IP4_TCP_UDP_L4_1588_VER 4 - -/* IS2 half key - IP4_OTHER */ -#define IS2_HKO_IP4_OTHER_L3_PROTO IS2_HKO_IP4_TCP_UDP_TCP -#define IS2_HKL_IP4_OTHER_L3_PROTO 8 -#define IS2_HKO_IP4_OTHER_L3_PAYLOAD \ - (IS2_HKO_IP4_OTHER_L3_PROTO + IS2_HKL_IP4_OTHER_L3_PROTO) -#define IS2_HKL_IP4_OTHER_L3_PAYLOAD 56 - -/* IS2 half key - IP6_STD */ -#define IS2_HKO_IP6_STD_L3_TTL_GT0 IS2_HKO_L2_DMAC -#define IS2_HKL_IP6_STD_L3_TTL_GT0 1 -#define IS2_HKO_IP6_STD_L3_IP6_SIP \ - (IS2_HKO_IP6_STD_L3_TTL_GT0 + IS2_HKL_IP6_STD_L3_TTL_GT0) -#define IS2_HKL_IP6_STD_L3_IP6_SIP 128 -#define IS2_HKO_IP6_STD_L3_PROTO \ - (IS2_HKO_IP6_STD_L3_IP6_SIP + IS2_HKL_IP6_STD_L3_IP6_SIP) -#define IS2_HKL_IP6_STD_L3_PROTO 8 - -/* IS2 half key - OAM */ -#define IS2_HKO_OAM_OAM_MEL_FLAGS IS2_HKO_MAC_ETYPE_ETYPE -#define IS2_HKL_OAM_OAM_MEL_FLAGS 7 -#define IS2_HKO_OAM_OAM_VER \ - (IS2_HKO_OAM_OAM_MEL_FLAGS + IS2_HKL_OAM_OAM_MEL_FLAGS) -#define IS2_HKL_OAM_OAM_VER 5 -#define IS2_HKO_OAM_OAM_OPCODE (IS2_HKO_OAM_OAM_VER + IS2_HKL_OAM_OAM_VER) -#define IS2_HKL_OAM_OAM_OPCODE 8 -#define IS2_HKO_OAM_OAM_FLAGS (IS2_HKO_OAM_OAM_OPCODE + IS2_HKL_OAM_OAM_OPCODE) -#define IS2_HKL_OAM_OAM_FLAGS 8 -#define IS2_HKO_OAM_OAM_MEPID (IS2_HKO_OAM_OAM_FLAGS + IS2_HKL_OAM_OAM_FLAGS) -#define IS2_HKL_OAM_OAM_MEPID 16 -#define IS2_HKO_OAM_OAM_CCM_CNTS_EQ0 \ - (IS2_HKO_OAM_OAM_MEPID + IS2_HKL_OAM_OAM_MEPID) -#define IS2_HKL_OAM_OAM_CCM_CNTS_EQ0 1 - -/* IS2 half key - SMAC_SIP6 */ -#define IS2_HKO_SMAC_SIP6_IGR_PORT IS2_HKL_TYPE -#define IS2_HKL_SMAC_SIP6_IGR_PORT VCAP_PORT_WIDTH -#define IS2_HKO_SMAC_SIP6_L2_SMAC \ - (IS2_HKO_SMAC_SIP6_IGR_PORT + IS2_HKL_SMAC_SIP6_IGR_PORT) -#define IS2_HKL_SMAC_SIP6_L2_SMAC 48 -#define IS2_HKO_SMAC_SIP6_L3_IP6_SIP \ - (IS2_HKO_SMAC_SIP6_L2_SMAC + IS2_HKL_SMAC_SIP6_L2_SMAC) -#define IS2_HKL_SMAC_SIP6_L3_IP6_SIP 128 - -/* IS2 full key - common */ -#define IS2_FKO_TYPE 0 -#define IS2_FKL_TYPE 2 -#define IS2_FKO_FIRST (IS2_FKO_TYPE + IS2_FKL_TYPE) -#define IS2_FKL_FIRST 1 -#define IS2_FKO_PAG (IS2_FKO_FIRST + IS2_FKL_FIRST) -#define IS2_FKL_PAG 8 -#define IS2_FKO_IGR_PORT_MASK (IS2_FKO_PAG + IS2_FKL_PAG) -#define IS2_FKL_IGR_PORT_MASK (VCAP_PORT_CNT + 1) -#define IS2_FKO_SERVICE_FRM (IS2_FKO_IGR_PORT_MASK + IS2_FKL_IGR_PORT_MASK) -#define IS2_FKL_SERVICE_FRM 1 -#define IS2_FKO_HOST_MATCH (IS2_FKO_SERVICE_FRM + IS2_FKL_SERVICE_FRM) -#define IS2_FKL_HOST_MATCH 1 -#define IS2_FKO_L2_MC (IS2_FKO_HOST_MATCH + IS2_FKL_HOST_MATCH) -#define IS2_FKL_L2_MC 1 -#define IS2_FKO_L2_BC (IS2_FKO_L2_MC + IS2_FKL_L2_MC) -#define IS2_FKL_L2_BC 1 -#define IS2_FKO_VLAN_TAGGED (IS2_FKO_L2_BC + IS2_FKL_L2_BC) -#define IS2_FKL_VLAN_TAGGED 1 -#define IS2_FKO_VID (IS2_FKO_VLAN_TAGGED + IS2_FKL_VLAN_TAGGED) -#define IS2_FKL_VID 12 -#define IS2_FKO_DEI (IS2_FKO_VID + IS2_FKL_VID) -#define IS2_FKL_DEI 1 -#define IS2_FKO_PCP (IS2_FKO_DEI + IS2_FKL_DEI) -#define IS2_FKL_PCP 3 - -/* IS2 full key - IP6_TCP_UDP/IP6_OTHER common */ -#define IS2_FKO_L3_TTL_GT0 (IS2_FKO_PCP + IS2_FKL_PCP) -#define IS2_FKL_L3_TTL_GT0 1 -#define IS2_FKO_L3_TOS (IS2_FKO_L3_TTL_GT0 + IS2_FKL_L3_TTL_GT0) -#define IS2_FKL_L3_TOS 8 -#define IS2_FKO_L3_IP6_DIP (IS2_FKO_L3_TOS + IS2_FKL_L3_TOS) -#define IS2_FKL_L3_IP6_DIP 128 -#define IS2_FKO_L3_IP6_SIP (IS2_FKO_L3_IP6_DIP + IS2_FKL_L3_IP6_DIP) -#define IS2_FKL_L3_IP6_SIP 128 -#define IS2_FKO_DIP_EQ_SIP (IS2_FKO_L3_IP6_SIP + IS2_FKL_L3_IP6_SIP) -#define IS2_FKL_DIP_EQ_SIP 1 - -/* IS2 full key - IP6_TCP_UDP */ -#define IS2_FKO_IP6_TCP_UDP_TCP (IS2_FKO_DIP_EQ_SIP + IS2_FKL_DIP_EQ_SIP) -#define IS2_FKL_IP6_TCP_UDP_TCP 1 -#define IS2_FKO_IP6_TCP_UDP_L4_DPORT \ - (IS2_FKO_IP6_TCP_UDP_TCP + IS2_FKL_IP6_TCP_UDP_TCP) -#define IS2_FKL_IP6_TCP_UDP_L4_DPORT 16 -#define IS2_FKO_IP6_TCP_UDP_L4_SPORT \ - (IS2_FKO_IP6_TCP_UDP_L4_DPORT + IS2_FKL_IP6_TCP_UDP_L4_DPORT) -#define IS2_FKL_IP6_TCP_UDP_L4_SPORT 16 -#define IS2_FKO_IP6_TCP_UDP_L4_RNG \ - (IS2_FKO_IP6_TCP_UDP_L4_SPORT + IS2_FKL_IP6_TCP_UDP_L4_SPORT) -#define IS2_FKL_IP6_TCP_UDP_L4_RNG 8 -#define IS2_FKO_IP6_TCP_UDP_SPORT_EQ_DPORT \ - (IS2_FKO_IP6_TCP_UDP_L4_RNG + IS2_FKL_IP6_TCP_UDP_L4_RNG) -#define IS2_FKL_IP6_TCP_UDP_SPORT_EQ_DPORT 1 -#define IS2_FKO_IP6_TCP_UDP_SEQUENCE_EQ0 \ - (IS2_FKO_IP6_TCP_UDP_SPORT_EQ_DPORT + \ - IS2_FKL_IP6_TCP_UDP_SPORT_EQ_DPORT) -#define IS2_FKL_IP6_TCP_UDP_SEQUENCE_EQ0 1 -#define IS2_FKO_IP6_TCP_UDP_L4_FIN \ - (IS2_FKO_IP6_TCP_UDP_SEQUENCE_EQ0 + IS2_FKL_IP6_TCP_UDP_SEQUENCE_EQ0) -#define IS2_FKL_IP6_TCP_UDP_L4_FIN 1 -#define IS2_FKO_IP6_TCP_UDP_L4_SYN \ - (IS2_FKO_IP6_TCP_UDP_L4_FIN + IS2_FKL_IP6_TCP_UDP_L4_FIN) -#define IS2_FKL_IP6_TCP_UDP_L4_SYN 1 -#define IS2_FKO_IP6_TCP_UDP_L4_RST \ - (IS2_FKO_IP6_TCP_UDP_L4_SYN + IS2_FKL_IP6_TCP_UDP_L4_SYN) -#define IS2_FKL_IP6_TCP_UDP_L4_RST 1 -#define IS2_FKO_IP6_TCP_UDP_L4_PSH \ - (IS2_FKO_IP6_TCP_UDP_L4_RST + IS2_FKL_IP6_TCP_UDP_L4_RST) -#define IS2_FKL_IP6_TCP_UDP_L4_PSH 1 -#define IS2_FKO_IP6_TCP_UDP_L4_ACK \ - (IS2_FKO_IP6_TCP_UDP_L4_PSH + IS2_FKL_IP6_TCP_UDP_L4_PSH) -#define IS2_FKL_IP6_TCP_UDP_L4_ACK 1 -#define IS2_FKO_IP6_TCP_UDP_L4_URG \ - (IS2_FKO_IP6_TCP_UDP_L4_ACK + IS2_FKL_IP6_TCP_UDP_L4_ACK) -#define IS2_FKL_IP6_TCP_UDP_L4_URG 1 -#define IS2_FKO_IP6_TCP_UDP_L4_1588_DOM \ - (IS2_FKO_IP6_TCP_UDP_L4_URG + IS2_FKL_IP6_TCP_UDP_L4_URG) -#define IS2_FKL_IP6_TCP_UDP_L4_1588_DOM 8 -#define IS2_FKO_IP6_TCP_UDP_L4_1588_VER \ - (IS2_FKO_IP6_TCP_UDP_L4_1588_DOM + IS2_FKL_IP6_TCP_UDP_L4_1588_DOM) -#define IS2_FKL_IP6_TCP_UDP_L4_1588_VER 4 - -/* IS2 full key - IP6_OTHER */ -#define IS2_FKO_IP6_OTHER_L3_PROTO IS2_FKO_IP6_TCP_UDP_TCP -#define IS2_FKL_IP6_OTHER_L3_PROTO 8 -#define IS2_FKO_IP6_OTHER_L3_PAYLOAD \ - (IS2_FKO_IP6_OTHER_L3_PROTO + IS2_FKL_IP6_OTHER_L3_PROTO) -#define IS2_FKL_IP6_OTHER_L3_PAYLOAD 56 - -/* IS2 full key - CUSTOM */ -#define IS2_FKO_CUSTOM_CUSTOM_TYPE IS2_FKO_L3_TTL_GT0 -#define IS2_FKL_CUSTOM_CUSTOM_TYPE 1 -#define IS2_FKO_CUSTOM_CUSTOM \ - (IS2_FKO_CUSTOM_CUSTOM_TYPE + IS2_FKL_CUSTOM_CUSTOM_TYPE) -#define IS2_FKL_CUSTOM_CUSTOM 320 - -/* IS2 action - BASE_TYPE */ -#define IS2_AO_HIT_ME_ONCE 0 -#define IS2_AL_HIT_ME_ONCE 1 -#define IS2_AO_CPU_COPY_ENA (IS2_AO_HIT_ME_ONCE + IS2_AL_HIT_ME_ONCE) -#define IS2_AL_CPU_COPY_ENA 1 -#define IS2_AO_CPU_QU_NUM (IS2_AO_CPU_COPY_ENA + IS2_AL_CPU_COPY_ENA) -#define IS2_AL_CPU_QU_NUM 3 -#define IS2_AO_MASK_MODE (IS2_AO_CPU_QU_NUM + IS2_AL_CPU_QU_NUM) -#define IS2_AL_MASK_MODE 2 -#define IS2_AO_MIRROR_ENA (IS2_AO_MASK_MODE + IS2_AL_MASK_MODE) -#define IS2_AL_MIRROR_ENA 1 -#define IS2_AO_LRN_DIS (IS2_AO_MIRROR_ENA + IS2_AL_MIRROR_ENA) -#define IS2_AL_LRN_DIS 1 -#define IS2_AO_POLICE_ENA (IS2_AO_LRN_DIS + IS2_AL_LRN_DIS) -#define IS2_AL_POLICE_ENA 1 -#define IS2_AO_POLICE_IDX (IS2_AO_POLICE_ENA + IS2_AL_POLICE_ENA) -#define IS2_AL_POLICE_IDX 9 -#define IS2_AO_POLICE_VCAP_ONLY (IS2_AO_POLICE_IDX + IS2_AL_POLICE_IDX) -#define IS2_AL_POLICE_VCAP_ONLY 1 -#define IS2_AO_PORT_MASK (IS2_AO_POLICE_VCAP_ONLY + IS2_AL_POLICE_VCAP_ONLY) -#define IS2_AL_PORT_MASK VCAP_PORT_CNT -#define IS2_AO_REW_OP (IS2_AO_PORT_MASK + IS2_AL_PORT_MASK) -#define IS2_AL_REW_OP 9 -#define IS2_AO_LM_CNT_DIS (IS2_AO_REW_OP + IS2_AL_REW_OP) -#define IS2_AL_LM_CNT_DIS 1 -#define IS2_AO_ISDX_ENA \ - (IS2_AO_LM_CNT_DIS + IS2_AL_LM_CNT_DIS + 1) /* Reserved bit */ -#define IS2_AL_ISDX_ENA 1 -#define IS2_AO_ACL_ID (IS2_AO_ISDX_ENA + IS2_AL_ISDX_ENA) -#define IS2_AL_ACL_ID 6 - -/* IS2 action - SMAC_SIP */ -#define IS2_AO_SMAC_SIP_CPU_COPY_ENA 0 -#define IS2_AL_SMAC_SIP_CPU_COPY_ENA 1 -#define IS2_AO_SMAC_SIP_CPU_QU_NUM 1 -#define IS2_AL_SMAC_SIP_CPU_QU_NUM 3 -#define IS2_AO_SMAC_SIP_FWD_KILL_ENA 4 -#define IS2_AL_SMAC_SIP_FWD_KILL_ENA 1 -#define IS2_AO_SMAC_SIP_HOST_MATCH 5 -#define IS2_AL_SMAC_SIP_HOST_MATCH 1 - -#endif /* _OCELOT_VCAP_H_ */ diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 74e7c63adad4..3da8285d4cd8 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -462,6 +462,9 @@ struct ocelot { struct ocelot_acl_block acl_block; + const struct vcap_field *vcap_is2_keys; + const struct vcap_field *vcap_is2_actions; + /* Workqueue to check statistics for overflow with its lock */ struct mutex stats_lock; u64 *stats; diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h new file mode 100644 index 000000000000..0783f0ffc813 --- /dev/null +++ b/include/soc/mscc/ocelot_vcap.h @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) + * Microsemi Ocelot Switch driver + * Copyright (c) 2019 Microsemi Corporation + */ + +#ifndef _OCELOT_VCAP_H_ +#define _OCELOT_VCAP_H_ + +/* ================================================================= + * VCAP Common + * ================================================================= + */ + +/* VCAP Type-Group values */ +#define VCAP_TG_NONE 0 /* Entry is invalid */ +#define VCAP_TG_FULL 1 /* Full entry */ +#define VCAP_TG_HALF 2 /* Half entry */ +#define VCAP_TG_QUARTER 3 /* Quarter entry */ + +/* ================================================================= + * VCAP IS2 + * ================================================================= + */ + +#define VCAP_IS2_CNT 64 +#define VCAP_IS2_ENTRY_WIDTH 376 +#define VCAP_IS2_ACTION_WIDTH 99 +#define VCAP_PORT_CNT 11 + +/* IS2 half key types */ +#define IS2_TYPE_ETYPE 0 +#define IS2_TYPE_LLC 1 +#define IS2_TYPE_SNAP 2 +#define IS2_TYPE_ARP 3 +#define IS2_TYPE_IP_UDP_TCP 4 +#define IS2_TYPE_IP_OTHER 5 +#define IS2_TYPE_IPV6 6 +#define IS2_TYPE_OAM 7 +#define IS2_TYPE_SMAC_SIP6 8 +#define IS2_TYPE_ANY 100 /* Pseudo type */ + +/* IS2 half key type mask for matching any IP */ +#define IS2_TYPE_MASK_IP_ANY 0xe + +/* IS2 action types */ +#define IS2_ACTION_TYPE_NORMAL 0 +#define IS2_ACTION_TYPE_SMAC_SIP 1 + +/* IS2 MASK_MODE values */ +#define IS2_ACT_MASK_MODE_NONE 0 +#define IS2_ACT_MASK_MODE_FILTER 1 +#define IS2_ACT_MASK_MODE_POLICY 2 +#define IS2_ACT_MASK_MODE_REDIR 3 + +/* IS2 REW_OP values */ +#define IS2_ACT_REW_OP_NONE 0 +#define IS2_ACT_REW_OP_PTP_ONE 2 +#define IS2_ACT_REW_OP_PTP_TWO 3 +#define IS2_ACT_REW_OP_SPECIAL 8 +#define IS2_ACT_REW_OP_PTP_ORG 9 +#define IS2_ACT_REW_OP_PTP_ONE_SUB_DELAY_1 (IS2_ACT_REW_OP_PTP_ONE | (1 << 3)) +#define IS2_ACT_REW_OP_PTP_ONE_SUB_DELAY_2 (IS2_ACT_REW_OP_PTP_ONE | (2 << 3)) +#define IS2_ACT_REW_OP_PTP_ONE_ADD_DELAY (IS2_ACT_REW_OP_PTP_ONE | (1 << 5)) +#define IS2_ACT_REW_OP_PTP_ONE_ADD_SUB BIT(7) + +#define VCAP_PORT_WIDTH 4 + +/* IS2 quarter key - SMAC_SIP4 */ +#define IS2_QKO_IGR_PORT 0 +#define IS2_QKL_IGR_PORT VCAP_PORT_WIDTH +#define IS2_QKO_L2_SMAC (IS2_QKO_IGR_PORT + IS2_QKL_IGR_PORT) +#define IS2_QKL_L2_SMAC 48 +#define IS2_QKO_L3_IP4_SIP (IS2_QKO_L2_SMAC + IS2_QKL_L2_SMAC) +#define IS2_QKL_L3_IP4_SIP 32 + +enum vcap_is2_half_key_field { + /* Common */ + VCAP_IS2_TYPE, + VCAP_IS2_HK_FIRST, + VCAP_IS2_HK_PAG, + VCAP_IS2_HK_RSV1, + VCAP_IS2_HK_IGR_PORT_MASK, + VCAP_IS2_HK_RSV2, + VCAP_IS2_HK_HOST_MATCH, + VCAP_IS2_HK_L2_MC, + VCAP_IS2_HK_L2_BC, + VCAP_IS2_HK_VLAN_TAGGED, + VCAP_IS2_HK_VID, + VCAP_IS2_HK_DEI, + VCAP_IS2_HK_PCP, + /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ + VCAP_IS2_HK_L2_DMAC, + VCAP_IS2_HK_L2_SMAC, + /* MAC_ETYPE (TYPE=000) */ + VCAP_IS2_HK_MAC_ETYPE_ETYPE, + VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0, + VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1, + VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2, + /* MAC_LLC (TYPE=001) */ + VCAP_IS2_HK_MAC_LLC_DMAC, + VCAP_IS2_HK_MAC_LLC_SMAC, + VCAP_IS2_HK_MAC_LLC_L2_LLC, + /* MAC_SNAP (TYPE=010) */ + VCAP_IS2_HK_MAC_SNAP_SMAC, + VCAP_IS2_HK_MAC_SNAP_DMAC, + VCAP_IS2_HK_MAC_SNAP_L2_SNAP, + /* MAC_ARP (TYPE=011) */ + VCAP_IS2_HK_MAC_ARP_SMAC, + VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK, + VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK, + VCAP_IS2_HK_MAC_ARP_LEN_OK, + VCAP_IS2_HK_MAC_ARP_TARGET_MATCH, + VCAP_IS2_HK_MAC_ARP_SENDER_MATCH, + VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN, + VCAP_IS2_HK_MAC_ARP_OPCODE, + VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP, + VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP, + VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP, + /* IP4_TCP_UDP / IP4_OTHER common */ + VCAP_IS2_HK_IP4, + VCAP_IS2_HK_L3_FRAGMENT, + VCAP_IS2_HK_L3_FRAG_OFS_GT0, + VCAP_IS2_HK_L3_OPTIONS, + VCAP_IS2_HK_IP4_L3_TTL_GT0, + VCAP_IS2_HK_L3_TOS, + VCAP_IS2_HK_L3_IP4_DIP, + VCAP_IS2_HK_L3_IP4_SIP, + VCAP_IS2_HK_DIP_EQ_SIP, + /* IP4_TCP_UDP (TYPE=100) */ + VCAP_IS2_HK_TCP, + VCAP_IS2_HK_L4_SPORT, + VCAP_IS2_HK_L4_DPORT, + VCAP_IS2_HK_L4_RNG, + VCAP_IS2_HK_L4_SPORT_EQ_DPORT, + VCAP_IS2_HK_L4_SEQUENCE_EQ0, + VCAP_IS2_HK_L4_URG, + VCAP_IS2_HK_L4_ACK, + VCAP_IS2_HK_L4_PSH, + VCAP_IS2_HK_L4_RST, + VCAP_IS2_HK_L4_SYN, + VCAP_IS2_HK_L4_FIN, + VCAP_IS2_HK_L4_1588_DOM, + VCAP_IS2_HK_L4_1588_VER, + /* IP4_OTHER (TYPE=101) */ + VCAP_IS2_HK_IP4_L3_PROTO, + VCAP_IS2_HK_L3_PAYLOAD, + /* IP6_STD (TYPE=110) */ + VCAP_IS2_HK_IP6_L3_TTL_GT0, + VCAP_IS2_HK_IP6_L3_PROTO, + VCAP_IS2_HK_L3_IP6_SIP, + /* OAM (TYPE=111) */ + VCAP_IS2_HK_OAM_MEL_FLAGS, + VCAP_IS2_HK_OAM_VER, + VCAP_IS2_HK_OAM_OPCODE, + VCAP_IS2_HK_OAM_FLAGS, + VCAP_IS2_HK_OAM_MEPID, + VCAP_IS2_HK_OAM_CCM_CNTS_EQ0, + VCAP_IS2_HK_OAM_IS_Y1731, +}; + +struct vcap_field { + int offset; + int length; +}; + +enum vcap_is2_action_field { + VCAP_IS2_ACT_HIT_ME_ONCE, + VCAP_IS2_ACT_CPU_COPY_ENA, + VCAP_IS2_ACT_CPU_QU_NUM, + VCAP_IS2_ACT_MASK_MODE, + VCAP_IS2_ACT_MIRROR_ENA, + VCAP_IS2_ACT_LRN_DIS, + VCAP_IS2_ACT_POLICE_ENA, + VCAP_IS2_ACT_POLICE_IDX, + VCAP_IS2_ACT_POLICE_VCAP_ONLY, + VCAP_IS2_ACT_PORT_MASK, + VCAP_IS2_ACT_REW_OP, + VCAP_IS2_ACT_SMAC_REPLACE_ENA, + VCAP_IS2_ACT_RSV, + VCAP_IS2_ACT_ACL_ID, + VCAP_IS2_ACT_HIT_CNT, +}; + +#endif /* _OCELOT_VCAP_H_ */ -- cgit v1.2.3 From 1ba8f6561a3ba3a4ac4becadb691667645fe73d2 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:31:11 +0200 Subject: net: mscc: ocelot: remove port_pcs_init indirection for VSC7514 The Felix driver is now using its own PHYLINK instance, not calling into ocelot_adjust_link. So the port_pcs_init function pointer is an unnecessary indirection. Remove it. Signed-off-by: Vladimir Oltean Tested-by: Horatiu Vultur Reviewed-by: Allan W. Nielsen Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 19 +++++++++++++++++-- drivers/net/ethernet/mscc/ocelot_board.c | 24 ------------------------ include/soc/mscc/ocelot.h | 3 --- 3 files changed, 17 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index fe5a0ff4ee2f..ac4cf34d3af5 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -442,8 +442,23 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port, ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA | mode, DEV_MAC_MODE_CFG); - if (ocelot->ops->pcs_init) - ocelot->ops->pcs_init(ocelot, port); + /* Disable HDX fast control */ + ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS, + DEV_PORT_MISC); + + /* SGMII only for now */ + ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA, + PCS1G_MODE_CFG); + ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); + + /* Enable PCS */ + ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); + + /* No aneg on SGMII */ + ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG); + + /* No loopback */ + ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); /* Enable MAC module */ ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c index c236936dbde7..c9257a828d91 100644 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ b/drivers/net/ethernet/mscc/ocelot_board.c @@ -212,29 +212,6 @@ static const struct of_device_id mscc_ocelot_match[] = { }; MODULE_DEVICE_TABLE(of, mscc_ocelot_match); -static void ocelot_port_pcs_init(struct ocelot *ocelot, int port) -{ - struct ocelot_port *ocelot_port = ocelot->ports[port]; - - /* Disable HDX fast control */ - ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS, - DEV_PORT_MISC); - - /* SGMII only for now */ - ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA, - PCS1G_MODE_CFG); - ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); - - /* Enable PCS */ - ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); - - /* No aneg on SGMII */ - ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG); - - /* No loopback */ - ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG); -} - static int ocelot_reset(struct ocelot *ocelot) { int retries = 100; @@ -259,7 +236,6 @@ static int ocelot_reset(struct ocelot *ocelot) } static const struct ocelot_ops ocelot_ops = { - .pcs_init = ocelot_port_pcs_init, .reset = ocelot_reset, }; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 3da8285d4cd8..45630bee8ed2 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -402,7 +402,6 @@ enum ocelot_tag_prefix { struct ocelot; struct ocelot_ops { - void (*pcs_init)(struct ocelot *ocelot, int port); int (*reset)(struct ocelot *ocelot); }; @@ -479,8 +478,6 @@ struct ocelot { struct mutex ptp_lock; /* Protects the PTP clock */ spinlock_t ptp_clock_lock; - - void (*port_pcs_init)(struct ocelot_port *port); }; #define ocelot_read_ix(ocelot, reg, gi, ri) __ocelot_read_ix(ocelot, reg, reg##_GSZ * (gi) + reg##_RSZ * (ri)) -- cgit v1.2.3 From 8551cdeb2ad1711e3ae85799ad9cc41c0bc64e0b Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:31:12 +0200 Subject: net: mscc: ocelot: parameterize the vcap_is2 properties Remove the definitions for the VCAP IS2 table from ocelot_ace.c, since it is specific to VSC7514. The VSC9959 VCAP IS2 table supports more rules (1024 instead of 64) and has a different width for the action (89 bits instead of 99). Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot_ace.c | 134 +++++++++++++------------------ drivers/net/ethernet/mscc/ocelot_board.c | 30 +++++++ include/soc/mscc/ocelot.h | 1 + include/soc/mscc/ocelot_vcap.h | 37 +++++++-- 4 files changed, 114 insertions(+), 88 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mscc/ocelot_ace.c b/drivers/net/ethernet/mscc/ocelot_ace.c index 9922033a2aaf..906b54025b17 100644 --- a/drivers/net/ethernet/mscc/ocelot_ace.c +++ b/drivers/net/ethernet/mscc/ocelot_ace.c @@ -11,53 +11,7 @@ #include "ocelot_s2.h" #define OCELOT_POLICER_DISCARD 0x17f - -struct vcap_props { - const char *name; /* Symbolic name */ - u16 tg_width; /* Type-group width (in bits) */ - u16 sw_count; /* Sub word count */ - u16 entry_count; /* Entry count */ - u16 entry_words; /* Number of entry words */ - u16 entry_width; /* Entry width (in bits) */ - u16 action_count; /* Action count */ - u16 action_words; /* Number of action words */ - u16 action_width; /* Action width (in bits) */ - u16 action_type_width; /* Action type width (in bits) */ - struct { - u16 width; /* Action type width (in bits) */ - u16 count; /* Action type sub word count */ - } action_table[2]; - u16 counter_words; /* Number of counter words */ - u16 counter_width; /* Counter width (in bits) */ -}; - #define ENTRY_WIDTH 32 -#define BITS_TO_32BIT(x) (1 + (((x) - 1) / ENTRY_WIDTH)) - -static const struct vcap_props vcap_is2 = { - .name = "IS2", - .tg_width = 2, - .sw_count = 4, - .entry_count = VCAP_IS2_CNT, - .entry_words = BITS_TO_32BIT(VCAP_IS2_ENTRY_WIDTH), - .entry_width = VCAP_IS2_ENTRY_WIDTH, - .action_count = (VCAP_IS2_CNT + VCAP_PORT_CNT + 2), - .action_words = BITS_TO_32BIT(VCAP_IS2_ACTION_WIDTH), - .action_width = (VCAP_IS2_ACTION_WIDTH), - .action_type_width = 1, - .action_table = { - { - .width = 49, - .count = 2 - }, - { - .width = 6, - .count = 4 - }, - }, - .counter_words = BITS_TO_32BIT(4 * ENTRY_WIDTH), - .counter_width = ENTRY_WIDTH, -}; enum vcap_sel { VCAP_SEL_ENTRY = 0x1, @@ -100,11 +54,13 @@ static u32 vcap_s2_read_update_ctrl(struct ocelot *ocelot) static void vcap_cmd(struct ocelot *ocelot, u16 ix, int cmd, int sel) { + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + u32 value = (S2_CORE_UPDATE_CTRL_UPDATE_CMD(cmd) | S2_CORE_UPDATE_CTRL_UPDATE_ADDR(ix) | S2_CORE_UPDATE_CTRL_UPDATE_SHOT); - if ((sel & VCAP_SEL_ENTRY) && ix >= vcap_is2.entry_count) + if ((sel & VCAP_SEL_ENTRY) && ix >= vcap_is2->entry_count) return; if (!(sel & VCAP_SEL_ENTRY)) @@ -125,14 +81,19 @@ static void vcap_cmd(struct ocelot *ocelot, u16 ix, int cmd, int sel) /* Convert from 0-based row to VCAP entry row and run command */ static void vcap_row_cmd(struct ocelot *ocelot, u32 row, int cmd, int sel) { - vcap_cmd(ocelot, vcap_is2.entry_count - row - 1, cmd, sel); + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + + vcap_cmd(ocelot, vcap_is2->entry_count - row - 1, cmd, sel); } static void vcap_entry2cache(struct ocelot *ocelot, struct vcap_data *data) { - u32 i; + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + u32 entry_words, i; + + entry_words = DIV_ROUND_UP(vcap_is2->entry_width, ENTRY_WIDTH); - for (i = 0; i < vcap_is2.entry_words; i++) { + for (i = 0; i < entry_words; i++) { ocelot_write_rix(ocelot, data->entry[i], S2_CACHE_ENTRY_DAT, i); ocelot_write_rix(ocelot, ~data->mask[i], S2_CACHE_MASK_DAT, i); } @@ -141,9 +102,12 @@ static void vcap_entry2cache(struct ocelot *ocelot, struct vcap_data *data) static void vcap_cache2entry(struct ocelot *ocelot, struct vcap_data *data) { - u32 i; + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + u32 entry_words, i; + + entry_words = DIV_ROUND_UP(vcap_is2->entry_width, ENTRY_WIDTH); - for (i = 0; i < vcap_is2.entry_words; i++) { + for (i = 0; i < entry_words; i++) { data->entry[i] = ocelot_read_rix(ocelot, S2_CACHE_ENTRY_DAT, i); // Invert mask data->mask[i] = ~ocelot_read_rix(ocelot, S2_CACHE_MASK_DAT, i); @@ -153,49 +117,56 @@ static void vcap_cache2entry(struct ocelot *ocelot, struct vcap_data *data) static void vcap_action2cache(struct ocelot *ocelot, struct vcap_data *data) { - u32 i, width, mask; + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + u32 action_words, i, width, mask; /* Encode action type */ - width = vcap_is2.action_type_width; + width = vcap_is2->action_type_width; if (width) { mask = GENMASK(width, 0); data->action[0] = ((data->action[0] & ~mask) | data->type); } - for (i = 0; i < vcap_is2.action_words; i++) - ocelot_write_rix(ocelot, data->action[i], - S2_CACHE_ACTION_DAT, i); + action_words = DIV_ROUND_UP(vcap_is2->action_width, ENTRY_WIDTH); - for (i = 0; i < vcap_is2.counter_words; i++) - ocelot_write_rix(ocelot, data->counter[i], - S2_CACHE_CNT_DAT, i); + for (i = 0; i < action_words; i++) + ocelot_write_rix(ocelot, data->action[i], S2_CACHE_ACTION_DAT, + i); + + for (i = 0; i < vcap_is2->counter_words; i++) + ocelot_write_rix(ocelot, data->counter[i], S2_CACHE_CNT_DAT, i); } static void vcap_cache2action(struct ocelot *ocelot, struct vcap_data *data) { - u32 i, width; + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + u32 action_words, i, width; + + action_words = DIV_ROUND_UP(vcap_is2->action_width, ENTRY_WIDTH); - for (i = 0; i < vcap_is2.action_words; i++) + for (i = 0; i < action_words; i++) data->action[i] = ocelot_read_rix(ocelot, S2_CACHE_ACTION_DAT, i); - for (i = 0; i < vcap_is2.counter_words; i++) + for (i = 0; i < vcap_is2->counter_words; i++) data->counter[i] = ocelot_read_rix(ocelot, S2_CACHE_CNT_DAT, i); /* Extract action type */ - width = vcap_is2.action_type_width; + width = vcap_is2->action_type_width; data->type = (width ? (data->action[0] & GENMASK(width, 0)) : 0); } /* Calculate offsets for entry */ -static void is2_data_get(struct vcap_data *data, int ix) +static void is2_data_get(struct ocelot *ocelot, struct vcap_data *data, int ix) { - u32 i, col, offset, count, cnt, base, width = vcap_is2.tg_width; + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; + u32 i, col, offset, count, cnt, base; + u32 width = vcap_is2->tg_width; count = (data->tg_sw == VCAP_TG_HALF ? 2 : 4); col = (ix % 2); - cnt = (vcap_is2.sw_count / count); - base = (vcap_is2.sw_count - col * cnt - cnt); + cnt = (vcap_is2->sw_count / count); + base = (vcap_is2->sw_count - col * cnt - cnt); data->tg_value = 0; data->tg_mask = 0; for (i = 0; i < cnt; i++) { @@ -206,13 +177,13 @@ static void is2_data_get(struct vcap_data *data, int ix) /* Calculate key/action/counter offsets */ col = (count - col - 1); - data->key_offset = (base * vcap_is2.entry_width) / vcap_is2.sw_count; - data->counter_offset = (cnt * col * vcap_is2.counter_width); + data->key_offset = (base * vcap_is2->entry_width) / vcap_is2->sw_count; + data->counter_offset = (cnt * col * vcap_is2->counter_width); i = data->type; - width = vcap_is2.action_table[i].width; - cnt = vcap_is2.action_table[i].count; + width = vcap_is2->action_table[i].width; + cnt = vcap_is2->action_table[i].count; data->action_offset = - (((cnt * col * width) / count) + vcap_is2.action_type_width); + (((cnt * col * width) / count) + vcap_is2->action_type_width); } static void vcap_data_set(u32 *data, u32 offset, u32 len, u32 value) @@ -354,6 +325,7 @@ static void is2_action_set(struct ocelot *ocelot, struct vcap_data *data, static void is2_entry_set(struct ocelot *ocelot, int ix, struct ocelot_ace_rule *ace) { + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; u32 val, msk, type, type_mask = 0xf, i, count; struct ocelot_ace_vlan *tag = &ace->vlan; struct ocelot_vcap_u64 payload; @@ -369,7 +341,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, vcap_cache2action(ocelot, &data); data.tg_sw = VCAP_TG_HALF; - is2_data_get(&data, ix); + is2_data_get(ocelot, &data, ix); data.tg = (data.tg & ~data.tg_mask); if (ace->prio != 0) data.tg |= data.tg_value; @@ -627,7 +599,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, default: type = 0; type_mask = 0; - count = (vcap_is2.entry_width / 2); + count = vcap_is2->entry_width / 2; /* Iterate over the non-common part of the key and * clear entry data */ @@ -641,7 +613,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, vcap_key_set(ocelot, &data, VCAP_IS2_TYPE, type, type_mask); is2_action_set(ocelot, &data, ace->action); vcap_data_set(data.counter, data.counter_offset, - vcap_is2.counter_width, ace->stats.pkts); + vcap_is2->counter_width, ace->stats.pkts); /* Write row */ vcap_entry2cache(ocelot, &data); @@ -652,6 +624,7 @@ static void is2_entry_set(struct ocelot *ocelot, int ix, static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, int ix) { + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; struct vcap_data data; int row = (ix / 2); u32 cnt; @@ -659,9 +632,9 @@ static void is2_entry_get(struct ocelot *ocelot, struct ocelot_ace_rule *rule, vcap_row_cmd(ocelot, row, VCAP_CMD_READ, VCAP_SEL_COUNTER); vcap_cache2action(ocelot, &data); data.tg_sw = VCAP_TG_HALF; - is2_data_get(&data, ix); + is2_data_get(ocelot, &data, ix); cnt = vcap_data_get(data.counter, data.counter_offset, - vcap_is2.counter_width); + vcap_is2->counter_width); rule->stats.pkts = cnt; } @@ -805,16 +778,17 @@ int ocelot_ace_rule_stats_update(struct ocelot *ocelot, int ocelot_ace_init(struct ocelot *ocelot) { + const struct vcap_props *vcap_is2 = &ocelot->vcap[VCAP_IS2]; struct vcap_data data; memset(&data, 0, sizeof(data)); vcap_entry2cache(ocelot, &data); - ocelot_write(ocelot, vcap_is2.entry_count, S2_CORE_MV_CFG); + ocelot_write(ocelot, vcap_is2->entry_count, S2_CORE_MV_CFG); vcap_cmd(ocelot, 0, VCAP_CMD_INITIALIZE, VCAP_SEL_ENTRY); vcap_action2cache(ocelot, &data); - ocelot_write(ocelot, vcap_is2.action_count, S2_CORE_MV_CFG); + ocelot_write(ocelot, vcap_is2->action_count, S2_CORE_MV_CFG); vcap_cmd(ocelot, 0, VCAP_CMD_INITIALIZE, VCAP_SEL_ACTION | VCAP_SEL_COUNTER); diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c index c9257a828d91..c343ca5276ef 100644 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ b/drivers/net/ethernet/mscc/ocelot_board.c @@ -18,6 +18,10 @@ #include "ocelot.h" #define IFH_EXTRACT_BITFIELD64(x, o, w) (((x) >> (o)) & GENMASK_ULL((w) - 1, 0)) +#define VSC7514_VCAP_IS2_CNT 64 +#define VSC7514_VCAP_IS2_ENTRY_WIDTH 376 +#define VSC7514_VCAP_IS2_ACTION_WIDTH 99 +#define VSC7514_VCAP_PORT_CNT 11 static int ocelot_parse_ifh(u32 *_ifh, struct frame_info *info) { @@ -337,6 +341,31 @@ static const struct vcap_field vsc7514_vcap_is2_actions[] = { [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, }; +static const struct vcap_props vsc7514_vcap_props[] = { + [VCAP_IS2] = { + .tg_width = 2, + .sw_count = 4, + .entry_count = VSC7514_VCAP_IS2_CNT, + .entry_width = VSC7514_VCAP_IS2_ENTRY_WIDTH, + .action_count = VSC7514_VCAP_IS2_CNT + + VSC7514_VCAP_PORT_CNT + 2, + .action_width = 99, + .action_type_width = 1, + .action_table = { + [IS2_ACTION_TYPE_NORMAL] = { + .width = 49, + .count = 2 + }, + [IS2_ACTION_TYPE_SMAC_SIP] = { + .width = 6, + .count = 4 + }, + }, + .counter_words = 4, + .counter_width = 32, + }, +}; + static int mscc_ocelot_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -439,6 +468,7 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ocelot->vcap_is2_keys = vsc7514_vcap_is2_keys; ocelot->vcap_is2_actions = vsc7514_vcap_is2_actions; + ocelot->vcap = vsc7514_vcap_props; ocelot_init(ocelot); ocelot_set_cpu_port(ocelot, ocelot->num_phys_ports, diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 45630bee8ed2..30270571fb71 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -463,6 +463,7 @@ struct ocelot { const struct vcap_field *vcap_is2_keys; const struct vcap_field *vcap_is2_actions; + const struct vcap_props *vcap; /* Workqueue to check statistics for overflow with its lock */ struct mutex stats_lock; diff --git a/include/soc/mscc/ocelot_vcap.h b/include/soc/mscc/ocelot_vcap.h index 0783f0ffc813..5748373ab4d3 100644 --- a/include/soc/mscc/ocelot_vcap.h +++ b/include/soc/mscc/ocelot_vcap.h @@ -11,6 +11,30 @@ * ================================================================= */ +enum { + /* VCAP_IS1, */ + VCAP_IS2, + /* VCAP_ES0, */ +}; + +struct vcap_props { + u16 tg_width; /* Type-group width (in bits) */ + u16 sw_count; /* Sub word count */ + u16 entry_count; /* Entry count */ + u16 entry_words; /* Number of entry words */ + u16 entry_width; /* Entry width (in bits) */ + u16 action_count; /* Action count */ + u16 action_words; /* Number of action words */ + u16 action_width; /* Action width (in bits) */ + u16 action_type_width; /* Action type width (in bits) */ + struct { + u16 width; /* Action type width (in bits) */ + u16 count; /* Action type sub word count */ + } action_table[2]; + u16 counter_words; /* Number of counter words */ + u16 counter_width; /* Counter width (in bits) */ +}; + /* VCAP Type-Group values */ #define VCAP_TG_NONE 0 /* Entry is invalid */ #define VCAP_TG_FULL 1 /* Full entry */ @@ -22,11 +46,6 @@ * ================================================================= */ -#define VCAP_IS2_CNT 64 -#define VCAP_IS2_ENTRY_WIDTH 376 -#define VCAP_IS2_ACTION_WIDTH 99 -#define VCAP_PORT_CNT 11 - /* IS2 half key types */ #define IS2_TYPE_ETYPE 0 #define IS2_TYPE_LLC 1 @@ -42,9 +61,11 @@ /* IS2 half key type mask for matching any IP */ #define IS2_TYPE_MASK_IP_ANY 0xe -/* IS2 action types */ -#define IS2_ACTION_TYPE_NORMAL 0 -#define IS2_ACTION_TYPE_SMAC_SIP 1 +enum { + IS2_ACTION_TYPE_NORMAL, + IS2_ACTION_TYPE_SMAC_SIP, + IS2_ACTION_TYPE_MAX, +}; /* IS2 MASK_MODE values */ #define IS2_ACT_MASK_MODE_NONE 0 -- cgit v1.2.3 From ed11bb1f9657e95e8b79a9a49211986bde96005a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:31:13 +0200 Subject: net: dsa: Add bypass operations for the flower classifier-action filter Due to the immense variety of classification keys and actions available for tc-flower, as well as due to potentially very different DSA switch capabilities, it doesn't make a lot of sense for the DSA mid layer to even attempt to interpret these. So just pass them on to the underlying switch driver. DSA implements just the standard boilerplate for binding and unbinding flow blocks to ports, since nobody wants to deal with that. Signed-off-by: Vladimir Oltean Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 6 ++++++ net/dsa/slave.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 7d3d84f0ef42..beeb81a532e3 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -540,6 +540,12 @@ struct dsa_switch_ops { /* * TC integration */ + int (*cls_flower_add)(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress); + int (*cls_flower_del)(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress); + int (*cls_flower_stats)(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress); int (*port_mirror_add)(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 088c886e609e..79d9b4384d7b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -946,6 +946,64 @@ static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, } } +static int dsa_slave_add_cls_flower(struct net_device *dev, + struct flow_cls_offload *cls, + bool ingress) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + int port = dp->index; + + if (!ds->ops->cls_flower_add) + return -EOPNOTSUPP; + + return ds->ops->cls_flower_add(ds, port, cls, ingress); +} + +static int dsa_slave_del_cls_flower(struct net_device *dev, + struct flow_cls_offload *cls, + bool ingress) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + int port = dp->index; + + if (!ds->ops->cls_flower_del) + return -EOPNOTSUPP; + + return ds->ops->cls_flower_del(ds, port, cls, ingress); +} + +static int dsa_slave_stats_cls_flower(struct net_device *dev, + struct flow_cls_offload *cls, + bool ingress) +{ + struct dsa_port *dp = dsa_slave_to_port(dev); + struct dsa_switch *ds = dp->ds; + int port = dp->index; + + if (!ds->ops->cls_flower_stats) + return -EOPNOTSUPP; + + return ds->ops->cls_flower_stats(ds, port, cls, ingress); +} + +static int dsa_slave_setup_tc_cls_flower(struct net_device *dev, + struct flow_cls_offload *cls, + bool ingress) +{ + switch (cls->command) { + case FLOW_CLS_REPLACE: + return dsa_slave_add_cls_flower(dev, cls, ingress); + case FLOW_CLS_DESTROY: + return dsa_slave_del_cls_flower(dev, cls, ingress); + case FLOW_CLS_STATS: + return dsa_slave_stats_cls_flower(dev, cls, ingress); + default: + return -EOPNOTSUPP; + } +} + static int dsa_slave_setup_tc_block_cb(enum tc_setup_type type, void *type_data, void *cb_priv, bool ingress) { @@ -957,6 +1015,8 @@ static int dsa_slave_setup_tc_block_cb(enum tc_setup_type type, void *type_data, switch (type) { case TC_SETUP_CLSMATCHALL: return dsa_slave_setup_tc_cls_matchall(dev, type_data, ingress); + case TC_SETUP_CLSFLOWER: + return dsa_slave_setup_tc_cls_flower(dev, type_data, ingress); default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 07d985eef07348345870f1062797852cb4489b83 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:31:14 +0200 Subject: net: dsa: felix: Wire up the ocelot cls_flower methods Export the cls_flower methods from the ocelot driver and hook them up to the DSA passthrough layer. Tables for the VCAP IS2 parameters, as well as half key packing (field offsets and lengths) need to be defined for the VSC9959 core, as they are different from Ocelot, mainly due to the different port count. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 31 ++++++++ drivers/net/dsa/ocelot/felix.h | 3 + drivers/net/dsa/ocelot/felix_vsc9959.c | 131 +++++++++++++++++++++++++++++++++ include/soc/mscc/ocelot.h | 6 ++ 4 files changed, 171 insertions(+) (limited to 'include') diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 7e66821b05b4..c0acd7dc7f48 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -2,6 +2,7 @@ /* Copyright 2019 NXP Semiconductors */ #include +#include #include #include #include @@ -401,6 +402,9 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports) ocelot->stats_layout = felix->info->stats_layout; ocelot->num_stats = felix->info->num_stats; ocelot->shared_queue_sz = felix->info->shared_queue_sz; + ocelot->vcap_is2_keys = felix->info->vcap_is2_keys; + ocelot->vcap_is2_actions= felix->info->vcap_is2_actions; + ocelot->vcap = felix->info->vcap; ocelot->ops = felix->info->ops; port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t), @@ -595,6 +599,30 @@ static bool felix_txtstamp(struct dsa_switch *ds, int port, return false; } +static int felix_cls_flower_add(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_cls_flower_replace(ocelot, port, cls, ingress); +} + +static int felix_cls_flower_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_cls_flower_destroy(ocelot, port, cls, ingress); +} + +static int felix_cls_flower_stats(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct ocelot *ocelot = ds->priv; + + return ocelot_cls_flower_stats(ocelot, port, cls, ingress); +} + static const struct dsa_switch_ops felix_switch_ops = { .get_tag_protocol = felix_get_tag_protocol, .setup = felix_setup, @@ -626,6 +654,9 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_hwtstamp_set = felix_hwtstamp_set, .port_rxtstamp = felix_rxtstamp, .port_txtstamp = felix_txtstamp, + .cls_flower_add = felix_cls_flower_add, + .cls_flower_del = felix_cls_flower_del, + .cls_flower_stats = felix_cls_flower_stats, }; static struct felix_info *felix_instance_tbl[] = { diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 3a7580015b62..82d46f260041 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -18,6 +18,9 @@ struct felix_info { const struct ocelot_stat_layout *stats_layout; unsigned int num_stats; int num_ports; + struct vcap_field *vcap_is2_keys; + struct vcap_field *vcap_is2_actions; + const struct vcap_props *vcap; int switch_pci_bar; int imdio_pci_bar; int (*mdio_bus_alloc)(struct ocelot *ocelot); diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index 93800e81cdd4..b4078f3c5c38 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -3,12 +3,17 @@ * Copyright 2018-2019 NXP Semiconductors */ #include +#include #include #include #include #include #include "felix.h" +#define VSC9959_VCAP_IS2_CNT 1024 +#define VSC9959_VCAP_IS2_ENTRY_WIDTH 376 +#define VSC9959_VCAP_PORT_CNT 6 + /* TODO: should find a better place for these */ #define USXGMII_BMCR_RESET BIT(15) #define USXGMII_BMCR_AN_EN BIT(12) @@ -547,6 +552,129 @@ static const struct ocelot_stat_layout vsc9959_stats_layout[] = { { .offset = 0x111, .name = "drop_green_prio_7", }, }; +struct vcap_field vsc9959_vcap_is2_keys[] = { + /* Common: 41 bits */ + [VCAP_IS2_TYPE] = { 0, 4}, + [VCAP_IS2_HK_FIRST] = { 4, 1}, + [VCAP_IS2_HK_PAG] = { 5, 8}, + [VCAP_IS2_HK_IGR_PORT_MASK] = { 13, 7}, + [VCAP_IS2_HK_RSV2] = { 20, 1}, + [VCAP_IS2_HK_HOST_MATCH] = { 21, 1}, + [VCAP_IS2_HK_L2_MC] = { 22, 1}, + [VCAP_IS2_HK_L2_BC] = { 23, 1}, + [VCAP_IS2_HK_VLAN_TAGGED] = { 24, 1}, + [VCAP_IS2_HK_VID] = { 25, 12}, + [VCAP_IS2_HK_DEI] = { 37, 1}, + [VCAP_IS2_HK_PCP] = { 38, 3}, + /* MAC_ETYPE / MAC_LLC / MAC_SNAP / OAM common */ + [VCAP_IS2_HK_L2_DMAC] = { 41, 48}, + [VCAP_IS2_HK_L2_SMAC] = { 89, 48}, + /* MAC_ETYPE (TYPE=000) */ + [VCAP_IS2_HK_MAC_ETYPE_ETYPE] = {137, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD0] = {153, 16}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD1] = {169, 8}, + [VCAP_IS2_HK_MAC_ETYPE_L2_PAYLOAD2] = {177, 3}, + /* MAC_LLC (TYPE=001) */ + [VCAP_IS2_HK_MAC_LLC_L2_LLC] = {137, 40}, + /* MAC_SNAP (TYPE=010) */ + [VCAP_IS2_HK_MAC_SNAP_L2_SNAP] = {137, 40}, + /* MAC_ARP (TYPE=011) */ + [VCAP_IS2_HK_MAC_ARP_SMAC] = { 41, 48}, + [VCAP_IS2_HK_MAC_ARP_ADDR_SPACE_OK] = { 89, 1}, + [VCAP_IS2_HK_MAC_ARP_PROTO_SPACE_OK] = { 90, 1}, + [VCAP_IS2_HK_MAC_ARP_LEN_OK] = { 91, 1}, + [VCAP_IS2_HK_MAC_ARP_TARGET_MATCH] = { 92, 1}, + [VCAP_IS2_HK_MAC_ARP_SENDER_MATCH] = { 93, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE_UNKNOWN] = { 94, 1}, + [VCAP_IS2_HK_MAC_ARP_OPCODE] = { 95, 2}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_DIP] = { 97, 32}, + [VCAP_IS2_HK_MAC_ARP_L3_IP4_SIP] = {129, 32}, + [VCAP_IS2_HK_MAC_ARP_DIP_EQ_SIP] = {161, 1}, + /* IP4_TCP_UDP / IP4_OTHER common */ + [VCAP_IS2_HK_IP4] = { 41, 1}, + [VCAP_IS2_HK_L3_FRAGMENT] = { 42, 1}, + [VCAP_IS2_HK_L3_FRAG_OFS_GT0] = { 43, 1}, + [VCAP_IS2_HK_L3_OPTIONS] = { 44, 1}, + [VCAP_IS2_HK_IP4_L3_TTL_GT0] = { 45, 1}, + [VCAP_IS2_HK_L3_TOS] = { 46, 8}, + [VCAP_IS2_HK_L3_IP4_DIP] = { 54, 32}, + [VCAP_IS2_HK_L3_IP4_SIP] = { 86, 32}, + [VCAP_IS2_HK_DIP_EQ_SIP] = {118, 1}, + /* IP4_TCP_UDP (TYPE=100) */ + [VCAP_IS2_HK_TCP] = {119, 1}, + [VCAP_IS2_HK_L4_SPORT] = {120, 16}, + [VCAP_IS2_HK_L4_DPORT] = {136, 16}, + [VCAP_IS2_HK_L4_RNG] = {152, 8}, + [VCAP_IS2_HK_L4_SPORT_EQ_DPORT] = {160, 1}, + [VCAP_IS2_HK_L4_SEQUENCE_EQ0] = {161, 1}, + [VCAP_IS2_HK_L4_URG] = {162, 1}, + [VCAP_IS2_HK_L4_ACK] = {163, 1}, + [VCAP_IS2_HK_L4_PSH] = {164, 1}, + [VCAP_IS2_HK_L4_RST] = {165, 1}, + [VCAP_IS2_HK_L4_SYN] = {166, 1}, + [VCAP_IS2_HK_L4_FIN] = {167, 1}, + [VCAP_IS2_HK_L4_1588_DOM] = {168, 8}, + [VCAP_IS2_HK_L4_1588_VER] = {176, 4}, + /* IP4_OTHER (TYPE=101) */ + [VCAP_IS2_HK_IP4_L3_PROTO] = {119, 8}, + [VCAP_IS2_HK_L3_PAYLOAD] = {127, 56}, + /* IP6_STD (TYPE=110) */ + [VCAP_IS2_HK_IP6_L3_TTL_GT0] = { 41, 1}, + [VCAP_IS2_HK_L3_IP6_SIP] = { 42, 128}, + [VCAP_IS2_HK_IP6_L3_PROTO] = {170, 8}, + /* OAM (TYPE=111) */ + [VCAP_IS2_HK_OAM_MEL_FLAGS] = {137, 7}, + [VCAP_IS2_HK_OAM_VER] = {144, 5}, + [VCAP_IS2_HK_OAM_OPCODE] = {149, 8}, + [VCAP_IS2_HK_OAM_FLAGS] = {157, 8}, + [VCAP_IS2_HK_OAM_MEPID] = {165, 16}, + [VCAP_IS2_HK_OAM_CCM_CNTS_EQ0] = {181, 1}, + [VCAP_IS2_HK_OAM_IS_Y1731] = {182, 1}, +}; + +struct vcap_field vsc9959_vcap_is2_actions[] = { + [VCAP_IS2_ACT_HIT_ME_ONCE] = { 0, 1}, + [VCAP_IS2_ACT_CPU_COPY_ENA] = { 1, 1}, + [VCAP_IS2_ACT_CPU_QU_NUM] = { 2, 3}, + [VCAP_IS2_ACT_MASK_MODE] = { 5, 2}, + [VCAP_IS2_ACT_MIRROR_ENA] = { 7, 1}, + [VCAP_IS2_ACT_LRN_DIS] = { 8, 1}, + [VCAP_IS2_ACT_POLICE_ENA] = { 9, 1}, + [VCAP_IS2_ACT_POLICE_IDX] = { 10, 9}, + [VCAP_IS2_ACT_POLICE_VCAP_ONLY] = { 19, 1}, + [VCAP_IS2_ACT_PORT_MASK] = { 20, 11}, + [VCAP_IS2_ACT_REW_OP] = { 31, 9}, + [VCAP_IS2_ACT_SMAC_REPLACE_ENA] = { 40, 1}, + [VCAP_IS2_ACT_RSV] = { 41, 2}, + [VCAP_IS2_ACT_ACL_ID] = { 43, 6}, + [VCAP_IS2_ACT_HIT_CNT] = { 49, 32}, +}; + +static const struct vcap_props vsc9959_vcap_props[] = { + [VCAP_IS2] = { + .tg_width = 2, + .sw_count = 4, + .entry_count = VSC9959_VCAP_IS2_CNT, + .entry_width = VSC9959_VCAP_IS2_ENTRY_WIDTH, + .action_count = VSC9959_VCAP_IS2_CNT + + VSC9959_VCAP_PORT_CNT + 2, + .action_width = 89, + .action_type_width = 1, + .action_table = { + [IS2_ACTION_TYPE_NORMAL] = { + .width = 44, + .count = 2 + }, + [IS2_ACTION_TYPE_SMAC_SIP] = { + .width = 6, + .count = 4 + }, + }, + .counter_words = 4, + .counter_width = 32, + }, +}; + #define VSC9959_INIT_TIMEOUT 50000 #define VSC9959_GCB_RST_SLEEP 100 #define VSC9959_SYS_RAMINIT_SLEEP 80 @@ -1088,6 +1216,9 @@ struct felix_info felix_info_vsc9959 = { .ops = &vsc9959_ops, .stats_layout = vsc9959_stats_layout, .num_stats = ARRAY_SIZE(vsc9959_stats_layout), + .vcap_is2_keys = vsc9959_vcap_is2_keys, + .vcap_is2_actions = vsc9959_vcap_is2_actions, + .vcap = vsc9959_vcap_props, .shared_queue_sz = 128 * 1024, .num_ports = 6, .switch_pci_bar = 4, diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 30270571fb71..5b037f976245 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -549,5 +549,11 @@ int ocelot_ptp_gettime64(struct ptp_clock_info *ptp, struct timespec64 *ts); int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, struct sk_buff *skb); void ocelot_get_txtstamp(struct ocelot *ocelot); +int ocelot_cls_flower_replace(struct ocelot *ocelot, int port, + struct flow_cls_offload *f, bool ingress); +int ocelot_cls_flower_destroy(struct ocelot *ocelot, int port, + struct flow_cls_offload *f, bool ingress); +int ocelot_cls_flower_stats(struct ocelot *ocelot, int port, + struct flow_cls_offload *f, bool ingress); #endif -- cgit v1.2.3 From 55e8c8eb2c7b6bf30e99423ccfe7ca032f498f59 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Fri, 28 Feb 2020 11:11:06 -0600 Subject: posix-cpu-timers: Store a reference to a pid not a task posix cpu timers do not handle the death of a process well. This is most clearly seen when a multi-threaded process calls exec from a thread that is not the leader of the thread group. The posix cpu timer code continues to pin the old thread group leader and is unable to find the siglock from there. This results in posix_cpu_timer_del being unable to delete a timer, posix_cpu_timer_set being unable to set a timer. Further to compensate for the problems in posix_cpu_timer_del on a multi-threaded exec all timers that point at the multi-threaded task are stopped. The code for the timers fundamentally needs to check if the target process/thread is alive. This needs an extra level of indirection. This level of indirection is already available in struct pid. So replace cpu.task with cpu.pid to get the needed extra layer of indirection. In addition to handling things more cleanly this reduces the amount of memory a timer can pin when a process exits and then is reaped from a task_struct to the vastly smaller struct pid. Fixes: e0a70217107e ("posix-cpu-timers: workaround to suppress the problems with mt exec") Signed-off-by: "Eric W. Biederman" Signed-off-by: Thomas Gleixner Link: https://lkml.kernel.org/r/87wo86tz6d.fsf@x220.int.ebiederm.org --- include/linux/posix-timers.h | 2 +- kernel/time/posix-cpu-timers.c | 73 +++++++++++++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 3d10c84a97a9..e3f0f8585da4 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -69,7 +69,7 @@ static inline int clockid_to_fd(const clockid_t clk) struct cpu_timer { struct timerqueue_node node; struct timerqueue_head *head; - struct task_struct *task; + struct pid *pid; struct list_head elist; int firing; }; diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index ef936c5a910b..6df468a622fe 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -118,6 +118,16 @@ static inline int validate_clock_permissions(const clockid_t clock) return __get_task_for_clock(clock, false, false) ? 0 : -EINVAL; } +static inline enum pid_type cpu_timer_pid_type(struct k_itimer *timer) +{ + return CPUCLOCK_PERTHREAD(timer->it_clock) ? PIDTYPE_PID : PIDTYPE_TGID; +} + +static inline struct task_struct *cpu_timer_task_rcu(struct k_itimer *timer) +{ + return pid_task(timer->it.cpu.pid, cpu_timer_pid_type(timer)); +} + /* * Update expiry time from increment, and increase overrun count, * given the current clock sample. @@ -391,7 +401,12 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) new_timer->kclock = &clock_posix_cpu; timerqueue_init(&new_timer->it.cpu.node); - new_timer->it.cpu.task = p; + new_timer->it.cpu.pid = get_task_pid(p, cpu_timer_pid_type(new_timer)); + /* + * get_task_for_clock() took a reference on @p. Drop it as the timer + * holds a reference on the pid of @p. + */ + put_task_struct(p); return 0; } @@ -404,13 +419,15 @@ static int posix_cpu_timer_create(struct k_itimer *new_timer) static int posix_cpu_timer_del(struct k_itimer *timer) { struct cpu_timer *ctmr = &timer->it.cpu; - struct task_struct *p = ctmr->task; struct sighand_struct *sighand; + struct task_struct *p; unsigned long flags; int ret = 0; - if (WARN_ON_ONCE(!p)) - return -EINVAL; + rcu_read_lock(); + p = cpu_timer_task_rcu(timer); + if (!p) + goto out; /* * Protect against sighand release/switch in exit/exec and process/ @@ -432,8 +449,10 @@ static int posix_cpu_timer_del(struct k_itimer *timer) unlock_task_sighand(p, &flags); } +out: + rcu_read_unlock(); if (!ret) - put_task_struct(p); + put_pid(ctmr->pid); return ret; } @@ -561,13 +580,21 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); u64 old_expires, new_expires, old_incr, val; struct cpu_timer *ctmr = &timer->it.cpu; - struct task_struct *p = ctmr->task; struct sighand_struct *sighand; + struct task_struct *p; unsigned long flags; int ret = 0; - if (WARN_ON_ONCE(!p)) - return -EINVAL; + rcu_read_lock(); + p = cpu_timer_task_rcu(timer); + if (!p) { + /* + * If p has just been reaped, we can no + * longer get any information about it at all. + */ + rcu_read_unlock(); + return -ESRCH; + } /* * Use the to_ktime conversion because that clamps the maximum @@ -584,8 +611,10 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, * If p has just been reaped, we can no * longer get any information about it at all. */ - if (unlikely(sighand == NULL)) + if (unlikely(sighand == NULL)) { + rcu_read_unlock(); return -ESRCH; + } /* * Disarm any old timer after extracting its expiry time. @@ -690,6 +719,7 @@ static int posix_cpu_timer_set(struct k_itimer *timer, int timer_flags, ret = 0; out: + rcu_read_unlock(); if (old) old->it_interval = ns_to_timespec64(old_incr); @@ -701,10 +731,12 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); struct cpu_timer *ctmr = &timer->it.cpu; u64 now, expires = cpu_timer_getexpires(ctmr); - struct task_struct *p = ctmr->task; + struct task_struct *p; - if (WARN_ON_ONCE(!p)) - return; + rcu_read_lock(); + p = cpu_timer_task_rcu(timer); + if (!p) + goto out; /* * Easy part: convert the reload time. @@ -712,7 +744,7 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp itp->it_interval = ktime_to_timespec64(timer->it_interval); if (!expires) - return; + goto out; /* * Sample the clock to take the difference with the expiry time. @@ -732,6 +764,8 @@ static void posix_cpu_timer_get(struct k_itimer *timer, struct itimerspec64 *itp itp->it_value.tv_nsec = 1; itp->it_value.tv_sec = 0; } +out: + rcu_read_unlock(); } #define MAX_COLLECTED 20 @@ -952,14 +986,15 @@ static void check_process_timers(struct task_struct *tsk, static void posix_cpu_timer_rearm(struct k_itimer *timer) { clockid_t clkid = CPUCLOCK_WHICH(timer->it_clock); - struct cpu_timer *ctmr = &timer->it.cpu; - struct task_struct *p = ctmr->task; + struct task_struct *p; struct sighand_struct *sighand; unsigned long flags; u64 now; - if (WARN_ON_ONCE(!p)) - return; + rcu_read_lock(); + p = cpu_timer_task_rcu(timer); + if (!p) + goto out; /* * Fetch the current sample and update the timer's expiry time. @@ -974,13 +1009,15 @@ static void posix_cpu_timer_rearm(struct k_itimer *timer) /* Protect timer list r/w in arm_timer() */ sighand = lock_task_sighand(p, &flags); if (unlikely(sighand == NULL)) - return; + goto out; /* * Now re-arm for the new expiry time. */ arm_timer(timer, p); unlock_task_sighand(p, &flags); +out: + rcu_read_unlock(); } /** -- cgit v1.2.3 From 75f81a7ffe4d45b97a14f6d8075bacb8323ac10e Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Wed, 26 Feb 2020 11:57:58 -0800 Subject: usb: typec: Add sysfs node to show cc orientation Export Type-C orientation information when available. - "normal": CC1 orientation - "reverse": CC2 orientation - "unknown": Orientation cannot be determined. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Heikki Krogerus Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20200226195758.150477-1-badhri@google.com Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 9 +++++++++ drivers/usb/typec/class.c | 26 ++++++++++++++++++++++++++ drivers/usb/typec/tcpm/tcpm.c | 1 + include/linux/usb/typec.h | 1 + 4 files changed, 37 insertions(+) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 0c2eb26fdc06..b834671522d6 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -108,6 +108,15 @@ Contact: Heikki Krogerus Description: Revision number of the supported USB Type-C specification. +What: /sys/class/typec//orientation +Date: February 2020 +Contact: Badhri Jagan Sridharan +Description: + Indicates the active orientation of the Type-C connector. + Valid values: + - "normal": CC1 orientation + - "reverse": CC2 orientation + - "unknown": Orientation cannot be determined. USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 12be5bb6d32c..bf97c31d0bba 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1244,6 +1244,25 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev, } static DEVICE_ATTR_RO(usb_power_delivery_revision); +static ssize_t orientation_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct typec_port *p = to_typec_port(dev); + enum typec_orientation orientation = typec_get_orientation(p); + + switch (orientation) { + case TYPEC_ORIENTATION_NORMAL: + return sprintf(buf, "%s\n", "normal"); + case TYPEC_ORIENTATION_REVERSE: + return sprintf(buf, "%s\n", "reverse"); + case TYPEC_ORIENTATION_NONE: + default: + return sprintf(buf, "%s\n", "unknown"); + } +} +static DEVICE_ATTR_RO(orientation); + static struct attribute *typec_attrs[] = { &dev_attr_data_role.attr, &dev_attr_power_operation_mode.attr, @@ -1254,6 +1273,7 @@ static struct attribute *typec_attrs[] = { &dev_attr_usb_typec_revision.attr, &dev_attr_vconn_source.attr, &dev_attr_port_type.attr, + &dev_attr_orientation.attr, NULL, }; @@ -1283,6 +1303,10 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, return 0; if (port->cap->type != TYPEC_PORT_DRP) return 0444; + } else if (attr == &dev_attr_orientation.attr) { + if (port->cap->orientation_aware) + return 0444; + return 0; } return attr->mode; @@ -1493,6 +1517,8 @@ int typec_set_orientation(struct typec_port *port, } port->orientation = orientation; + sysfs_notify(&port->dev.kobj, NULL, "orientation"); + kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); return 0; } diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 78077c234ef2..bc0032a6b9a1 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4742,6 +4742,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) port->typec_caps.pd_revision = 0x0300; /* USB-PD spec release 3.0 */ port->typec_caps.driver_data = port; port->typec_caps.ops = &tcpm_ops; + port->typec_caps.orientation_aware = 1; port->partner_desc.identity = &port->partner_ident; port->port_type = port->typec_caps.type; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 44d28387ced4..b00a2642a9cd 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -211,6 +211,7 @@ struct typec_capability { u16 pd_revision; /* 0300H = "3.0" */ int prefer_role; enum typec_accessory accessory[TYPEC_MAX_ACCESSORY]; + unsigned int orientation_aware:1; struct fwnode_handle *fwnode; void *driver_data; -- cgit v1.2.3 From c111566bea7ccd8a05e2c56f1fb3cbb6f4b7b441 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 25 Feb 2020 11:31:02 +0200 Subject: PM: runtime: Add pm_runtime_get_if_active() pm_runtime_get_if_in_use() bumps up the PM-runtime usage count if it is not equal to zero and the device's PM-runtime status is 'active'. This works for drivers that do not use autoidle, but for those that do, the function returns zero even when the device is active. In order to maintain sane device state while the device is powered on in the hope that it'll be needed, pm_runtime_get_if_active(dev, true) returns a positive value if the device's PM-runtime status is 'active' when it is called, in which case it also increments the device's usage count. If the second argument of pm_runtime_get_if_active() is 'false', the function behaves just like pm_runtime_get_if_in_use(), so redefine the latter as a wrapper around the former. Signed-off-by: Sakari Ailus [ rjw: Changelog ] Signed-off-by: Rafael J. Wysocki --- Documentation/power/runtime_pm.rst | 6 ++++++ drivers/base/power/runtime.c | 36 +++++++++++++++++++++++++++--------- include/linux/pm_runtime.h | 12 +++++++++++- 3 files changed, 44 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/power/runtime_pm.rst b/Documentation/power/runtime_pm.rst index ab8406c84254..0553008b6279 100644 --- a/Documentation/power/runtime_pm.rst +++ b/Documentation/power/runtime_pm.rst @@ -382,6 +382,12 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h: nonzero, increment the counter and return 1; otherwise return 0 without changing the counter + `int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count);` + - return -EINVAL if 'power.disable_depth' is nonzero; otherwise, if the + runtime PM status is RPM_ACTIVE, and either ign_usage_count is true + or the device's usage_count is non-zero, increment the counter and + return 1; otherwise return 0 without changing the counter + `void pm_runtime_put_noidle(struct device *dev);` - decrement the device's usage counter diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 16134a69bf6f..99c7da112c95 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1087,29 +1087,47 @@ int __pm_runtime_resume(struct device *dev, int rpmflags) EXPORT_SYMBOL_GPL(__pm_runtime_resume); /** - * pm_runtime_get_if_in_use - Conditionally bump up the device's usage counter. + * pm_runtime_get_if_active - Conditionally bump up the device's usage counter. * @dev: Device to handle. * * Return -EINVAL if runtime PM is disabled for the device. * - * If that's not the case and if the device's runtime PM status is RPM_ACTIVE - * and the runtime PM usage counter is nonzero, increment the counter and - * return 1. Otherwise return 0 without changing the counter. + * Otherwise, if the device's runtime PM status is RPM_ACTIVE and either + * ign_usage_count is true or the device's usage_count is non-zero, increment + * the counter and return 1. Otherwise return 0 without changing the counter. + * + * If ign_usage_count is true, the function can be used to prevent suspending + * the device when its runtime PM status is RPM_ACTIVE. + * + * If ign_usage_count is false, the function can be used to prevent suspending + * the device when both its runtime PM status is RPM_ACTIVE and its usage_count + * is non-zero. + * + * The caller is resposible for putting the device's usage count when ther + * return value is greater than zero. */ -int pm_runtime_get_if_in_use(struct device *dev) +int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count) { unsigned long flags; int retval; spin_lock_irqsave(&dev->power.lock, flags); - retval = dev->power.disable_depth > 0 ? -EINVAL : - dev->power.runtime_status == RPM_ACTIVE - && atomic_inc_not_zero(&dev->power.usage_count); + if (dev->power.disable_depth > 0) { + retval = -EINVAL; + } else if (dev->power.runtime_status != RPM_ACTIVE) { + retval = 0; + } else if (ign_usage_count) { + retval = 1; + atomic_inc(&dev->power.usage_count); + } else { + retval = atomic_inc_not_zero(&dev->power.usage_count); + } trace_rpm_usage_rcuidle(dev, 0); spin_unlock_irqrestore(&dev->power.lock, flags); + return retval; } -EXPORT_SYMBOL_GPL(pm_runtime_get_if_in_use); +EXPORT_SYMBOL_GPL(pm_runtime_get_if_active); /** * __pm_runtime_set_status - Set runtime PM status of a device. diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h index 22af69d237a6..3bdcbce8141a 100644 --- a/include/linux/pm_runtime.h +++ b/include/linux/pm_runtime.h @@ -38,7 +38,7 @@ extern int pm_runtime_force_resume(struct device *dev); extern int __pm_runtime_idle(struct device *dev, int rpmflags); extern int __pm_runtime_suspend(struct device *dev, int rpmflags); extern int __pm_runtime_resume(struct device *dev, int rpmflags); -extern int pm_runtime_get_if_in_use(struct device *dev); +extern int pm_runtime_get_if_active(struct device *dev, bool ign_usage_count); extern int pm_schedule_suspend(struct device *dev, unsigned int delay); extern int __pm_runtime_set_status(struct device *dev, unsigned int status); extern int pm_runtime_barrier(struct device *dev); @@ -60,6 +60,11 @@ extern void pm_runtime_put_suppliers(struct device *dev); extern void pm_runtime_new_link(struct device *dev); extern void pm_runtime_drop_link(struct device *dev); +static inline int pm_runtime_get_if_in_use(struct device *dev) +{ + return pm_runtime_get_if_active(dev, false); +} + static inline void pm_suspend_ignore_children(struct device *dev, bool enable) { dev->power.ignore_children = enable; @@ -143,6 +148,11 @@ static inline int pm_runtime_get_if_in_use(struct device *dev) { return -EINVAL; } +static inline int pm_runtime_get_if_active(struct device *dev, + bool ign_usage_count) +{ + return -EINVAL; +} static inline int __pm_runtime_set_status(struct device *dev, unsigned int status) { return 0; } static inline int pm_runtime_barrier(struct device *dev) { return 0; } -- cgit v1.2.3 From ef441dd6af91e1f4265e890021ac3ad631b2b10e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:45 +0300 Subject: usb: typec: mux: Allow the muxes to be named The mux devices have been named by using the name of the parent device as base until now, but if for example the parent device has multiple muxes that will not work. This makes it possible to supply the name for a mux during registration. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 6 ++++-- include/linux/usb/typec_mux.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index b952fa2fff39..2a073614172a 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -127,7 +127,8 @@ typec_switch_register(struct device *parent, sw->dev.class = &typec_mux_class; sw->dev.type = &typec_switch_dev_type; sw->dev.driver_data = desc->drvdata; - dev_set_name(&sw->dev, "%s-switch", dev_name(parent)); + dev_set_name(&sw->dev, "%s-switch", + desc->name ? desc->name : dev_name(parent)); ret = device_add(&sw->dev); if (ret) { @@ -309,7 +310,8 @@ typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) mux->dev.class = &typec_mux_class; mux->dev.type = &typec_mux_dev_type; mux->dev.driver_data = desc->drvdata; - dev_set_name(&mux->dev, "%s-mux", dev_name(parent)); + dev_set_name(&mux->dev, "%s-mux", + desc->name ? desc->name : dev_name(parent)); ret = device_add(&mux->dev); if (ret) { diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index be7292c0be5e..47ab5a828b07 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -17,6 +17,7 @@ typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw, struct typec_switch_desc { struct fwnode_handle *fwnode; typec_switch_set_fn_t set; + const char *name; void *drvdata; }; @@ -42,6 +43,7 @@ typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux, struct typec_mux_desc { struct fwnode_handle *fwnode; typec_mux_set_fn_t set; + const char *name; void *drvdata; }; -- cgit v1.2.3 From 774a9df6aeac236282dc0ec711b73865b64ef6a1 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:46 +0300 Subject: usb: typec: mux: Add helpers for setting the mux state Adding helpers typec_switch_set() and typec_mux_set() that simply call the ->set callback function of the mux. These functions make it possible to set the mux states also from outside the class code. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 10 ++++------ drivers/usb/typec/mux.c | 19 +++++++++++++++++++ include/linux/usb/typec_mux.h | 5 +++++ 3 files changed, 28 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index bf97c31d0bba..2a33ff159d04 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1510,11 +1510,9 @@ int typec_set_orientation(struct typec_port *port, { int ret; - if (port->sw) { - ret = port->sw->set(port->sw, orientation); - if (ret) - return ret; - } + ret = typec_switch_set(port->sw, orientation); + if (ret) + return ret; port->orientation = orientation; sysfs_notify(&port->dev.kobj, NULL, "orientation"); @@ -1550,7 +1548,7 @@ int typec_set_mode(struct typec_port *port, int mode) state.mode = mode; - return port->mux ? port->mux->set(port->mux, &state) : 0; + return typec_mux_set(port->mux, &state); } EXPORT_SYMBOL_GPL(typec_set_mode); diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 2a073614172a..800c140d7853 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -141,6 +141,16 @@ typec_switch_register(struct device *parent, } EXPORT_SYMBOL_GPL(typec_switch_register); +int typec_switch_set(struct typec_switch *sw, + enum typec_orientation orientation) +{ + if (IS_ERR_OR_NULL(sw)) + return 0; + + return sw->set(sw, orientation); +} +EXPORT_SYMBOL_GPL(typec_switch_set); + /** * typec_switch_unregister - Unregister USB Type-C orientation switch * @sw: USB Type-C orientation switch @@ -269,6 +279,15 @@ void typec_mux_put(struct typec_mux *mux) } EXPORT_SYMBOL_GPL(typec_mux_put); +int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state) +{ + if (IS_ERR_OR_NULL(mux)) + return 0; + + return mux->set(mux, state); +} +EXPORT_SYMBOL_GPL(typec_mux_set); + static void typec_mux_release(struct device *dev) { kfree(to_typec_mux(dev)); diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 47ab5a828b07..4991c93df5d0 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -23,6 +23,9 @@ struct typec_switch_desc { struct typec_switch *typec_switch_get(struct device *dev); void typec_switch_put(struct typec_switch *sw); +int typec_switch_set(struct typec_switch *sw, + enum typec_orientation orientation); + struct typec_switch * typec_switch_register(struct device *parent, const struct typec_switch_desc *desc); @@ -50,6 +53,8 @@ struct typec_mux_desc { struct typec_mux * typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc); void typec_mux_put(struct typec_mux *mux); +int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state); + struct typec_mux * typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); void typec_mux_unregister(struct typec_mux *mux); -- cgit v1.2.3 From d1c6a769cdf466053ae211789f2b0671c8a72331 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:47 +0300 Subject: usb: typec: mux: Allow the mux handles to be requested with fwnode Introducing fwnode_typec_switch_get() and fwnode_typec_mux_get() functions that work just like typec_switch_get() and typec_mux_get() but they take struct fwnode_handle as the first parameter instead of struct device. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-4-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 22 +++++++++++----------- include/linux/usb/typec_mux.h | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index 800c140d7853..52ad277e4565 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -49,26 +49,26 @@ static void *typec_switch_match(struct device_connection *con, int ep, } /** - * typec_switch_get - Find USB Type-C orientation switch - * @dev: The caller device + * fwnode_typec_switch_get - Find USB Type-C orientation switch + * @fwnode: The caller device node * * Finds a switch linked with @dev. Returns a reference to the switch on * success, NULL if no matching connection was found, or * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch * has not been enumerated yet. */ -struct typec_switch *typec_switch_get(struct device *dev) +struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode) { struct typec_switch *sw; - sw = device_connection_find_match(dev, "orientation-switch", NULL, + sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL, typec_switch_match); if (!IS_ERR_OR_NULL(sw)) WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); return sw; } -EXPORT_SYMBOL_GPL(typec_switch_get); +EXPORT_SYMBOL_GPL(fwnode_typec_switch_get); /** * typec_put_switch - Release USB Type-C orientation switch @@ -241,8 +241,8 @@ find_mux: } /** - * typec_mux_get - Find USB Type-C Multiplexer - * @dev: The caller device + * fwnode_typec_mux_get - Find USB Type-C Multiplexer + * @fwnode: The caller device node * @desc: Alt Mode description * * Finds a mux linked to the caller. This function is primarily meant for the @@ -250,19 +250,19 @@ find_mux: * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection * was found but the mux has not been enumerated yet. */ -struct typec_mux *typec_mux_get(struct device *dev, - const struct typec_altmode_desc *desc) +struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, + const struct typec_altmode_desc *desc) { struct typec_mux *mux; - mux = device_connection_find_match(dev, "mode-switch", (void *)desc, + mux = fwnode_connection_find_match(fwnode, "mode-switch", (void *)desc, typec_mux_match); if (!IS_ERR_OR_NULL(mux)) WARN_ON(!try_module_get(mux->dev.parent->driver->owner)); return mux; } -EXPORT_SYMBOL_GPL(typec_mux_get); +EXPORT_SYMBOL_GPL(fwnode_typec_mux_get); /** * typec_mux_put - Release handle to a Multiplexer diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 4991c93df5d0..a9d9957933dc 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -3,6 +3,7 @@ #ifndef __USB_TYPEC_MUX #define __USB_TYPEC_MUX +#include #include struct device; @@ -21,11 +22,16 @@ struct typec_switch_desc { void *drvdata; }; -struct typec_switch *typec_switch_get(struct device *dev); +struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode); void typec_switch_put(struct typec_switch *sw); int typec_switch_set(struct typec_switch *sw, enum typec_orientation orientation); +static inline struct typec_switch *typec_switch_get(struct device *dev) +{ + return fwnode_typec_switch_get(dev_fwnode(dev)); +} + struct typec_switch * typec_switch_register(struct device *parent, const struct typec_switch_desc *desc); @@ -50,11 +56,17 @@ struct typec_mux_desc { void *drvdata; }; -struct typec_mux * -typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc); +struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, + const struct typec_altmode_desc *desc); void typec_mux_put(struct typec_mux *mux); int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state); +static inline struct typec_mux * +typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) +{ + return fwnode_typec_mux_get(dev_fwnode(dev), desc); +} + struct typec_mux * typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); void typec_mux_unregister(struct typec_mux *mux); -- cgit v1.2.3 From 69af044a7700552512a147e2ce3520741b65df41 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:48 +0300 Subject: usb: roles: Leave the private driver data pointer to the drivers Adding usb_role_switch_get/set_drvdata() functions that the switch drivers can use for setting and getting private data pointer that is associated with the switch. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-5-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 22 ++++++++++++++++++++++ include/linux/usb/role.h | 16 ++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'include') diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 486b0b1e2a7a..fe34959af230 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -329,6 +329,7 @@ usb_role_switch_register(struct device *parent, sw->dev.fwnode = desc->fwnode; sw->dev.class = role_class; sw->dev.type = &usb_role_dev_type; + dev_set_drvdata(&sw->dev, desc->driver_data); dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); ret = device_register(&sw->dev); @@ -356,6 +357,27 @@ void usb_role_switch_unregister(struct usb_role_switch *sw) } EXPORT_SYMBOL_GPL(usb_role_switch_unregister); +/** + * usb_role_switch_set_drvdata - Assign private data pointer to a switch + * @sw: USB Role Switch + * @data: Private data pointer + */ +void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data) +{ + dev_set_drvdata(&sw->dev, data); +} +EXPORT_SYMBOL_GPL(usb_role_switch_set_drvdata); + +/** + * usb_role_switch_get_drvdata - Get the private data pointer of a switch + * @sw: USB Role Switch + */ +void *usb_role_switch_get_drvdata(struct usb_role_switch *sw) +{ + return dev_get_drvdata(&sw->dev); +} +EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata); + static int __init usb_roles_init(void) { role_class = class_create(THIS_MODULE, "usb_role"); diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index efac3af83d6b..02dae936cebd 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -25,6 +25,7 @@ typedef enum usb_role (*usb_role_switch_get_t)(struct device *dev); * @set: Callback for setting the role * @get: Callback for getting the role (optional) * @allow_userspace_control: If true userspace may change the role through sysfs + * @driver_data: Private data pointer * * @usb2_port and @usb3_port will point to the USB host port and @udc to the USB * device controller behind the USB connector with the role switch. If @@ -40,6 +41,7 @@ struct usb_role_switch_desc { usb_role_switch_set_t set; usb_role_switch_get_t get; bool allow_userspace_control; + void *driver_data; }; @@ -57,6 +59,9 @@ struct usb_role_switch * usb_role_switch_register(struct device *parent, const struct usb_role_switch_desc *desc); void usb_role_switch_unregister(struct usb_role_switch *sw); + +void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data); +void *usb_role_switch_get_drvdata(struct usb_role_switch *sw); #else static inline int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) @@ -90,6 +95,17 @@ usb_role_switch_register(struct device *parent, } static inline void usb_role_switch_unregister(struct usb_role_switch *sw) { } + +static inline void +usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data) +{ +} + +static inline void *usb_role_switch_get_drvdata(struct usb_role_switch *sw) +{ + return NULL; +} + #endif #endif /* __LINUX_USB_ROLE_H */ -- cgit v1.2.3 From bce3052f0c165685a074e50136e4d341bcd59f4a Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:49 +0300 Subject: usb: roles: Provide the switch drivers handle to the switch in the API The USB role callback functions had a parameter pointing to the parent device (struct device) of the switch. The assumption was that the switch parent is always the controller. Firstly, that may not be true in every case, and secondly, it prevents us from supporting devices that supply multiple muxes. Changing the first parameter of usb_role_switch_set_t and usb_role_switch_get_t from struct device to struct usb_role_switch. Cc: Peter Chen Cc: Felipe Balbi Cc: Chunfeng Yun Cc: Bin Liu Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-6-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/core.c | 22 +++++++++++----------- drivers/usb/chipidea/core.c | 10 ++++++---- drivers/usb/dwc3/dwc3-meson-g12a.c | 10 ++++++---- drivers/usb/gadget/udc/renesas_usb3.c | 26 ++++++++++++++------------ drivers/usb/gadget/udc/tegra-xudc.c | 8 +++++--- drivers/usb/mtu3/mtu3_dr.c | 9 +++++---- drivers/usb/musb/mediatek.c | 16 ++++++++++------ drivers/usb/roles/class.c | 4 ++-- drivers/usb/roles/intel-xhci-usb-role-switch.c | 26 +++++++++++++++----------- include/linux/usb/role.h | 5 +++-- 10 files changed, 77 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index c2123ef8d8a3..4aafba20f450 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -330,9 +330,9 @@ exit: * * Returns role */ -static enum usb_role cdns3_role_get(struct device *dev) +static enum usb_role cdns3_role_get(struct usb_role_switch *sw) { - struct cdns3 *cdns = dev_get_drvdata(dev); + struct cdns3 *cdns = usb_role_switch_get_drvdata(sw); return cdns->role; } @@ -346,9 +346,9 @@ static enum usb_role cdns3_role_get(struct device *dev) * - Role switch for dual-role devices * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices */ -static int cdns3_role_set(struct device *dev, enum usb_role role) +static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role) { - struct cdns3 *cdns = dev_get_drvdata(dev); + struct cdns3 *cdns = usb_role_switch_get_drvdata(sw); int ret = 0; pm_runtime_get_sync(cdns->dev); @@ -423,12 +423,6 @@ pm_put: return ret; } -static const struct usb_role_switch_desc cdns3_switch_desc = { - .set = cdns3_role_set, - .get = cdns3_role_get, - .allow_userspace_control = true, -}; - /** * cdns3_probe - probe for cdns3 core device * @pdev: Pointer to cdns3 core platform device @@ -437,6 +431,7 @@ static const struct usb_role_switch_desc cdns3_switch_desc = { */ static int cdns3_probe(struct platform_device *pdev) { + struct usb_role_switch_desc sw_desc = { }; struct device *dev = &pdev->dev; struct resource *res; struct cdns3 *cdns; @@ -529,7 +524,12 @@ static int cdns3_probe(struct platform_device *pdev) if (ret) goto err3; - cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc); + sw_desc.set = cdns3_role_set; + sw_desc.get = cdns3_role_get; + sw_desc.allow_userspace_control = true; + sw_desc.driver_data = cdns; + + cdns->role_sw = usb_role_switch_register(dev, &sw_desc); if (IS_ERR(cdns->role_sw)) { ret = PTR_ERR(cdns->role_sw); dev_warn(dev, "Unable to register Role Switch\n"); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 52139c2a9924..ae0bdc036464 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -600,9 +600,9 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event, return NOTIFY_DONE; } -static enum usb_role ci_usb_role_switch_get(struct device *dev) +static enum usb_role ci_usb_role_switch_get(struct usb_role_switch *sw) { - struct ci_hdrc *ci = dev_get_drvdata(dev); + struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); enum usb_role role; unsigned long flags; @@ -613,9 +613,10 @@ static enum usb_role ci_usb_role_switch_get(struct device *dev) return role; } -static int ci_usb_role_switch_set(struct device *dev, enum usb_role role) +static int ci_usb_role_switch_set(struct usb_role_switch *sw, + enum usb_role role) { - struct ci_hdrc *ci = dev_get_drvdata(dev); + struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw); struct ci_hdrc_cable *cable = NULL; enum usb_role current_role = ci_role_to_usb_role(ci); enum ci_role ci_role = usb_role_to_ci_role(role); @@ -1118,6 +1119,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) } if (ci_role_switch.fwnode) { + ci_role_switch.driver_data = ci; ci->role_switch = usb_role_switch_register(dev, &ci_role_switch); if (IS_ERR(ci->role_switch)) { diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 8a3ec1a951fe..3309ce90ca14 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -321,9 +321,10 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, return 0; } -static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) +static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw, + enum usb_role role) { - struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw); enum phy_mode mode; if (role == USB_ROLE_NONE) @@ -338,9 +339,9 @@ static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role) return dwc3_meson_g12a_otg_mode_set(priv, mode); } -static enum usb_role dwc3_meson_g12a_role_get(struct device *dev) +static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw) { - struct dwc3_meson_g12a *priv = dev_get_drvdata(dev); + struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw); return priv->otg_phy_mode == PHY_MODE_USB_HOST ? USB_ROLE_HOST : USB_ROLE_DEVICE; @@ -499,6 +500,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) priv->switch_desc.allow_userspace_control = true; priv->switch_desc.set = dwc3_meson_g12a_role_set; priv->switch_desc.get = dwc3_meson_g12a_role_get; + priv->switch_desc.driver_data = priv; priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc); if (IS_ERR(priv->role_switch)) diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index 42ae99ad9b25..0c418ce50ba0 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2355,14 +2355,14 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = { .set_selfpowered = renesas_usb3_set_selfpowered, }; -static enum usb_role renesas_usb3_role_switch_get(struct device *dev) +static enum usb_role renesas_usb3_role_switch_get(struct usb_role_switch *sw) { - struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + struct renesas_usb3 *usb3 = usb_role_switch_get_drvdata(sw); enum usb_role cur_role; - pm_runtime_get_sync(dev); + pm_runtime_get_sync(usb3_to_dev(usb3)); cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE; - pm_runtime_put(dev); + pm_runtime_put(usb3_to_dev(usb3)); return cur_role; } @@ -2372,7 +2372,7 @@ static void handle_ext_role_switch_states(struct device *dev, { struct renesas_usb3 *usb3 = dev_get_drvdata(dev); struct device *host = usb3->host_dev; - enum usb_role cur_role = renesas_usb3_role_switch_get(dev); + enum usb_role cur_role = renesas_usb3_role_switch_get(usb3->role_sw); switch (role) { case USB_ROLE_NONE: @@ -2424,7 +2424,7 @@ static void handle_role_switch_states(struct device *dev, { struct renesas_usb3 *usb3 = dev_get_drvdata(dev); struct device *host = usb3->host_dev; - enum usb_role cur_role = renesas_usb3_role_switch_get(dev); + enum usb_role cur_role = renesas_usb3_role_switch_get(usb3->role_sw); if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) { device_release_driver(host); @@ -2438,19 +2438,19 @@ static void handle_role_switch_states(struct device *dev, } } -static int renesas_usb3_role_switch_set(struct device *dev, +static int renesas_usb3_role_switch_set(struct usb_role_switch *sw, enum usb_role role) { - struct renesas_usb3 *usb3 = dev_get_drvdata(dev); + struct renesas_usb3 *usb3 = usb_role_switch_get_drvdata(sw); - pm_runtime_get_sync(dev); + pm_runtime_get_sync(usb3_to_dev(usb3)); if (usb3->role_sw_by_connector) - handle_ext_role_switch_states(dev, role); + handle_ext_role_switch_states(usb3_to_dev(usb3), role); else - handle_role_switch_states(dev, role); + handle_role_switch_states(usb3_to_dev(usb3), role); - pm_runtime_put(dev); + pm_runtime_put(usb3_to_dev(usb3)); return 0; } @@ -2831,6 +2831,8 @@ static int renesas_usb3_probe(struct platform_device *pdev) renesas_usb3_role_switch_desc.fwnode = dev_fwnode(&pdev->dev); } + renesas_usb3_role_switch_desc.driver_data = usb3; + INIT_WORK(&usb3->role_work, renesas_usb3_role_work); usb3->role_sw = usb_role_switch_register(&pdev->dev, &renesas_usb3_role_switch_desc); diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 634c2c19a176..b9df6369d56d 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -676,12 +676,13 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work) } -static int tegra_xudc_usb_role_sw_set(struct device *dev, enum usb_role role) +static int tegra_xudc_usb_role_sw_set(struct usb_role_switch *sw, + enum usb_role role) { - struct tegra_xudc *xudc = dev_get_drvdata(dev); + struct tegra_xudc *xudc = usb_role_switch_get_drvdata(sw); unsigned long flags; - dev_dbg(dev, "%s role is %d\n", __func__, role); + dev_dbg(xudc->dev, "%s role is %d\n", __func__, role); spin_lock_irqsave(&xudc->lock, flags); @@ -3590,6 +3591,7 @@ static int tegra_xudc_probe(struct platform_device *pdev) if (of_property_read_bool(xudc->dev->of_node, "usb-role-switch")) { role_sx_desc.set = tegra_xudc_usb_role_sw_set; role_sx_desc.fwnode = dev_fwnode(xudc->dev); + role_sx_desc.driver_data = xudc; xudc->usb_role_sw = usb_role_switch_register(xudc->dev, &role_sx_desc); diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index 08e18448e8b8..04f666e85731 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -320,9 +320,9 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb, mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value); } -static int ssusb_role_sw_set(struct device *dev, enum usb_role role) +static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role) { - struct ssusb_mtk *ssusb = dev_get_drvdata(dev); + struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw); bool to_host = false; if (role == USB_ROLE_HOST) @@ -334,9 +334,9 @@ static int ssusb_role_sw_set(struct device *dev, enum usb_role role) return 0; } -static enum usb_role ssusb_role_sw_get(struct device *dev) +static enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw) { - struct ssusb_mtk *ssusb = dev_get_drvdata(dev); + struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw); enum usb_role role; role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE; @@ -356,6 +356,7 @@ static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx) role_sx_desc.set = ssusb_role_sw_set; role_sx_desc.get = ssusb_role_sw_get; role_sx_desc.fwnode = dev_fwnode(ssusb->dev); + role_sx_desc.driver_data = ssusb; otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc); return PTR_ERR_OR_ZERO(otg_sx->role_sw); diff --git a/drivers/usb/musb/mediatek.c b/drivers/usb/musb/mediatek.c index 6b88c2f5d970..a627f4133d6b 100644 --- a/drivers/usb/musb/mediatek.c +++ b/drivers/usb/musb/mediatek.c @@ -115,9 +115,8 @@ static void mtk_musb_clks_disable(struct mtk_glue *glue) clk_disable_unprepare(glue->main); } -static int musb_usb_role_sx_set(struct device *dev, enum usb_role role) +static int mtk_otg_switch_set(struct mtk_glue *glue, enum usb_role role) { - struct mtk_glue *glue = dev_get_drvdata(dev); struct musb *musb = glue->musb; u8 devctl = readb(musb->mregs + MUSB_DEVCTL); enum usb_role new_role; @@ -168,9 +167,14 @@ static int musb_usb_role_sx_set(struct device *dev, enum usb_role role) return 0; } -static enum usb_role musb_usb_role_sx_get(struct device *dev) +static int musb_usb_role_sx_set(struct usb_role_switch *sw, enum usb_role role) { - struct mtk_glue *glue = dev_get_drvdata(dev); + return mtk_otg_switch_set(usb_role_switch_get_drvdata(sw), role); +} + +static enum usb_role musb_usb_role_sx_get(struct usb_role_switch *sw) +{ + struct mtk_glue *glue = usb_role_switch_get_drvdata(sw); return glue->role; } @@ -182,6 +186,7 @@ static int mtk_otg_switch_init(struct mtk_glue *glue) role_sx_desc.set = musb_usb_role_sx_set; role_sx_desc.get = musb_usb_role_sx_get; role_sx_desc.fwnode = dev_fwnode(glue->dev); + role_sx_desc.driver_data = glue; glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc); return PTR_ERR_OR_ZERO(glue->role_sw); @@ -288,8 +293,7 @@ static int mtk_musb_set_mode(struct musb *musb, u8 mode) return -EINVAL; } - glue->role = new_role; - musb_usb_role_sx_set(dev, glue->role); + mtk_otg_switch_set(glue, new_role); return 0; } diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index fe34959af230..cda8cbb54581 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -48,7 +48,7 @@ int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) mutex_lock(&sw->lock); - ret = sw->set(sw->dev.parent, role); + ret = sw->set(sw, role); if (!ret) sw->role = role; @@ -75,7 +75,7 @@ enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) mutex_lock(&sw->lock); if (sw->get) - role = sw->get(sw->dev.parent); + role = sw->get(sw); else role = sw->role; diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 80d6559bbcb2..5c96e929acea 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -42,6 +42,7 @@ #define DRV_NAME "intel_xhci_usb_sw" struct intel_xhci_usb_data { + struct device *dev; struct usb_role_switch *role_sw; void __iomem *base; bool enable_sw_switch; @@ -51,9 +52,10 @@ static const struct software_node intel_xhci_usb_node = { "intel-xhci-usb-sw", }; -static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) +static int intel_xhci_usb_set_role(struct usb_role_switch *sw, + enum usb_role role) { - struct intel_xhci_usb_data *data = dev_get_drvdata(dev); + struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw); unsigned long timeout; acpi_status status; u32 glk, val; @@ -66,11 +68,11 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) */ status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk); if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) { - dev_err(dev, "Error could not acquire lock\n"); + dev_err(data->dev, "Error could not acquire lock\n"); return -EIO; } - pm_runtime_get_sync(dev); + pm_runtime_get_sync(data->dev); /* * Set idpin value as requested. @@ -112,7 +114,7 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) do { val = readl(data->base + DUAL_ROLE_CFG1); if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) { - pm_runtime_put(dev); + pm_runtime_put(data->dev); return 0; } @@ -120,21 +122,21 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role) usleep_range(5000, 10000); } while (time_before(jiffies, timeout)); - pm_runtime_put(dev); + pm_runtime_put(data->dev); - dev_warn(dev, "Timeout waiting for role-switch\n"); + dev_warn(data->dev, "Timeout waiting for role-switch\n"); return -ETIMEDOUT; } -static enum usb_role intel_xhci_usb_get_role(struct device *dev) +static enum usb_role intel_xhci_usb_get_role(struct usb_role_switch *sw) { - struct intel_xhci_usb_data *data = dev_get_drvdata(dev); + struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw); enum usb_role role; u32 val; - pm_runtime_get_sync(dev); + pm_runtime_get_sync(data->dev); val = readl(data->base + DUAL_ROLE_CFG0); - pm_runtime_put(dev); + pm_runtime_put(data->dev); if (!(val & SW_IDPIN)) role = USB_ROLE_HOST; @@ -175,7 +177,9 @@ static int intel_xhci_usb_probe(struct platform_device *pdev) sw_desc.get = intel_xhci_usb_get_role, sw_desc.allow_userspace_control = true, sw_desc.fwnode = software_node_fwnode(&intel_xhci_usb_node); + sw_desc.driver_data = data; + data->dev = dev; data->enable_sw_switch = !device_property_read_bool(dev, "sw_switch_disable"); diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index 02dae936cebd..c028ba8029ad 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -13,8 +13,9 @@ enum usb_role { USB_ROLE_DEVICE, }; -typedef int (*usb_role_switch_set_t)(struct device *dev, enum usb_role role); -typedef enum usb_role (*usb_role_switch_get_t)(struct device *dev); +typedef int (*usb_role_switch_set_t)(struct usb_role_switch *sw, + enum usb_role role); +typedef enum usb_role (*usb_role_switch_get_t)(struct usb_role_switch *sw); /** * struct usb_role_switch_desc - USB Role Switch Descriptor -- cgit v1.2.3 From e5256194cb51ced7a2f656590f1e2132235d442c Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:50 +0300 Subject: usb: roles: Allow the role switches to be named The switch devices have been named by using the name of the parent device as base for now, but if for example the parent device controls multiple muxes, that will not work. Adding an optional member "name" to the switch descriptor that can be used for naming the switch during registration. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-7-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 3 ++- include/linux/usb/role.h | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index cda8cbb54581..5b17709821df 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -330,7 +330,8 @@ usb_role_switch_register(struct device *parent, sw->dev.class = role_class; sw->dev.type = &usb_role_dev_type; dev_set_drvdata(&sw->dev, desc->driver_data); - dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); + dev_set_name(&sw->dev, "%s-role-switch", + desc->name ? desc->name : dev_name(parent)); ret = device_register(&sw->dev); if (ret) { diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index c028ba8029ad..0164fed31b06 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -27,6 +27,7 @@ typedef enum usb_role (*usb_role_switch_get_t)(struct usb_role_switch *sw); * @get: Callback for getting the role (optional) * @allow_userspace_control: If true userspace may change the role through sysfs * @driver_data: Private data pointer + * @name: Name for the switch (optional) * * @usb2_port and @usb3_port will point to the USB host port and @udc to the USB * device controller behind the USB connector with the role switch. If @@ -43,6 +44,7 @@ struct usb_role_switch_desc { usb_role_switch_get_t get; bool allow_userspace_control; void *driver_data; + const char *name; }; -- cgit v1.2.3 From ca469c292edc2248f1eaaef1ecec4576192fe743 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 2 Mar 2020 16:53:52 +0300 Subject: usb: typec: Add definitions for Thunderbolt 3 Alternate Mode This adds separate header file for the Thunderbolt 3 Alternate Mode (aka. TBT). The header supplies definitions for all the Thunderbolt specific VDOs (Vendor Defined Objects) that are described in the USB Type-C Connector specification v2.0, as well as definition for the Thunderbolt 3 Standard ID (SID). There is also a new connector state value for the Thunderbolt 3 Alternate Mode that can be used with the mux drivers. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/r/20200302135353.56659-9-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_tbt.h | 53 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 include/linux/usb/typec_tbt.h (limited to 'include') diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h new file mode 100644 index 000000000000..47c2d501ddce --- /dev/null +++ b/include/linux/usb/typec_tbt.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __USB_TYPEC_TBT_H +#define __USB_TYPEC_TBT_H + +#include + +#define USB_TYPEC_VENDOR_INTEL 0x8087 +/* Alias for convenience */ +#define USB_TYPEC_TBT_SID USB_TYPEC_VENDOR_INTEL + +/* Connector state for Thunderbolt3 */ +#define TYPEC_TBT_MODE TYPEC_STATE_MODAL + +/** + * struct typec_thunderbolt_data - Thundebolt3 Alt Mode specific data + * @device_mode: Device Discover Mode VDO + * @cable_mode: Cable Discover Mode VDO + * @enter_vdo: Enter Mode VDO + */ +struct typec_thunderbolt_data { + u32 device_mode; + u32 cable_mode; + u32 enter_vdo; +}; + +/* TBT3 Device Discover Mode VDO bits */ +#define TBT_MODE BIT(0) +#define TBT_ADAPTER(_vdo_) (((_vdo_) & BIT(16)) >> 16) +#define TBT_ADAPTER_LEGACY 0 +#define TBT_ADAPTER_TBT3 1 +#define TBT_INTEL_SPECIFIC_B0 BIT(26) +#define TBT_VENDOR_SPECIFIC_B0 BIT(30) +#define TBT_VENDOR_SPECIFIC_B1 BIT(31) + +#define TBT_SET_ADAPTER(a) (((a) & 1) << 16) + +/* TBT3 Cable Discover Mode VDO bits */ +#define TBT_CABLE_SPEED(_vdo_) (((_vdo_) & GENMASK(18, 16)) >> 16) +#define TBT_CABLE_USB3_GEN1 1 +#define TBT_CABLE_USB3_PASSIVE 2 +#define TBT_CABLE_10_AND_20GBPS 3 +#define TBT_CABLE_ROUNDED BIT(19) +#define TBT_CABLE_OPTICAL BIT(21) +#define TBT_CABLE_RETIMER BIT(22) +#define TBT_CABLE_LINK_TRAINING BIT(23) + +#define TBT_SET_CABLE_SPEED(_s_) (((_s_) & GENMASK(2, 0)) << 16) + +/* TBT3 Device Enter Mode VDO bits */ +#define TBT_ENTER_MODE_CABLE_SPEED(s) TBT_SET_CABLE_SPEED(s) +#define TBT_ENTER_MODE_ACTIVE_CABLE BIT(24) + +#endif /* __USB_TYPEC_TBT_H */ -- cgit v1.2.3 From 4d0dd3802ee1b6b14b7c46621cd581eb7c1ade79 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Thu, 27 Feb 2020 16:28:33 -0600 Subject: dt-bindings: bus: ti-sysc: Add support for PRUSS SYSC type The PRUSS module has a SYSCFG which is unique. The SYSCFG has two additional unique fields called STANDBY_INIT and SUB_MWAIT in addition to regular IDLE_MODE and STANDBY_MODE fields. Add the bindings for this new sysc type. Cc: Rob Herring Signed-off-by: Roger Quadros Signed-off-by: Suman Anna Acked-by: Rob Herring Signed-off-by: Tony Lindgren --- Documentation/devicetree/bindings/bus/ti-sysc.txt | 1 + include/dt-bindings/bus/ti-sysc.h | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/bus/ti-sysc.txt b/Documentation/devicetree/bindings/bus/ti-sysc.txt index 233eb8294204..c984143d08d2 100644 --- a/Documentation/devicetree/bindings/bus/ti-sysc.txt +++ b/Documentation/devicetree/bindings/bus/ti-sysc.txt @@ -38,6 +38,7 @@ Required standard properties: "ti,sysc-dra7-mcasp" "ti,sysc-usb-host-fs" "ti,sysc-dra7-mcan" + "ti,sysc-pruss" - reg shall have register areas implemented for the interconnect target module in question such as revision, sysc and syss diff --git a/include/dt-bindings/bus/ti-sysc.h b/include/dt-bindings/bus/ti-sysc.h index babd08a1d226..76b07826ed05 100644 --- a/include/dt-bindings/bus/ti-sysc.h +++ b/include/dt-bindings/bus/ti-sysc.h @@ -18,6 +18,10 @@ #define SYSC_DRA7_MCAN_ENAWAKEUP (1 << 4) +/* PRUSS sysc found on AM33xx/AM43xx/AM57xx */ +#define SYSC_PRUSS_SUB_MWAIT (1 << 5) +#define SYSC_PRUSS_STANDBY_INIT (1 << 4) + /* SYSCONFIG STANDBYMODE/MIDLEMODE/SIDLEMODE supported by hardware */ #define SYSC_IDLE_FORCE 0 #define SYSC_IDLE_NO 1 -- cgit v1.2.3 From b2745d92bb015cc4454d4195c4ce6e2852db397e Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Thu, 27 Feb 2020 16:28:34 -0600 Subject: bus: ti-sysc: Add support for PRUSS SYSC type The PRU-ICSS present on AM33xx/AM43xx/AM57xx has a very unique SYSCFG register. The register follows the OMAP4-style SYSC_TYPE3 for Master Standby and Slave Idle, but also has two additional unique fields - STANDBY_INIT and SUB_MWAIT. The STANDBY_INIT is a control bit that is used to initiate a Standby sequence (when set) and trigger a MStandby request to the SoC's PRCM module. This same bit is also used to enable the OCP master ports (when cleared) to allow the PRU cores to access any peripherals or memory beyond the PRU subsystem. The SUB_MWAIT is a ready status field for the external access. Add support for this SYSC type. The STANDBY_INIT has to be set during suspend, without which it results in a hang in the resume sequence on AM33xx/AM43xx boards and requires a board reset to come out of the hang. Any PRU applications requiring external access are supposed to clear the STANDBY_INIT bit. Note that the PRUSS context is lost during a suspend sequence because the PRUSS module is reset and/or disabled. Signed-off-by: Suman Anna Signed-off-by: Roger Quadros [tony@atomide.com: updated quirk define number and to use -ENODEV] Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 27 +++++++++++++++++++++++++++ include/linux/platform_data/ti-sysc.h | 2 ++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index bc1c52f87046..86ac61fa5bc6 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -1341,6 +1341,9 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, SYSC_MODULE_QUIRK_WDT), + /* PRUSS on am3, am4 and am5 */ + SYSC_QUIRK("pruss", 0, 0x26000, 0x26004, -ENODEV, 0x47000000, 0xff000000, + SYSC_MODULE_QUIRK_PRUSS), /* Watchdog on am3 and am4 */ SYSC_QUIRK("wdt", 0x44e35000, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, SYSC_MODULE_QUIRK_WDT | SYSC_QUIRK_SWSUP_SIDLE), @@ -1712,6 +1715,16 @@ static void sysc_reset_done_quirk_wdt(struct sysc *ddata) dev_warn(ddata->dev, "wdt disable step2 failed\n"); } +/* PRUSS needs to set MSTANDBY_INIT inorder to idle properly */ +static void sysc_module_disable_quirk_pruss(struct sysc *ddata) +{ + u32 reg; + + reg = sysc_read(ddata, ddata->offsets[SYSC_SYSCONFIG]); + reg |= SYSC_PRUSS_STANDBY_INIT; + sysc_write(ddata, ddata->offsets[SYSC_SYSCONFIG], reg); +} + static void sysc_init_module_quirks(struct sysc *ddata) { if (ddata->legacy_mode || !ddata->name) @@ -1750,6 +1763,9 @@ static void sysc_init_module_quirks(struct sysc *ddata) ddata->reset_done_quirk = sysc_reset_done_quirk_wdt; ddata->module_disable_quirk = sysc_reset_done_quirk_wdt; } + + if (ddata->cfg.quirks & SYSC_MODULE_QUIRK_PRUSS) + ddata->module_disable_quirk = sysc_module_disable_quirk_pruss; } static int sysc_clockdomain_init(struct sysc *ddata) @@ -2555,6 +2571,16 @@ static const struct sysc_capabilities sysc_dra7_mcan = { .mod_quirks = SYSS_QUIRK_RESETDONE_INVERTED, }; +/* + * PRUSS found on some AM33xx, AM437x and AM57xx SoCs + */ +static const struct sysc_capabilities sysc_pruss = { + .type = TI_SYSC_PRUSS, + .sysc_mask = SYSC_PRUSS_STANDBY_INIT | SYSC_PRUSS_SUB_MWAIT, + .regbits = &sysc_regbits_omap4_simple, + .mod_quirks = SYSC_MODULE_QUIRK_PRUSS, +}; + static int sysc_init_pdata(struct sysc *ddata) { struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev); @@ -2936,6 +2962,7 @@ static const struct of_device_id sysc_match[] = { { .compatible = "ti,sysc-usb-host-fs", .data = &sysc_omap4_usb_host_fs, }, { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, }, + { .compatible = "ti,sysc-pruss", .data = &sysc_pruss, }, { }, }; MODULE_DEVICE_TABLE(of, sysc_match); diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index ecd3a979a14d..c59999ce044e 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -17,6 +17,7 @@ enum ti_sysc_module_type { TI_SYSC_OMAP4_MCASP, TI_SYSC_OMAP4_USB_HOST_FS, TI_SYSC_DRA7_MCAN, + TI_SYSC_PRUSS, }; struct ti_sysc_cookie { @@ -49,6 +50,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_MODULE_QUIRK_PRUSS BIT(24) #define SYSC_MODULE_QUIRK_DSS_RESET BIT(23) #define SYSC_MODULE_QUIRK_RTC_UNLOCK BIT(22) #define SYSC_QUIRK_CLKDM_NOAUTO BIT(21) -- cgit v1.2.3 From 1aae4bdd787998ea331a56f3db9d8595790fe2f9 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 2 Mar 2020 16:32:31 -0800 Subject: bpf: Switch BPF UAPI #define constants used from BPF program side to enums Switch BPF UAPI constants, previously defined as #define macro, to anonymous enum values. This preserves constants values and behavior in expressions, but has added advantaged of being captured as part of DWARF and, subsequently, BTF type info. Which, in turn, greatly improves usefulness of generated vmlinux.h for BPF applications, as it will not require BPF users to copy/paste various flags and constants, which are frequently used with BPF helpers. Only those constants that are used/useful from BPF program side are converted. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20200303003233.3496043-2-andriin@fb.com --- include/uapi/linux/bpf.h | 175 +++++++++++++++++++++++++--------------- tools/include/uapi/linux/bpf.h | 177 +++++++++++++++++++++++++---------------- 2 files changed, 219 insertions(+), 133 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 180337fae97e..d6b33ea27bcc 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -325,44 +325,46 @@ enum bpf_attach_type { #define BPF_PSEUDO_CALL 1 /* flags for BPF_MAP_UPDATE_ELEM command */ -#define BPF_ANY 0 /* create new element or update existing */ -#define BPF_NOEXIST 1 /* create new element if it didn't exist */ -#define BPF_EXIST 2 /* update existing element */ -#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */ +enum { + BPF_ANY = 0, /* create new element or update existing */ + BPF_NOEXIST = 1, /* create new element if it didn't exist */ + BPF_EXIST = 2, /* update existing element */ + BPF_F_LOCK = 4, /* spin_lock-ed map_lookup/map_update */ +}; /* flags for BPF_MAP_CREATE command */ -#define BPF_F_NO_PREALLOC (1U << 0) +enum { + BPF_F_NO_PREALLOC = (1U << 0), /* Instead of having one common LRU list in the * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list * which can scale and perform better. * Note, the LRU nodes (including free nodes) cannot be moved * across different LRU lists. */ -#define BPF_F_NO_COMMON_LRU (1U << 1) + BPF_F_NO_COMMON_LRU = (1U << 1), /* Specify numa node during map creation */ -#define BPF_F_NUMA_NODE (1U << 2) - -#define BPF_OBJ_NAME_LEN 16U + BPF_F_NUMA_NODE = (1U << 2), /* Flags for accessing BPF object from syscall side. */ -#define BPF_F_RDONLY (1U << 3) -#define BPF_F_WRONLY (1U << 4) + BPF_F_RDONLY = (1U << 3), + BPF_F_WRONLY = (1U << 4), /* Flag for stack_map, store build_id+offset instead of pointer */ -#define BPF_F_STACK_BUILD_ID (1U << 5) + BPF_F_STACK_BUILD_ID = (1U << 5), /* Zero-initialize hash function seed. This should only be used for testing. */ -#define BPF_F_ZERO_SEED (1U << 6) + BPF_F_ZERO_SEED = (1U << 6), /* Flags for accessing BPF object from program side. */ -#define BPF_F_RDONLY_PROG (1U << 7) -#define BPF_F_WRONLY_PROG (1U << 8) + BPF_F_RDONLY_PROG = (1U << 7), + BPF_F_WRONLY_PROG = (1U << 8), /* Clone map from listener for newly accepted socket */ -#define BPF_F_CLONE (1U << 9) + BPF_F_CLONE = (1U << 9), /* Enable memory-mapping BPF map */ -#define BPF_F_MMAPABLE (1U << 10) + BPF_F_MMAPABLE = (1U << 10), +}; /* Flags for BPF_PROG_QUERY. */ @@ -391,6 +393,8 @@ struct bpf_stack_build_id { }; }; +#define BPF_OBJ_NAME_LEN 16U + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -3045,72 +3049,100 @@ enum bpf_func_id { /* All flags used by eBPF helper functions, placed here. */ /* BPF_FUNC_skb_store_bytes flags. */ -#define BPF_F_RECOMPUTE_CSUM (1ULL << 0) -#define BPF_F_INVALIDATE_HASH (1ULL << 1) +enum { + BPF_F_RECOMPUTE_CSUM = (1ULL << 0), + BPF_F_INVALIDATE_HASH = (1ULL << 1), +}; /* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. * First 4 bits are for passing the header field size. */ -#define BPF_F_HDR_FIELD_MASK 0xfULL +enum { + BPF_F_HDR_FIELD_MASK = 0xfULL, +}; /* BPF_FUNC_l4_csum_replace flags. */ -#define BPF_F_PSEUDO_HDR (1ULL << 4) -#define BPF_F_MARK_MANGLED_0 (1ULL << 5) -#define BPF_F_MARK_ENFORCE (1ULL << 6) +enum { + BPF_F_PSEUDO_HDR = (1ULL << 4), + BPF_F_MARK_MANGLED_0 = (1ULL << 5), + BPF_F_MARK_ENFORCE = (1ULL << 6), +}; /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ -#define BPF_F_INGRESS (1ULL << 0) +enum { + BPF_F_INGRESS = (1ULL << 0), +}; /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ -#define BPF_F_TUNINFO_IPV6 (1ULL << 0) +enum { + BPF_F_TUNINFO_IPV6 = (1ULL << 0), +}; /* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ -#define BPF_F_SKIP_FIELD_MASK 0xffULL -#define BPF_F_USER_STACK (1ULL << 8) +enum { + BPF_F_SKIP_FIELD_MASK = 0xffULL, + BPF_F_USER_STACK = (1ULL << 8), /* flags used by BPF_FUNC_get_stackid only. */ -#define BPF_F_FAST_STACK_CMP (1ULL << 9) -#define BPF_F_REUSE_STACKID (1ULL << 10) + BPF_F_FAST_STACK_CMP = (1ULL << 9), + BPF_F_REUSE_STACKID = (1ULL << 10), /* flags used by BPF_FUNC_get_stack only. */ -#define BPF_F_USER_BUILD_ID (1ULL << 11) + BPF_F_USER_BUILD_ID = (1ULL << 11), +}; /* BPF_FUNC_skb_set_tunnel_key flags. */ -#define BPF_F_ZERO_CSUM_TX (1ULL << 1) -#define BPF_F_DONT_FRAGMENT (1ULL << 2) -#define BPF_F_SEQ_NUMBER (1ULL << 3) +enum { + BPF_F_ZERO_CSUM_TX = (1ULL << 1), + BPF_F_DONT_FRAGMENT = (1ULL << 2), + BPF_F_SEQ_NUMBER = (1ULL << 3), +}; /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and * BPF_FUNC_perf_event_read_value flags. */ -#define BPF_F_INDEX_MASK 0xffffffffULL -#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK +enum { + BPF_F_INDEX_MASK = 0xffffffffULL, + BPF_F_CURRENT_CPU = BPF_F_INDEX_MASK, /* BPF_FUNC_perf_event_output for sk_buff input context. */ -#define BPF_F_CTXLEN_MASK (0xfffffULL << 32) + BPF_F_CTXLEN_MASK = (0xfffffULL << 32), +}; /* Current network namespace */ -#define BPF_F_CURRENT_NETNS (-1L) +enum { + BPF_F_CURRENT_NETNS = (-1L), +}; /* BPF_FUNC_skb_adjust_room flags. */ -#define BPF_F_ADJ_ROOM_FIXED_GSO (1ULL << 0) +enum { + BPF_F_ADJ_ROOM_FIXED_GSO = (1ULL << 0), + BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = (1ULL << 1), + BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = (1ULL << 2), + BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3), + BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4), +}; -#define BPF_ADJ_ROOM_ENCAP_L2_MASK 0xff -#define BPF_ADJ_ROOM_ENCAP_L2_SHIFT 56 +enum { + BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff, + BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56, +}; -#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 (1ULL << 1) -#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 (1ULL << 2) -#define BPF_F_ADJ_ROOM_ENCAP_L4_GRE (1ULL << 3) -#define BPF_F_ADJ_ROOM_ENCAP_L4_UDP (1ULL << 4) #define BPF_F_ADJ_ROOM_ENCAP_L2(len) (((__u64)len & \ BPF_ADJ_ROOM_ENCAP_L2_MASK) \ << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) /* BPF_FUNC_sysctl_get_name flags. */ -#define BPF_F_SYSCTL_BASE_NAME (1ULL << 0) +enum { + BPF_F_SYSCTL_BASE_NAME = (1ULL << 0), +}; /* BPF_FUNC_sk_storage_get flags */ -#define BPF_SK_STORAGE_GET_F_CREATE (1ULL << 0) +enum { + BPF_SK_STORAGE_GET_F_CREATE = (1ULL << 0), +}; /* BPF_FUNC_read_branch_records flags. */ -#define BPF_F_GET_BRANCH_RECORDS_SIZE (1ULL << 0) +enum { + BPF_F_GET_BRANCH_RECORDS_SIZE = (1ULL << 0), +}; /* Mode for BPF_FUNC_skb_adjust_room helper. */ enum bpf_adj_room_mode { @@ -3529,13 +3561,14 @@ struct bpf_sock_ops { }; /* Definitions for bpf_sock_ops_cb_flags */ -#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0) -#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1) -#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2) -#define BPF_SOCK_OPS_RTT_CB_FLAG (1<<3) -#define BPF_SOCK_OPS_ALL_CB_FLAGS 0xF /* Mask of all currently - * supported cb flags - */ +enum { + BPF_SOCK_OPS_RTO_CB_FLAG = (1<<0), + BPF_SOCK_OPS_RETRANS_CB_FLAG = (1<<1), + BPF_SOCK_OPS_STATE_CB_FLAG = (1<<2), + BPF_SOCK_OPS_RTT_CB_FLAG = (1<<3), +/* Mask of all currently supported cb flags */ + BPF_SOCK_OPS_ALL_CB_FLAGS = 0xF, +}; /* List of known BPF sock_ops operators. * New entries can only be added at the end @@ -3614,8 +3647,10 @@ enum { BPF_TCP_MAX_STATES /* Leave at the end! */ }; -#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ -#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ +enum { + TCP_BPF_IW = 1001, /* Set TCP initial congestion window */ + TCP_BPF_SNDCWND_CLAMP = 1002, /* Set sndcwnd_clamp */ +}; struct bpf_perf_event_value { __u64 counter; @@ -3623,12 +3658,16 @@ struct bpf_perf_event_value { __u64 running; }; -#define BPF_DEVCG_ACC_MKNOD (1ULL << 0) -#define BPF_DEVCG_ACC_READ (1ULL << 1) -#define BPF_DEVCG_ACC_WRITE (1ULL << 2) +enum { + BPF_DEVCG_ACC_MKNOD = (1ULL << 0), + BPF_DEVCG_ACC_READ = (1ULL << 1), + BPF_DEVCG_ACC_WRITE = (1ULL << 2), +}; -#define BPF_DEVCG_DEV_BLOCK (1ULL << 0) -#define BPF_DEVCG_DEV_CHAR (1ULL << 1) +enum { + BPF_DEVCG_DEV_BLOCK = (1ULL << 0), + BPF_DEVCG_DEV_CHAR = (1ULL << 1), +}; struct bpf_cgroup_dev_ctx { /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */ @@ -3644,8 +3683,10 @@ struct bpf_raw_tracepoint_args { /* DIRECT: Skip the FIB rules and go to FIB table associated with device * OUTPUT: Do lookup from egress perspective; default is ingress */ -#define BPF_FIB_LOOKUP_DIRECT (1U << 0) -#define BPF_FIB_LOOKUP_OUTPUT (1U << 1) +enum { + BPF_FIB_LOOKUP_DIRECT = (1U << 0), + BPF_FIB_LOOKUP_OUTPUT = (1U << 1), +}; enum { BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ @@ -3717,9 +3758,11 @@ enum bpf_task_fd_type { BPF_FD_TYPE_URETPROBE, /* filename + offset */ }; -#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0) -#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1) -#define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP (1U << 2) +enum { + BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = (1U << 0), + BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = (1U << 1), + BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = (1U << 2), +}; struct bpf_flow_keys { __u16 nhoff; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 7c689f4552dd..d6b33ea27bcc 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -73,7 +73,7 @@ struct bpf_insn { /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ - __u8 data[0]; /* Arbitrary size */ + __u8 data[]; /* Arbitrary size */ }; struct bpf_cgroup_storage_key { @@ -325,44 +325,46 @@ enum bpf_attach_type { #define BPF_PSEUDO_CALL 1 /* flags for BPF_MAP_UPDATE_ELEM command */ -#define BPF_ANY 0 /* create new element or update existing */ -#define BPF_NOEXIST 1 /* create new element if it didn't exist */ -#define BPF_EXIST 2 /* update existing element */ -#define BPF_F_LOCK 4 /* spin_lock-ed map_lookup/map_update */ +enum { + BPF_ANY = 0, /* create new element or update existing */ + BPF_NOEXIST = 1, /* create new element if it didn't exist */ + BPF_EXIST = 2, /* update existing element */ + BPF_F_LOCK = 4, /* spin_lock-ed map_lookup/map_update */ +}; /* flags for BPF_MAP_CREATE command */ -#define BPF_F_NO_PREALLOC (1U << 0) +enum { + BPF_F_NO_PREALLOC = (1U << 0), /* Instead of having one common LRU list in the * BPF_MAP_TYPE_LRU_[PERCPU_]HASH map, use a percpu LRU list * which can scale and perform better. * Note, the LRU nodes (including free nodes) cannot be moved * across different LRU lists. */ -#define BPF_F_NO_COMMON_LRU (1U << 1) + BPF_F_NO_COMMON_LRU = (1U << 1), /* Specify numa node during map creation */ -#define BPF_F_NUMA_NODE (1U << 2) - -#define BPF_OBJ_NAME_LEN 16U + BPF_F_NUMA_NODE = (1U << 2), /* Flags for accessing BPF object from syscall side. */ -#define BPF_F_RDONLY (1U << 3) -#define BPF_F_WRONLY (1U << 4) + BPF_F_RDONLY = (1U << 3), + BPF_F_WRONLY = (1U << 4), /* Flag for stack_map, store build_id+offset instead of pointer */ -#define BPF_F_STACK_BUILD_ID (1U << 5) + BPF_F_STACK_BUILD_ID = (1U << 5), /* Zero-initialize hash function seed. This should only be used for testing. */ -#define BPF_F_ZERO_SEED (1U << 6) + BPF_F_ZERO_SEED = (1U << 6), /* Flags for accessing BPF object from program side. */ -#define BPF_F_RDONLY_PROG (1U << 7) -#define BPF_F_WRONLY_PROG (1U << 8) + BPF_F_RDONLY_PROG = (1U << 7), + BPF_F_WRONLY_PROG = (1U << 8), /* Clone map from listener for newly accepted socket */ -#define BPF_F_CLONE (1U << 9) + BPF_F_CLONE = (1U << 9), /* Enable memory-mapping BPF map */ -#define BPF_F_MMAPABLE (1U << 10) + BPF_F_MMAPABLE = (1U << 10), +}; /* Flags for BPF_PROG_QUERY. */ @@ -391,6 +393,8 @@ struct bpf_stack_build_id { }; }; +#define BPF_OBJ_NAME_LEN 16U + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -3045,72 +3049,100 @@ enum bpf_func_id { /* All flags used by eBPF helper functions, placed here. */ /* BPF_FUNC_skb_store_bytes flags. */ -#define BPF_F_RECOMPUTE_CSUM (1ULL << 0) -#define BPF_F_INVALIDATE_HASH (1ULL << 1) +enum { + BPF_F_RECOMPUTE_CSUM = (1ULL << 0), + BPF_F_INVALIDATE_HASH = (1ULL << 1), +}; /* BPF_FUNC_l3_csum_replace and BPF_FUNC_l4_csum_replace flags. * First 4 bits are for passing the header field size. */ -#define BPF_F_HDR_FIELD_MASK 0xfULL +enum { + BPF_F_HDR_FIELD_MASK = 0xfULL, +}; /* BPF_FUNC_l4_csum_replace flags. */ -#define BPF_F_PSEUDO_HDR (1ULL << 4) -#define BPF_F_MARK_MANGLED_0 (1ULL << 5) -#define BPF_F_MARK_ENFORCE (1ULL << 6) +enum { + BPF_F_PSEUDO_HDR = (1ULL << 4), + BPF_F_MARK_MANGLED_0 = (1ULL << 5), + BPF_F_MARK_ENFORCE = (1ULL << 6), +}; /* BPF_FUNC_clone_redirect and BPF_FUNC_redirect flags. */ -#define BPF_F_INGRESS (1ULL << 0) +enum { + BPF_F_INGRESS = (1ULL << 0), +}; /* BPF_FUNC_skb_set_tunnel_key and BPF_FUNC_skb_get_tunnel_key flags. */ -#define BPF_F_TUNINFO_IPV6 (1ULL << 0) +enum { + BPF_F_TUNINFO_IPV6 = (1ULL << 0), +}; /* flags for both BPF_FUNC_get_stackid and BPF_FUNC_get_stack. */ -#define BPF_F_SKIP_FIELD_MASK 0xffULL -#define BPF_F_USER_STACK (1ULL << 8) +enum { + BPF_F_SKIP_FIELD_MASK = 0xffULL, + BPF_F_USER_STACK = (1ULL << 8), /* flags used by BPF_FUNC_get_stackid only. */ -#define BPF_F_FAST_STACK_CMP (1ULL << 9) -#define BPF_F_REUSE_STACKID (1ULL << 10) + BPF_F_FAST_STACK_CMP = (1ULL << 9), + BPF_F_REUSE_STACKID = (1ULL << 10), /* flags used by BPF_FUNC_get_stack only. */ -#define BPF_F_USER_BUILD_ID (1ULL << 11) + BPF_F_USER_BUILD_ID = (1ULL << 11), +}; /* BPF_FUNC_skb_set_tunnel_key flags. */ -#define BPF_F_ZERO_CSUM_TX (1ULL << 1) -#define BPF_F_DONT_FRAGMENT (1ULL << 2) -#define BPF_F_SEQ_NUMBER (1ULL << 3) +enum { + BPF_F_ZERO_CSUM_TX = (1ULL << 1), + BPF_F_DONT_FRAGMENT = (1ULL << 2), + BPF_F_SEQ_NUMBER = (1ULL << 3), +}; /* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and * BPF_FUNC_perf_event_read_value flags. */ -#define BPF_F_INDEX_MASK 0xffffffffULL -#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK +enum { + BPF_F_INDEX_MASK = 0xffffffffULL, + BPF_F_CURRENT_CPU = BPF_F_INDEX_MASK, /* BPF_FUNC_perf_event_output for sk_buff input context. */ -#define BPF_F_CTXLEN_MASK (0xfffffULL << 32) + BPF_F_CTXLEN_MASK = (0xfffffULL << 32), +}; /* Current network namespace */ -#define BPF_F_CURRENT_NETNS (-1L) +enum { + BPF_F_CURRENT_NETNS = (-1L), +}; /* BPF_FUNC_skb_adjust_room flags. */ -#define BPF_F_ADJ_ROOM_FIXED_GSO (1ULL << 0) +enum { + BPF_F_ADJ_ROOM_FIXED_GSO = (1ULL << 0), + BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = (1ULL << 1), + BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = (1ULL << 2), + BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3), + BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4), +}; -#define BPF_ADJ_ROOM_ENCAP_L2_MASK 0xff -#define BPF_ADJ_ROOM_ENCAP_L2_SHIFT 56 +enum { + BPF_ADJ_ROOM_ENCAP_L2_MASK = 0xff, + BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56, +}; -#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 (1ULL << 1) -#define BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 (1ULL << 2) -#define BPF_F_ADJ_ROOM_ENCAP_L4_GRE (1ULL << 3) -#define BPF_F_ADJ_ROOM_ENCAP_L4_UDP (1ULL << 4) #define BPF_F_ADJ_ROOM_ENCAP_L2(len) (((__u64)len & \ BPF_ADJ_ROOM_ENCAP_L2_MASK) \ << BPF_ADJ_ROOM_ENCAP_L2_SHIFT) /* BPF_FUNC_sysctl_get_name flags. */ -#define BPF_F_SYSCTL_BASE_NAME (1ULL << 0) +enum { + BPF_F_SYSCTL_BASE_NAME = (1ULL << 0), +}; /* BPF_FUNC_sk_storage_get flags */ -#define BPF_SK_STORAGE_GET_F_CREATE (1ULL << 0) +enum { + BPF_SK_STORAGE_GET_F_CREATE = (1ULL << 0), +}; /* BPF_FUNC_read_branch_records flags. */ -#define BPF_F_GET_BRANCH_RECORDS_SIZE (1ULL << 0) +enum { + BPF_F_GET_BRANCH_RECORDS_SIZE = (1ULL << 0), +}; /* Mode for BPF_FUNC_skb_adjust_room helper. */ enum bpf_adj_room_mode { @@ -3529,13 +3561,14 @@ struct bpf_sock_ops { }; /* Definitions for bpf_sock_ops_cb_flags */ -#define BPF_SOCK_OPS_RTO_CB_FLAG (1<<0) -#define BPF_SOCK_OPS_RETRANS_CB_FLAG (1<<1) -#define BPF_SOCK_OPS_STATE_CB_FLAG (1<<2) -#define BPF_SOCK_OPS_RTT_CB_FLAG (1<<3) -#define BPF_SOCK_OPS_ALL_CB_FLAGS 0xF /* Mask of all currently - * supported cb flags - */ +enum { + BPF_SOCK_OPS_RTO_CB_FLAG = (1<<0), + BPF_SOCK_OPS_RETRANS_CB_FLAG = (1<<1), + BPF_SOCK_OPS_STATE_CB_FLAG = (1<<2), + BPF_SOCK_OPS_RTT_CB_FLAG = (1<<3), +/* Mask of all currently supported cb flags */ + BPF_SOCK_OPS_ALL_CB_FLAGS = 0xF, +}; /* List of known BPF sock_ops operators. * New entries can only be added at the end @@ -3614,8 +3647,10 @@ enum { BPF_TCP_MAX_STATES /* Leave at the end! */ }; -#define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ -#define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ +enum { + TCP_BPF_IW = 1001, /* Set TCP initial congestion window */ + TCP_BPF_SNDCWND_CLAMP = 1002, /* Set sndcwnd_clamp */ +}; struct bpf_perf_event_value { __u64 counter; @@ -3623,12 +3658,16 @@ struct bpf_perf_event_value { __u64 running; }; -#define BPF_DEVCG_ACC_MKNOD (1ULL << 0) -#define BPF_DEVCG_ACC_READ (1ULL << 1) -#define BPF_DEVCG_ACC_WRITE (1ULL << 2) +enum { + BPF_DEVCG_ACC_MKNOD = (1ULL << 0), + BPF_DEVCG_ACC_READ = (1ULL << 1), + BPF_DEVCG_ACC_WRITE = (1ULL << 2), +}; -#define BPF_DEVCG_DEV_BLOCK (1ULL << 0) -#define BPF_DEVCG_DEV_CHAR (1ULL << 1) +enum { + BPF_DEVCG_DEV_BLOCK = (1ULL << 0), + BPF_DEVCG_DEV_CHAR = (1ULL << 1), +}; struct bpf_cgroup_dev_ctx { /* access_type encoded as (BPF_DEVCG_ACC_* << 16) | BPF_DEVCG_DEV_* */ @@ -3644,8 +3683,10 @@ struct bpf_raw_tracepoint_args { /* DIRECT: Skip the FIB rules and go to FIB table associated with device * OUTPUT: Do lookup from egress perspective; default is ingress */ -#define BPF_FIB_LOOKUP_DIRECT (1U << 0) -#define BPF_FIB_LOOKUP_OUTPUT (1U << 1) +enum { + BPF_FIB_LOOKUP_DIRECT = (1U << 0), + BPF_FIB_LOOKUP_OUTPUT = (1U << 1), +}; enum { BPF_FIB_LKUP_RET_SUCCESS, /* lookup successful */ @@ -3717,9 +3758,11 @@ enum bpf_task_fd_type { BPF_FD_TYPE_URETPROBE, /* filename + offset */ }; -#define BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG (1U << 0) -#define BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL (1U << 1) -#define BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP (1U << 2) +enum { + BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = (1U << 0), + BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = (1U << 1), + BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = (1U << 2), +}; struct bpf_flow_keys { __u16 nhoff; -- cgit v1.2.3 From 2333e829952fb437db915bbb17f4d8c43127d438 Mon Sep 17 00:00:00 2001 From: Yu Chen Date: Sun, 23 Feb 2020 15:28:52 +0800 Subject: workqueue: Make workqueue_init*() return void The return values of workqueue_init() and workqueue_early_int() are always 0, and there is no usage of their return value. So just make them return void. Signed-off-by: Yu Chen Signed-off-by: Tejun Heo --- include/linux/workqueue.h | 4 ++-- kernel/workqueue.c | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/workqueue.h b/include/linux/workqueue.h index 4261d1c6e87b..c86a7691e13c 100644 --- a/include/linux/workqueue.h +++ b/include/linux/workqueue.h @@ -649,7 +649,7 @@ int workqueue_online_cpu(unsigned int cpu); int workqueue_offline_cpu(unsigned int cpu); #endif -int __init workqueue_init_early(void); -int __init workqueue_init(void); +void __init workqueue_init_early(void); +void __init workqueue_init(void); #endif diff --git a/kernel/workqueue.c b/kernel/workqueue.c index 301db4406bc3..5afa9ad45eba 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -5896,7 +5896,7 @@ static void __init wq_numa_init(void) * items. Actual work item execution starts only after kthreads can be * created and scheduled right before early initcalls. */ -int __init workqueue_init_early(void) +void __init workqueue_init_early(void) { int std_nice[NR_STD_WORKER_POOLS] = { 0, HIGHPRI_NICE_LEVEL }; int hk_flags = HK_FLAG_DOMAIN | HK_FLAG_WQ; @@ -5963,8 +5963,6 @@ int __init workqueue_init_early(void) !system_unbound_wq || !system_freezable_wq || !system_power_efficient_wq || !system_freezable_power_efficient_wq); - - return 0; } /** @@ -5976,7 +5974,7 @@ int __init workqueue_init_early(void) * are no kworkers executing the work items yet. Populate the worker pools * with the initial workers and enable future kworker creations. */ -int __init workqueue_init(void) +void __init workqueue_init(void) { struct workqueue_struct *wq; struct worker_pool *pool; @@ -6023,6 +6021,4 @@ int __init workqueue_init(void) wq_online = true; wq_watchdog_init(); - - return 0; } -- cgit v1.2.3 From 8375e74f2bca9802a4ddf431a6d7bd2ab9950f27 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 21 Feb 2020 17:40:35 -0800 Subject: driver core: Add fw_devlink kernel commandline option fwnode_operations.add_links allows creating device links from information provided by firmware. fwnode_operations.add_links is currently implemented only by OF/devicetree code and a specific case of efi. However, there's nothing preventing ACPI or other firmware types from implementing it. The OF implementation is currently controlled by a kernel commandline parameter called of_devlink. Since this feature is generic isn't limited to OF, add a generic fw_devlink kernel commandline parameter to control this feature across firmware types. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20200222014038.180923-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/kernel-parameters.txt | 18 +++++++++++++++++ drivers/base/core.c | 27 ++++++++++++++++++++++++- include/linux/fwnode.h | 2 ++ 3 files changed, 46 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index dbc22d684627..29985152b66d 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1350,6 +1350,24 @@ can be changed at run time by the max_graph_depth file in the tracefs tracing directory. default: 0 (no limit) + fw_devlink= [KNL] Create device links between consumer and supplier + devices by scanning the firmware to infer the + consumer/supplier relationships. This feature is + especially useful when drivers are loaded as modules as + it ensures proper ordering of tasks like device probing + (suppliers first, then consumers), supplier boot state + clean up (only after all consumers have probed), + suspend/resume & runtime PM (consumers first, then + suppliers). + Format: { off | permissive | on | rpm } + off -- Don't create device links from firmware info. + permissive -- Create device links from firmware info + but use it only for ordering boot state clean + up (sync_state() calls). + on -- Create device links from firmware info and use it + to enforce probe and suspend/resume ordering. + rpm -- Like "on", but also use to order runtime PM. + gamecon.map[2|3]= [HW,JOY] Multisystem joystick and NES/SNES/PSX pad support via parallel port (up to 5 devices per port) diff --git a/drivers/base/core.c b/drivers/base/core.c index 79863354c801..922680af9051 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2332,6 +2332,31 @@ static int device_private_init(struct device *dev) return 0; } +u32 fw_devlink_flags; +static int __init fw_devlink_setup(char *arg) +{ + if (!arg) + return -EINVAL; + + if (strcmp(arg, "off") == 0) { + fw_devlink_flags = 0; + } else if (strcmp(arg, "permissive") == 0) { + fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY; + } else if (strcmp(arg, "on") == 0) { + fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER; + } else if (strcmp(arg, "rpm") == 0) { + fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER | + DL_FLAG_PM_RUNTIME; + } + return 0; +} +early_param("fw_devlink", fw_devlink_setup); + +u32 fw_devlink_get_flags(void) +{ + return fw_devlink_flags; +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -2480,7 +2505,7 @@ int device_add(struct device *dev) */ device_link_add_missing_supplier_links(); - if (fwnode_has_op(dev->fwnode, add_links)) { + if (fw_devlink_flags && fwnode_has_op(dev->fwnode, add_links)) { fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); if (fw_ret == -ENODEV) device_link_wait_for_mandatory_supplier(dev); diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 8feeb94b8acc..e0abafbb17f8 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -170,4 +170,6 @@ struct fwnode_operations { } while (false) #define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) +extern u32 fw_devlink_get_flags(void); + #endif -- cgit v1.2.3 From 0e9f8d09d2806aa2e10a04a78f82544e4ee737a1 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 25 Feb 2020 05:08:26 +0000 Subject: driver core: Remove driver_deferred_probe_check_state_continue() Now that driver_deferred_probe_check_state() works better, and we've converted the only user of driver_deferred_probe_check_state_continue() we can simply remove it and simplify some of the logic. Cc: linux-pm@vger.kernel.org Cc: Greg Kroah-Hartman Cc: Linus Walleij Cc: Thierry Reding Cc: Mark Brown Cc: Liam Girdwood Cc: Bjorn Andersson Cc: Saravana Kannan Cc: Todd Kjos Cc: Len Brown Cc: Pavel Machek Cc: Ulf Hansson Cc: Kevin Hilman Cc: "Rafael J. Wysocki" Cc: Rob Herring Reviewed-by: Bjorn Andersson Reviewed-by: Rafael J. Wysocki Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200225050828.56458-5-john.stultz@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 53 +++++++------------------------------------ include/linux/device/driver.h | 1 - 2 files changed, 8 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index fe26f2574a6d..c09e4e7277d4 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -244,19 +244,6 @@ static int __init deferred_probe_timeout_setup(char *str) } __setup("deferred_probe_timeout=", deferred_probe_timeout_setup); -static int __driver_deferred_probe_check_state(struct device *dev) -{ - if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) - return -ENODEV; - - if (!deferred_probe_timeout) { - dev_WARN(dev, "deferred probe timeout, ignoring dependency"); - return -ETIMEDOUT; - } - - return -EPROBE_DEFER; -} - /** * driver_deferred_probe_check_state() - Check deferred probe state * @dev: device to check @@ -272,39 +259,15 @@ static int __driver_deferred_probe_check_state(struct device *dev) */ int driver_deferred_probe_check_state(struct device *dev) { - int ret; - - ret = __driver_deferred_probe_check_state(dev); - if (ret != -ENODEV) - return ret; - - dev_warn(dev, "ignoring dependency for device, assuming no driver"); - - return -ENODEV; -} - -/** - * driver_deferred_probe_check_state_continue() - check deferred probe state - * @dev: device to check - * - * Returns -ETIMEDOUT if deferred probe debug timeout has expired, or - * -EPROBE_DEFER otherwise. - * - * Drivers or subsystems can opt-in to calling this function instead of - * directly returning -EPROBE_DEFER. - * - * This is similar to driver_deferred_probe_check_state(), but it allows the - * subsystem to keep deferring probe after built-in drivers have had a chance - * to probe. One scenario where that is useful is if built-in drivers rely on - * resources that are provided by modular drivers. - */ -int driver_deferred_probe_check_state_continue(struct device *dev) -{ - int ret; + if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) { + dev_warn(dev, "ignoring dependency for device, assuming no driver"); + return -ENODEV; + } - ret = __driver_deferred_probe_check_state(dev); - if (ret != -ENODEV) - return ret; + if (!deferred_probe_timeout) { + dev_WARN(dev, "deferred probe timeout, ignoring dependency"); + return -ETIMEDOUT; + } return -EPROBE_DEFER; } diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 1188260f9a02..5242afabfaba 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -238,7 +238,6 @@ driver_find_device_by_acpi_dev(struct device_driver *drv, const void *adev) void driver_deferred_probe_add(struct device *dev); int driver_deferred_probe_check_state(struct device *dev); -int driver_deferred_probe_check_state_continue(struct device *dev); void driver_init(void); /** -- cgit v1.2.3 From 64c775fb4b21ab2532555e833edf52055df5fb1c Mon Sep 17 00:00:00 2001 From: John Stultz Date: Tue, 25 Feb 2020 05:08:27 +0000 Subject: driver core: Rename deferred_probe_timeout and make it global Since other subsystems (like regulator) have similar arbitrary timeouts for how long they try to resolve driver dependencies, rename deferred_probe_timeout to driver_deferred_probe_timeout and set it as global, so it can be shared. Cc: linux-pm@vger.kernel.org Cc: Greg Kroah-Hartman Cc: Linus Walleij Cc: Thierry Reding Cc: Mark Brown Cc: Liam Girdwood Cc: Bjorn Andersson Cc: Saravana Kannan Cc: Todd Kjos Cc: Len Brown Cc: Pavel Machek Cc: Ulf Hansson Cc: Kevin Hilman Cc: "Rafael J. Wysocki" Cc: Rob Herring Reviewed-by: Bjorn Andersson Reviewed-by: Rafael J. Wysocki Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200225050828.56458-6-john.stultz@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 16 +++++++++------- include/linux/device/driver.h | 1 + 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index c09e4e7277d4..76888a7459d8 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -229,17 +229,19 @@ DEFINE_SHOW_ATTRIBUTE(deferred_devs); * In the case of modules, set the default probe timeout to * 30 seconds to give userland some time to load needed modules */ -static int deferred_probe_timeout = 30; +int driver_deferred_probe_timeout = 30; #else /* In the case of !modules, no probe timeout needed */ -static int deferred_probe_timeout = -1; +int driver_deferred_probe_timeout = -1; #endif +EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout); + static int __init deferred_probe_timeout_setup(char *str) { int timeout; if (!kstrtoint(str, 10, &timeout)) - deferred_probe_timeout = timeout; + driver_deferred_probe_timeout = timeout; return 1; } __setup("deferred_probe_timeout=", deferred_probe_timeout_setup); @@ -264,7 +266,7 @@ int driver_deferred_probe_check_state(struct device *dev) return -ENODEV; } - if (!deferred_probe_timeout) { + if (!driver_deferred_probe_timeout) { dev_WARN(dev, "deferred probe timeout, ignoring dependency"); return -ETIMEDOUT; } @@ -276,7 +278,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) { struct device_private *private, *p; - deferred_probe_timeout = 0; + driver_deferred_probe_timeout = 0; driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); @@ -310,9 +312,9 @@ static int deferred_probe_initcall(void) driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); - if (deferred_probe_timeout > 0) { + if (driver_deferred_probe_timeout > 0) { schedule_delayed_work(&deferred_probe_timeout_work, - deferred_probe_timeout * HZ); + driver_deferred_probe_timeout * HZ); } return 0; } diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 5242afabfaba..ee7ba5b5417e 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -236,6 +236,7 @@ driver_find_device_by_acpi_dev(struct device_driver *drv, const void *adev) } #endif +extern int driver_deferred_probe_timeout; void driver_deferred_probe_add(struct device *dev); int driver_deferred_probe_check_state(struct device *dev); void driver_init(void); -- cgit v1.2.3 From 90baeb9dd2656dd9fa0a66f338f22236be96e69f Mon Sep 17 00:00:00 2001 From: Leslie Monis Date: Thu, 5 Mar 2020 00:25:59 +0530 Subject: pie: use term backlog instead of qlen Remove ambiguity by using the term backlog instead of qlen when representing the queue length in bytes. Signed-off-by: Mohit P. Tahiliani Signed-off-by: Gautam Ramakrishnan Signed-off-by: Leslie Monis Signed-off-by: David S. Miller --- include/net/pie.h | 10 +++++----- net/sched/sch_pie.c | 22 +++++++++++----------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/pie.h b/include/net/pie.h index fd5a37cb7993..24f68c1e9919 100644 --- a/include/net/pie.h +++ b/include/net/pie.h @@ -46,7 +46,7 @@ struct pie_params { * @accu_prob: accumulated drop probability * @dq_count: number of bytes dequeued in a measurement cycle * @avg_dq_rate: calculated average dq rate - * @qlen_old: queue length during previous qdelay calculation + * @backlog_old: queue backlog during previous qdelay calculation * @accu_prob_overflows: number of times accu_prob overflows */ struct pie_vars { @@ -58,7 +58,7 @@ struct pie_vars { u64 accu_prob; u64 dq_count; u32 avg_dq_rate; - u32 qlen_old; + u32 backlog_old; u8 accu_prob_overflows; }; @@ -127,12 +127,12 @@ static inline void pie_set_enqueue_time(struct sk_buff *skb) } bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, - struct pie_vars *vars, u32 qlen, u32 packet_size); + struct pie_vars *vars, u32 backlog, u32 packet_size); void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, - struct pie_vars *vars, u32 qlen); + struct pie_vars *vars, u32 backlog); void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, - u32 qlen); + u32 backlog); #endif diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 915bcdb59a9f..8a2f9f11c86f 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -31,7 +31,7 @@ struct pie_sched_data { }; bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, - struct pie_vars *vars, u32 qlen, u32 packet_size) + struct pie_vars *vars, u32 backlog, u32 packet_size) { u64 rnd; u64 local_prob = vars->prob; @@ -51,7 +51,7 @@ bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, /* If we have fewer than 2 mtu-sized packets, disable pie_drop_early, * similar to min_th in RED */ - if (qlen < 2 * mtu) + if (backlog < 2 * mtu) return false; /* If bytemode is turned on, use packet size to compute new @@ -215,7 +215,7 @@ static int pie_change(struct Qdisc *sch, struct nlattr *opt, } void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, - struct pie_vars *vars, u32 qlen) + struct pie_vars *vars, u32 backlog) { psched_time_t now = psched_get_time(); u32 dtime = 0; @@ -231,7 +231,7 @@ void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, vars->dq_tstamp = now; - if (qlen == 0) + if (backlog == 0) vars->qdelay = 0; if (dtime == 0) @@ -244,7 +244,7 @@ void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, * we have enough packets to calculate the drain rate. Save * current time as dq_tstamp and start measurement cycle. */ - if (qlen >= QUEUE_THRESHOLD && vars->dq_count == DQCOUNT_INVALID) { + if (backlog >= QUEUE_THRESHOLD && vars->dq_count == DQCOUNT_INVALID) { vars->dq_tstamp = psched_get_time(); vars->dq_count = 0; } @@ -283,7 +283,7 @@ void pie_process_dequeue(struct sk_buff *skb, struct pie_params *params, * dq_count to 0 to re-enter the if block when the next * packet is dequeued */ - if (qlen < QUEUE_THRESHOLD) { + if (backlog < QUEUE_THRESHOLD) { vars->dq_count = DQCOUNT_INVALID; } else { vars->dq_count = 0; @@ -307,7 +307,7 @@ burst_allowance_reduction: EXPORT_SYMBOL_GPL(pie_process_dequeue); void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, - u32 qlen) + u32 backlog) { psched_time_t qdelay = 0; /* in pschedtime */ psched_time_t qdelay_old = 0; /* in pschedtime */ @@ -322,7 +322,7 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, vars->qdelay_old = vars->qdelay; if (vars->avg_dq_rate > 0) - qdelay = (qlen << PIE_SCALE) / vars->avg_dq_rate; + qdelay = (backlog << PIE_SCALE) / vars->avg_dq_rate; else qdelay = 0; } else { @@ -330,10 +330,10 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, qdelay_old = vars->qdelay_old; } - /* If qdelay is zero and qlen is not, it means qlen is very small, + /* If qdelay is zero and backlog is not, it means backlog is very small, * so we do not update probabilty in this round. */ - if (qdelay == 0 && qlen != 0) + if (qdelay == 0 && backlog != 0) update_prob = false; /* In the algorithm, alpha and beta are between 0 and 2 with typical @@ -409,7 +409,7 @@ void pie_calculate_probability(struct pie_params *params, struct pie_vars *vars, vars->prob -= vars->prob / 64; vars->qdelay = qdelay; - vars->qlen_old = qlen; + vars->backlog_old = backlog; /* We restart the measurement cycle if the following conditions are met * 1. If the delay has been low for 2 consecutive Tupdate periods -- cgit v1.2.3 From 105e808c1da2a2827a4a374ae6e3003249729eec Mon Sep 17 00:00:00 2001 From: Leslie Monis Date: Thu, 5 Mar 2020 00:26:01 +0530 Subject: pie: remove pie_vars->accu_prob_overflows The variable pie_vars->accu_prob is used as an accumulator for probability values. Since probabilty values are scaled using the MAX_PROB macro denoting (2^64 - 1), pie_vars->accu_prob is likely to overflow as it is of type u64. The variable pie_vars->accu_prob_overflows counts the number of times the variable pie_vars->accu_prob overflows. The MAX_PROB macro needs to be equal to at least (2^39 - 1) in order to do precise calculations without any underflow. Thus MAX_PROB can be reduced to (2^56 - 1) without affecting the precision in calculations drastically. Doing so will eliminate the need for the variable pie_vars->accu_prob_overflows as the variable pie_vars->accu_prob will never overflow. Removing the variable pie_vars->accu_prob_overflows also reduces the size of the structure pie_vars to exactly 64 bytes. Signed-off-by: Mohit P. Tahiliani Signed-off-by: Gautam Ramakrishnan Signed-off-by: Leslie Monis Signed-off-by: David S. Miller --- include/net/pie.h | 5 +---- net/sched/sch_fq_pie.c | 1 - net/sched/sch_pie.c | 21 ++++++--------------- 3 files changed, 7 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/pie.h b/include/net/pie.h index 24f68c1e9919..1c645b76a2ed 100644 --- a/include/net/pie.h +++ b/include/net/pie.h @@ -8,7 +8,7 @@ #include #include -#define MAX_PROB U64_MAX +#define MAX_PROB (U64_MAX >> BITS_PER_BYTE) #define DTIME_INVALID U64_MAX #define QUEUE_THRESHOLD 16384 #define DQCOUNT_INVALID -1 @@ -47,7 +47,6 @@ struct pie_params { * @dq_count: number of bytes dequeued in a measurement cycle * @avg_dq_rate: calculated average dq rate * @backlog_old: queue backlog during previous qdelay calculation - * @accu_prob_overflows: number of times accu_prob overflows */ struct pie_vars { psched_time_t qdelay; @@ -59,7 +58,6 @@ struct pie_vars { u64 dq_count; u32 avg_dq_rate; u32 backlog_old; - u8 accu_prob_overflows; }; /** @@ -107,7 +105,6 @@ static inline void pie_vars_init(struct pie_vars *vars) vars->accu_prob = 0; vars->dq_count = DQCOUNT_INVALID; vars->avg_dq_rate = 0; - vars->accu_prob_overflows = 0; } static inline struct pie_skb_cb *get_pie_cb(const struct sk_buff *skb) diff --git a/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c index 214657eb3dfd..a9da8776bf5b 100644 --- a/net/sched/sch_fq_pie.c +++ b/net/sched/sch_fq_pie.c @@ -189,7 +189,6 @@ static int fq_pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, out: q->stats.dropped++; sel_flow->vars.accu_prob = 0; - sel_flow->vars.accu_prob_overflows = 0; __qdisc_drop(skb, to_free); qdisc_qstats_drop(sch); return NET_XMIT_CN; diff --git a/net/sched/sch_pie.c b/net/sched/sch_pie.c index 198cfa34a00a..f52442d39bf5 100644 --- a/net/sched/sch_pie.c +++ b/net/sched/sch_pie.c @@ -62,27 +62,19 @@ bool pie_drop_early(struct Qdisc *sch, struct pie_params *params, else local_prob = vars->prob; - if (local_prob == 0) { + if (local_prob == 0) vars->accu_prob = 0; - vars->accu_prob_overflows = 0; - } - - if (local_prob > MAX_PROB - vars->accu_prob) - vars->accu_prob_overflows++; - - vars->accu_prob += local_prob; + else + vars->accu_prob += local_prob; - if (vars->accu_prob_overflows == 0 && - vars->accu_prob < (MAX_PROB / 100) * 85) + if (vars->accu_prob < (MAX_PROB / 100) * 85) return false; - if (vars->accu_prob_overflows == 8 && - vars->accu_prob >= MAX_PROB / 2) + if (vars->accu_prob >= (MAX_PROB / 2) * 17) return true; prandom_bytes(&rnd, 8); - if (rnd < local_prob) { + if ((rnd >> BITS_PER_BYTE) < local_prob) { vars->accu_prob = 0; - vars->accu_prob_overflows = 0; return true; } @@ -129,7 +121,6 @@ static int pie_qdisc_enqueue(struct sk_buff *skb, struct Qdisc *sch, out: q->stats.dropped++; q->vars.accu_prob = 0; - q->vars.accu_prob_overflows = 0; return qdisc_drop(skb, sch, to_free); } -- cgit v1.2.3 From 5c5840e4b9684b9c82c068c85f136205dedd9435 Mon Sep 17 00:00:00 2001 From: Leslie Monis Date: Thu, 5 Mar 2020 00:26:02 +0530 Subject: pie: realign comment Realign a comment after the change introduced by the previous patch. Signed-off-by: Leslie Monis Signed-off-by: David S. Miller --- include/net/pie.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/pie.h b/include/net/pie.h index 1c645b76a2ed..3fe2361e03b4 100644 --- a/include/net/pie.h +++ b/include/net/pie.h @@ -38,15 +38,15 @@ struct pie_params { /** * struct pie_vars - contains pie variables - * @qdelay: current queue delay - * @qdelay_old: queue delay in previous qdelay calculation - * @burst_time: burst time allowance - * @dq_tstamp: timestamp at which dq rate was last calculated - * @prob: drop probability - * @accu_prob: accumulated drop probability - * @dq_count: number of bytes dequeued in a measurement cycle - * @avg_dq_rate: calculated average dq rate - * @backlog_old: queue backlog during previous qdelay calculation + * @qdelay: current queue delay + * @qdelay_old: queue delay in previous qdelay calculation + * @burst_time: burst time allowance + * @dq_tstamp: timestamp at which dq rate was last calculated + * @prob: drop probability + * @accu_prob: accumulated drop probability + * @dq_count: number of bytes dequeued in a measurement cycle + * @avg_dq_rate: calculated average dq rate + * @backlog_old: queue backlog during previous qdelay calculation */ struct pie_vars { psched_time_t qdelay; -- cgit v1.2.3 From 88fd9e5352fe05f7fe57778293aebd4cd106960b Mon Sep 17 00:00:00 2001 From: KP Singh Date: Wed, 4 Mar 2020 20:18:47 +0100 Subject: bpf: Refactor trampoline update code As we need to introduce a third type of attachment for trampolines, the flattened signature of arch_prepare_bpf_trampoline gets even more complicated. Refactor the prog and count argument to arch_prepare_bpf_trampoline to use bpf_tramp_progs to simplify the addition and accounting for new attachment types. Signed-off-by: KP Singh Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20200304191853.1529-2-kpsingh@chromium.org --- arch/x86/net/bpf_jit_comp.c | 31 ++++++++++++----------- include/linux/bpf.h | 13 ++++++++-- kernel/bpf/bpf_struct_ops.c | 10 +++++++- kernel/bpf/trampoline.c | 62 +++++++++++++++++++++++++-------------------- 4 files changed, 71 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 9ba08e9abc09..15c7d28bc05c 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1362,12 +1362,12 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args, } static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, - struct bpf_prog **progs, int prog_cnt, int stack_size) + struct bpf_tramp_progs *tp, int stack_size) { u8 *prog = *pprog; int cnt = 0, i; - for (i = 0; i < prog_cnt; i++) { + for (i = 0; i < tp->nr_progs; i++) { if (emit_call(&prog, __bpf_prog_enter, prog)) return -EINVAL; /* remember prog start time returned by __bpf_prog_enter */ @@ -1376,17 +1376,17 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, /* arg1: lea rdi, [rbp - stack_size] */ EMIT4(0x48, 0x8D, 0x7D, -stack_size); /* arg2: progs[i]->insnsi for interpreter */ - if (!progs[i]->jited) + if (!tp->progs[i]->jited) emit_mov_imm64(&prog, BPF_REG_2, - (long) progs[i]->insnsi >> 32, - (u32) (long) progs[i]->insnsi); + (long) tp->progs[i]->insnsi >> 32, + (u32) (long) tp->progs[i]->insnsi); /* call JITed bpf program or interpreter */ - if (emit_call(&prog, progs[i]->bpf_func, prog)) + if (emit_call(&prog, tp->progs[i]->bpf_func, prog)) return -EINVAL; /* arg1: mov rdi, progs[i] */ - emit_mov_imm64(&prog, BPF_REG_1, (long) progs[i] >> 32, - (u32) (long) progs[i]); + emit_mov_imm64(&prog, BPF_REG_1, (long) tp->progs[i] >> 32, + (u32) (long) tp->progs[i]); /* arg2: mov rsi, rbx <- start time in nsec */ emit_mov_reg(&prog, true, BPF_REG_2, BPF_REG_6); if (emit_call(&prog, __bpf_prog_exit, prog)) @@ -1458,12 +1458,13 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, */ int arch_prepare_bpf_trampoline(void *image, void *image_end, const struct btf_func_model *m, u32 flags, - struct bpf_prog **fentry_progs, int fentry_cnt, - struct bpf_prog **fexit_progs, int fexit_cnt, + struct bpf_tramp_progs *tprogs, void *orig_call) { int cnt = 0, nr_args = m->nr_args; int stack_size = nr_args * 8; + struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY]; + struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT]; u8 *prog; /* x86-64 supports up to 6 arguments. 7+ can be added in the future */ @@ -1492,12 +1493,12 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, save_regs(m, &prog, nr_args, stack_size); - if (fentry_cnt) - if (invoke_bpf(m, &prog, fentry_progs, fentry_cnt, stack_size)) + if (fentry->nr_progs) + if (invoke_bpf(m, &prog, fentry, stack_size)) return -EINVAL; if (flags & BPF_TRAMP_F_CALL_ORIG) { - if (fentry_cnt) + if (fentry->nr_progs) restore_regs(m, &prog, nr_args, stack_size); /* call original function */ @@ -1507,8 +1508,8 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); } - if (fexit_cnt) - if (invoke_bpf(m, &prog, fexit_progs, fexit_cnt, stack_size)) + if (fexit->nr_progs) + if (invoke_bpf(m, &prog, fexit, stack_size)) return -EINVAL; if (flags & BPF_TRAMP_F_RESTORE_REGS) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f13c78c6f29d..98ec10b23dbb 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -433,6 +433,16 @@ struct btf_func_model { */ #define BPF_TRAMP_F_SKIP_FRAME BIT(2) +/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50 + * bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2 + */ +#define BPF_MAX_TRAMP_PROGS 40 + +struct bpf_tramp_progs { + struct bpf_prog *progs[BPF_MAX_TRAMP_PROGS]; + int nr_progs; +}; + /* Different use cases for BPF trampoline: * 1. replace nop at the function entry (kprobe equivalent) * flags = BPF_TRAMP_F_RESTORE_REGS @@ -455,8 +465,7 @@ struct btf_func_model { */ int arch_prepare_bpf_trampoline(void *image, void *image_end, const struct btf_func_model *m, u32 flags, - struct bpf_prog **fentry_progs, int fentry_cnt, - struct bpf_prog **fexit_progs, int fexit_cnt, + struct bpf_tramp_progs *tprogs, void *orig_call); /* these two functions are called from generated trampoline */ u64 notrace __bpf_prog_enter(void); diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c index c498f0fffb40..ca5cc8cdb6eb 100644 --- a/kernel/bpf/bpf_struct_ops.c +++ b/kernel/bpf/bpf_struct_ops.c @@ -320,6 +320,7 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, struct bpf_struct_ops_value *uvalue, *kvalue; const struct btf_member *member; const struct btf_type *t = st_ops->type; + struct bpf_tramp_progs *tprogs = NULL; void *udata, *kdata; int prog_fd, err = 0; void *image; @@ -343,6 +344,10 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, if (uvalue->state || refcount_read(&uvalue->refcnt)) return -EINVAL; + tprogs = kcalloc(BPF_TRAMP_MAX, sizeof(*tprogs), GFP_KERNEL); + if (!tprogs) + return -ENOMEM; + uvalue = (struct bpf_struct_ops_value *)st_map->uvalue; kvalue = (struct bpf_struct_ops_value *)&st_map->kvalue; @@ -425,10 +430,12 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key, goto reset_unlock; } + tprogs[BPF_TRAMP_FENTRY].progs[0] = prog; + tprogs[BPF_TRAMP_FENTRY].nr_progs = 1; err = arch_prepare_bpf_trampoline(image, st_map->image + PAGE_SIZE, &st_ops->func_models[i], 0, - &prog, 1, NULL, 0, NULL); + tprogs, NULL); if (err < 0) goto reset_unlock; @@ -469,6 +476,7 @@ reset_unlock: memset(uvalue, 0, map->value_size); memset(kvalue, 0, map->value_size); unlock: + kfree(tprogs); mutex_unlock(&st_map->lock); return err; } diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 704fa787fec0..546198f6f307 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -190,40 +190,49 @@ static int register_fentry(struct bpf_trampoline *tr, void *new_addr) return ret; } -/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50 - * bytes on x86. Pick a number to fit into BPF_IMAGE_SIZE / 2 - */ -#define BPF_MAX_TRAMP_PROGS 40 +static struct bpf_tramp_progs * +bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total) +{ + const struct bpf_prog_aux *aux; + struct bpf_tramp_progs *tprogs; + struct bpf_prog **progs; + int kind; + + *total = 0; + tprogs = kcalloc(BPF_TRAMP_MAX, sizeof(*tprogs), GFP_KERNEL); + if (!tprogs) + return ERR_PTR(-ENOMEM); + + for (kind = 0; kind < BPF_TRAMP_MAX; kind++) { + tprogs[kind].nr_progs = tr->progs_cnt[kind]; + *total += tr->progs_cnt[kind]; + progs = tprogs[kind].progs; + + hlist_for_each_entry(aux, &tr->progs_hlist[kind], tramp_hlist) + *progs++ = aux->prog; + } + return tprogs; +} static int bpf_trampoline_update(struct bpf_trampoline *tr) { void *old_image = tr->image + ((tr->selector + 1) & 1) * BPF_IMAGE_SIZE/2; void *new_image = tr->image + (tr->selector & 1) * BPF_IMAGE_SIZE/2; - struct bpf_prog *progs_to_run[BPF_MAX_TRAMP_PROGS]; - int fentry_cnt = tr->progs_cnt[BPF_TRAMP_FENTRY]; - int fexit_cnt = tr->progs_cnt[BPF_TRAMP_FEXIT]; - struct bpf_prog **progs, **fentry, **fexit; + struct bpf_tramp_progs *tprogs; u32 flags = BPF_TRAMP_F_RESTORE_REGS; - struct bpf_prog_aux *aux; - int err; + int err, total; - if (fentry_cnt + fexit_cnt == 0) { + tprogs = bpf_trampoline_get_progs(tr, &total); + if (IS_ERR(tprogs)) + return PTR_ERR(tprogs); + + if (total == 0) { err = unregister_fentry(tr, old_image); tr->selector = 0; goto out; } - /* populate fentry progs */ - fentry = progs = progs_to_run; - hlist_for_each_entry(aux, &tr->progs_hlist[BPF_TRAMP_FENTRY], tramp_hlist) - *progs++ = aux->prog; - - /* populate fexit progs */ - fexit = progs; - hlist_for_each_entry(aux, &tr->progs_hlist[BPF_TRAMP_FEXIT], tramp_hlist) - *progs++ = aux->prog; - - if (fexit_cnt) + if (tprogs[BPF_TRAMP_FEXIT].nr_progs) flags = BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_SKIP_FRAME; /* Though the second half of trampoline page is unused a task could be @@ -232,12 +241,11 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) * preempted task. Hence wait for tasks to voluntarily schedule or go * to userspace. */ + synchronize_rcu_tasks(); err = arch_prepare_bpf_trampoline(new_image, new_image + BPF_IMAGE_SIZE / 2, - &tr->func.model, flags, - fentry, fentry_cnt, - fexit, fexit_cnt, + &tr->func.model, flags, tprogs, tr->func.addr); if (err < 0) goto out; @@ -252,6 +260,7 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) goto out; tr->selector++; out: + kfree(tprogs); return err; } @@ -409,8 +418,7 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start) int __weak arch_prepare_bpf_trampoline(void *image, void *image_end, const struct btf_func_model *m, u32 flags, - struct bpf_prog **fentry_progs, int fentry_cnt, - struct bpf_prog **fexit_progs, int fexit_cnt, + struct bpf_tramp_progs *tprogs, void *orig_call) { return -ENOTSUPP; -- cgit v1.2.3 From ae24082331d9bbaae283aafbe930a8f0eb85605a Mon Sep 17 00:00:00 2001 From: KP Singh Date: Wed, 4 Mar 2020 20:18:49 +0100 Subject: bpf: Introduce BPF_MODIFY_RETURN When multiple programs are attached, each program receives the return value from the previous program on the stack and the last program provides the return value to the attached function. The fmod_ret bpf programs are run after the fentry programs and before the fexit programs. The original function is only called if all the fmod_ret programs return 0 to avoid any unintended side-effects. The success value, i.e. 0 is not currently configurable but can be made so where user-space can specify it at load time. For example: int func_to_be_attached(int a, int b) { <--- do_fentry do_fmod_ret: if (ret != 0) goto do_fexit; original_function: } <--- do_fexit The fmod_ret program attached to this function can be defined as: SEC("fmod_ret/func_to_be_attached") int BPF_PROG(func_name, int a, int b, int ret) { // This will skip the original function logic. return 1; } The first fmod_ret program is passed 0 in its return argument. Signed-off-by: KP Singh Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20200304191853.1529-4-kpsingh@chromium.org --- arch/x86/net/bpf_jit_comp.c | 130 +++++++++++++++++++++++++++++++++++++---- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 1 + kernel/bpf/btf.c | 3 +- kernel/bpf/syscall.c | 1 + kernel/bpf/trampoline.c | 5 +- kernel/bpf/verifier.c | 1 + tools/include/uapi/linux/bpf.h | 1 + 8 files changed, 130 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index d6349e930b06..b1fd000feb89 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -1362,7 +1362,7 @@ static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args, } static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, - struct bpf_prog *p, int stack_size) + struct bpf_prog *p, int stack_size, bool mod_ret) { u8 *prog = *pprog; int cnt = 0; @@ -1383,6 +1383,13 @@ static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog, if (emit_call(&prog, p->bpf_func, prog)) return -EINVAL; + /* BPF_TRAMP_MODIFY_RETURN trampolines can modify the return + * of the previous call which is then passed on the stack to + * the next BPF program. + */ + if (mod_ret) + emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); + /* arg1: mov rdi, progs[i] */ emit_mov_imm64(&prog, BPF_REG_1, (long) p >> 32, (u32) (long) p); @@ -1442,6 +1449,23 @@ static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond) return 0; } +static int emit_mod_ret_check_imm8(u8 **pprog, int value) +{ + u8 *prog = *pprog; + int cnt = 0; + + if (!is_imm8(value)) + return -EINVAL; + + if (value == 0) + EMIT2(0x85, add_2reg(0xC0, BPF_REG_0, BPF_REG_0)); + else + EMIT3(0x83, add_1reg(0xF8, BPF_REG_0), value); + + *pprog = prog; + return 0; +} + static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, struct bpf_tramp_progs *tp, int stack_size) { @@ -1449,9 +1473,49 @@ static int invoke_bpf(const struct btf_func_model *m, u8 **pprog, u8 *prog = *pprog; for (i = 0; i < tp->nr_progs; i++) { - if (invoke_bpf_prog(m, &prog, tp->progs[i], stack_size)) + if (invoke_bpf_prog(m, &prog, tp->progs[i], stack_size, false)) + return -EINVAL; + } + *pprog = prog; + return 0; +} + +static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog, + struct bpf_tramp_progs *tp, int stack_size, + u8 **branches) +{ + u8 *prog = *pprog; + int i; + + /* The first fmod_ret program will receive a garbage return value. + * Set this to 0 to avoid confusing the program. + */ + emit_mov_imm32(&prog, false, BPF_REG_0, 0); + emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); + for (i = 0; i < tp->nr_progs; i++) { + if (invoke_bpf_prog(m, &prog, tp->progs[i], stack_size, true)) return -EINVAL; + + /* Generate a branch: + * + * if (ret != 0) + * goto do_fexit; + * + * If needed this can be extended to any integer value which can + * be passed by user-space when the program is loaded. + */ + if (emit_mod_ret_check_imm8(&prog, 0)) + return -EINVAL; + + /* Save the location of the branch and Generate 6 nops + * (4 bytes for an offset and 2 bytes for the jump) These nops + * are replaced with a conditional jump once do_fexit (i.e. the + * start of the fexit invocation) is finalized. + */ + branches[i] = prog; + emit_nops(&prog, 4 + 2); } + *pprog = prog; return 0; } @@ -1521,10 +1585,12 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, struct bpf_tramp_progs *tprogs, void *orig_call) { - int cnt = 0, nr_args = m->nr_args; + int ret, i, cnt = 0, nr_args = m->nr_args; int stack_size = nr_args * 8; struct bpf_tramp_progs *fentry = &tprogs[BPF_TRAMP_FENTRY]; struct bpf_tramp_progs *fexit = &tprogs[BPF_TRAMP_FEXIT]; + struct bpf_tramp_progs *fmod_ret = &tprogs[BPF_TRAMP_MODIFY_RETURN]; + u8 **branches = NULL; u8 *prog; /* x86-64 supports up to 6 arguments. 7+ can be added in the future */ @@ -1557,24 +1623,60 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, if (invoke_bpf(m, &prog, fentry, stack_size)) return -EINVAL; + if (fmod_ret->nr_progs) { + branches = kcalloc(fmod_ret->nr_progs, sizeof(u8 *), + GFP_KERNEL); + if (!branches) + return -ENOMEM; + + if (invoke_bpf_mod_ret(m, &prog, fmod_ret, stack_size, + branches)) { + ret = -EINVAL; + goto cleanup; + } + } + if (flags & BPF_TRAMP_F_CALL_ORIG) { - if (fentry->nr_progs) + if (fentry->nr_progs || fmod_ret->nr_progs) restore_regs(m, &prog, nr_args, stack_size); /* call original function */ - if (emit_call(&prog, orig_call, prog)) - return -EINVAL; + if (emit_call(&prog, orig_call, prog)) { + ret = -EINVAL; + goto cleanup; + } /* remember return value in a stack for bpf prog to access */ emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -8); } + if (fmod_ret->nr_progs) { + /* From Intel 64 and IA-32 Architectures Optimization + * Reference Manual, 3.4.1.4 Code Alignment, Assembly/Compiler + * Coding Rule 11: All branch targets should be 16-byte + * aligned. + */ + emit_align(&prog, 16); + /* Update the branches saved in invoke_bpf_mod_ret with the + * aligned address of do_fexit. + */ + for (i = 0; i < fmod_ret->nr_progs; i++) + emit_cond_near_jump(&branches[i], prog, branches[i], + X86_JNE); + } + if (fexit->nr_progs) - if (invoke_bpf(m, &prog, fexit, stack_size)) - return -EINVAL; + if (invoke_bpf(m, &prog, fexit, stack_size)) { + ret = -EINVAL; + goto cleanup; + } if (flags & BPF_TRAMP_F_RESTORE_REGS) restore_regs(m, &prog, nr_args, stack_size); + /* This needs to be done regardless. If there were fmod_ret programs, + * the return value is only updated on the stack and still needs to be + * restored to R0. + */ if (flags & BPF_TRAMP_F_CALL_ORIG) /* restore original return value back into RAX */ emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8); @@ -1586,9 +1688,15 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */ EMIT1(0xC3); /* ret */ /* Make sure the trampoline generation logic doesn't overflow */ - if (WARN_ON_ONCE(prog > (u8 *)image_end - BPF_INSN_SAFETY)) - return -EFAULT; - return prog - (u8 *)image; + if (WARN_ON_ONCE(prog > (u8 *)image_end - BPF_INSN_SAFETY)) { + ret = -EFAULT; + goto cleanup; + } + ret = prog - (u8 *)image; + +cleanup: + kfree(branches); + return ret; } static int emit_fallback_jump(u8 **pprog) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 98ec10b23dbb..f748b31e5888 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -474,6 +474,7 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start); enum bpf_tramp_prog_type { BPF_TRAMP_FENTRY, BPF_TRAMP_FEXIT, + BPF_TRAMP_MODIFY_RETURN, BPF_TRAMP_MAX, BPF_TRAMP_REPLACE, /* more than MAX */ }; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index d6b33ea27bcc..40b2d9476268 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -210,6 +210,7 @@ enum bpf_attach_type { BPF_TRACE_RAW_TP, BPF_TRACE_FENTRY, BPF_TRACE_FEXIT, + BPF_MODIFY_RETURN, __MAX_BPF_ATTACH_TYPE }; diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 787140095e58..30841fb8b3c0 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -3710,7 +3710,8 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type, nr_args--; } - if (prog->expected_attach_type == BPF_TRACE_FEXIT && + if ((prog->expected_attach_type == BPF_TRACE_FEXIT || + prog->expected_attach_type == BPF_MODIFY_RETURN) && arg == nr_args) { if (!t) /* Default prog with 5 args. 6th arg is retval. */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 13de65363ba2..7ce0815793dd 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2324,6 +2324,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) if (prog->expected_attach_type != BPF_TRACE_FENTRY && prog->expected_attach_type != BPF_TRACE_FEXIT && + prog->expected_attach_type != BPF_MODIFY_RETURN && prog->type != BPF_PROG_TYPE_EXT) { err = -EINVAL; goto out_put_prog; diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 546198f6f307..221a17af1f81 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -232,7 +232,8 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) goto out; } - if (tprogs[BPF_TRAMP_FEXIT].nr_progs) + if (tprogs[BPF_TRAMP_FEXIT].nr_progs || + tprogs[BPF_TRAMP_MODIFY_RETURN].nr_progs) flags = BPF_TRAMP_F_CALL_ORIG | BPF_TRAMP_F_SKIP_FRAME; /* Though the second half of trampoline page is unused a task could be @@ -269,6 +270,8 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(enum bpf_attach_type t) switch (t) { case BPF_TRACE_FENTRY: return BPF_TRAMP_FENTRY; + case BPF_MODIFY_RETURN: + return BPF_TRAMP_MODIFY_RETURN; case BPF_TRACE_FEXIT: return BPF_TRAMP_FEXIT; default: diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 289383edfc8c..2460c8e6b5be 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -9950,6 +9950,7 @@ static int check_attach_btf_id(struct bpf_verifier_env *env) if (!prog_extension) return -EINVAL; /* fallthrough */ + case BPF_MODIFY_RETURN: case BPF_TRACE_FENTRY: case BPF_TRACE_FEXIT: if (!btf_type_is_func(t)) { diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index d6b33ea27bcc..40b2d9476268 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -210,6 +210,7 @@ enum bpf_attach_type { BPF_TRACE_RAW_TP, BPF_TRACE_FENTRY, BPF_TRACE_FEXIT, + BPF_MODIFY_RETURN, __MAX_BPF_ATTACH_TYPE }; -- cgit v1.2.3 From da00d2f117a08fbca262db5ea422c80a568b112b Mon Sep 17 00:00:00 2001 From: KP Singh Date: Wed, 4 Mar 2020 20:18:52 +0100 Subject: bpf: Add test ops for BPF_PROG_TYPE_TRACING The current fexit and fentry tests rely on a different program to exercise the functions they attach to. Instead of doing this, implement the test operations for tracing which will also be used for BPF_MODIFY_RETURN in a subsequent patch. Also, clean up the fexit test to use the generated skeleton. Signed-off-by: KP Singh Signed-off-by: Alexei Starovoitov Acked-by: Andrii Nakryiko Acked-by: Daniel Borkmann Link: https://lore.kernel.org/bpf/20200304191853.1529-7-kpsingh@chromium.org --- include/linux/bpf.h | 10 ++++ kernel/trace/bpf_trace.c | 1 + net/bpf/test_run.c | 37 +++++++++--- .../selftests/bpf/prog_tests/fentry_fexit.c | 12 +--- .../testing/selftests/bpf/prog_tests/fentry_test.c | 14 ++--- .../testing/selftests/bpf/prog_tests/fexit_test.c | 69 +++++++--------------- 6 files changed, 67 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index f748b31e5888..40c53924571d 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1156,6 +1156,9 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr); int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr); +int bpf_prog_test_run_tracing(struct bpf_prog *prog, + const union bpf_attr *kattr, + union bpf_attr __user *uattr); int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr); @@ -1313,6 +1316,13 @@ static inline int bpf_prog_test_run_skb(struct bpf_prog *prog, return -ENOTSUPP; } +static inline int bpf_prog_test_run_tracing(struct bpf_prog *prog, + const union bpf_attr *kattr, + union bpf_attr __user *uattr) +{ + return -ENOTSUPP; +} + static inline int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 07764c761073..363e0a2c75cf 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1266,6 +1266,7 @@ const struct bpf_verifier_ops tracing_verifier_ops = { }; const struct bpf_prog_ops tracing_prog_ops = { + .test_run = bpf_prog_test_run_tracing, }; static bool raw_tp_writable_prog_is_valid_access(int off, int size, diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 1cd7a1c2f8b2..3600f098e7c6 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -160,18 +160,37 @@ static void *bpf_test_init(const union bpf_attr *kattr, u32 size, kfree(data); return ERR_PTR(-EFAULT); } - if (bpf_fentry_test1(1) != 2 || - bpf_fentry_test2(2, 3) != 5 || - bpf_fentry_test3(4, 5, 6) != 15 || - bpf_fentry_test4((void *)7, 8, 9, 10) != 34 || - bpf_fentry_test5(11, (void *)12, 13, 14, 15) != 65 || - bpf_fentry_test6(16, (void *)17, 18, 19, (void *)20, 21) != 111) { - kfree(data); - return ERR_PTR(-EFAULT); - } + return data; } +int bpf_prog_test_run_tracing(struct bpf_prog *prog, + const union bpf_attr *kattr, + union bpf_attr __user *uattr) +{ + int err = -EFAULT; + + switch (prog->expected_attach_type) { + case BPF_TRACE_FENTRY: + case BPF_TRACE_FEXIT: + if (bpf_fentry_test1(1) != 2 || + bpf_fentry_test2(2, 3) != 5 || + bpf_fentry_test3(4, 5, 6) != 15 || + bpf_fentry_test4((void *)7, 8, 9, 10) != 34 || + bpf_fentry_test5(11, (void *)12, 13, 14, 15) != 65 || + bpf_fentry_test6(16, (void *)17, 18, 19, (void *)20, 21) != 111) + goto out; + break; + default: + goto out; + } + + err = 0; +out: + trace_bpf_test_finish(&err); + return err; +} + static void *bpf_ctx_init(const union bpf_attr *kattr, u32 max_size) { void __user *data_in = u64_to_user_ptr(kattr->test.ctx_in); diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c index 235ac4f67f5b..83493bd5745c 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_fexit.c @@ -1,22 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ #include -#include "test_pkt_access.skel.h" #include "fentry_test.skel.h" #include "fexit_test.skel.h" void test_fentry_fexit(void) { - struct test_pkt_access *pkt_skel = NULL; struct fentry_test *fentry_skel = NULL; struct fexit_test *fexit_skel = NULL; __u64 *fentry_res, *fexit_res; __u32 duration = 0, retval; - int err, pkt_fd, i; + int err, prog_fd, i; - pkt_skel = test_pkt_access__open_and_load(); - if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n")) - return; fentry_skel = fentry_test__open_and_load(); if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) goto close_prog; @@ -31,8 +26,8 @@ void test_fentry_fexit(void) if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err)) goto close_prog; - pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access); - err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6), + prog_fd = bpf_program__fd(fexit_skel->progs.test1); + err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, NULL, &retval, &duration); CHECK(err || retval, "ipv6", "err %d errno %d retval %d duration %d\n", @@ -49,7 +44,6 @@ void test_fentry_fexit(void) } close_prog: - test_pkt_access__destroy(pkt_skel); fentry_test__destroy(fentry_skel); fexit_test__destroy(fexit_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/fentry_test.c b/tools/testing/selftests/bpf/prog_tests/fentry_test.c index 5cc06021f27d..04ebbf1cb390 100644 --- a/tools/testing/selftests/bpf/prog_tests/fentry_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fentry_test.c @@ -1,20 +1,15 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ #include -#include "test_pkt_access.skel.h" #include "fentry_test.skel.h" void test_fentry_test(void) { - struct test_pkt_access *pkt_skel = NULL; struct fentry_test *fentry_skel = NULL; - int err, pkt_fd, i; + int err, prog_fd, i; __u32 duration = 0, retval; __u64 *result; - pkt_skel = test_pkt_access__open_and_load(); - if (CHECK(!pkt_skel, "pkt_skel_load", "pkt_access skeleton failed\n")) - return; fentry_skel = fentry_test__open_and_load(); if (CHECK(!fentry_skel, "fentry_skel_load", "fentry skeleton failed\n")) goto cleanup; @@ -23,10 +18,10 @@ void test_fentry_test(void) if (CHECK(err, "fentry_attach", "fentry attach failed: %d\n", err)) goto cleanup; - pkt_fd = bpf_program__fd(pkt_skel->progs.test_pkt_access); - err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6), + prog_fd = bpf_program__fd(fentry_skel->progs.test1); + err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, NULL, &retval, &duration); - CHECK(err || retval, "ipv6", + CHECK(err || retval, "test_run", "err %d errno %d retval %d duration %d\n", err, errno, retval, duration); @@ -39,5 +34,4 @@ void test_fentry_test(void) cleanup: fentry_test__destroy(fentry_skel); - test_pkt_access__destroy(pkt_skel); } diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_test.c b/tools/testing/selftests/bpf/prog_tests/fexit_test.c index d2c3655dd7a3..78d7a2765c27 100644 --- a/tools/testing/selftests/bpf/prog_tests/fexit_test.c +++ b/tools/testing/selftests/bpf/prog_tests/fexit_test.c @@ -1,64 +1,37 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2019 Facebook */ #include +#include "fexit_test.skel.h" void test_fexit_test(void) { - struct bpf_prog_load_attr attr = { - .file = "./fexit_test.o", - }; - - char prog_name[] = "fexit/bpf_fentry_testX"; - struct bpf_object *obj = NULL, *pkt_obj; - int err, pkt_fd, kfree_skb_fd, i; - struct bpf_link *link[6] = {}; - struct bpf_program *prog[6]; + struct fexit_test *fexit_skel = NULL; + int err, prog_fd, i; __u32 duration = 0, retval; - struct bpf_map *data_map; - const int zero = 0; - u64 result[6]; + __u64 *result; - err = bpf_prog_load("./test_pkt_access.o", BPF_PROG_TYPE_SCHED_CLS, - &pkt_obj, &pkt_fd); - if (CHECK(err, "prog_load sched cls", "err %d errno %d\n", err, errno)) - return; - err = bpf_prog_load_xattr(&attr, &obj, &kfree_skb_fd); - if (CHECK(err, "prog_load fail", "err %d errno %d\n", err, errno)) - goto close_prog; + fexit_skel = fexit_test__open_and_load(); + if (CHECK(!fexit_skel, "fexit_skel_load", "fexit skeleton failed\n")) + goto cleanup; - for (i = 0; i < 6; i++) { - prog_name[sizeof(prog_name) - 2] = '1' + i; - prog[i] = bpf_object__find_program_by_title(obj, prog_name); - if (CHECK(!prog[i], "find_prog", "prog %s not found\n", prog_name)) - goto close_prog; - link[i] = bpf_program__attach_trace(prog[i]); - if (CHECK(IS_ERR(link[i]), "attach_trace", "failed to link\n")) - goto close_prog; - } - data_map = bpf_object__find_map_by_name(obj, "fexit_te.bss"); - if (CHECK(!data_map, "find_data_map", "data map not found\n")) - goto close_prog; + err = fexit_test__attach(fexit_skel); + if (CHECK(err, "fexit_attach", "fexit attach failed: %d\n", err)) + goto cleanup; - err = bpf_prog_test_run(pkt_fd, 1, &pkt_v6, sizeof(pkt_v6), + prog_fd = bpf_program__fd(fexit_skel->progs.test1); + err = bpf_prog_test_run(prog_fd, 1, NULL, 0, NULL, NULL, &retval, &duration); - CHECK(err || retval, "ipv6", + CHECK(err || retval, "test_run", "err %d errno %d retval %d duration %d\n", err, errno, retval, duration); - err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &result); - if (CHECK(err, "get_result", - "failed to get output data: %d\n", err)) - goto close_prog; - - for (i = 0; i < 6; i++) - if (CHECK(result[i] != 1, "result", "bpf_fentry_test%d failed err %ld\n", - i + 1, result[i])) - goto close_prog; + result = (__u64 *)fexit_skel->bss; + for (i = 0; i < 6; i++) { + if (CHECK(result[i] != 1, "result", + "fexit_test%d failed err %lld\n", i + 1, result[i])) + goto cleanup; + } -close_prog: - for (i = 0; i < 6; i++) - if (!IS_ERR_OR_NULL(link[i])) - bpf_link__destroy(link[i]); - bpf_object__close(obj); - bpf_object__close(pkt_obj); +cleanup: + fexit_test__destroy(fexit_skel); } -- cgit v1.2.3 From 69df578c5f4b1f9d9b649e5fb06b5d337c25d27f Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:50:02 +0200 Subject: net: mscc: ocelot: eliminate confusion between CPU and NPI port Ocelot has the concept of a CPU port. The CPU port is represented in the forwarding and the queueing system, but it is not a physical device. The CPU port can either be accessed via register-based injection/extraction (which is the case of Ocelot), via Frame-DMA (similar to the first one), or "connected" to a physical Ethernet port (called NPI in the datasheet) which is the case of the Felix DSA switch. In Ocelot the CPU port is at index 11. In Felix the CPU port is at index 6. The CPU bit is treated special in the forwarding, as it is never cleared from the forwarding port mask (once added to it). Other than that, it is treated the same as a normal front port. Both Felix and Ocelot should use the CPU port in the same way. This means that Felix should not use the NPI port directly when forwarding to the CPU, but instead use the CPU port. This patch is fixing this such that Felix will use port 6 as its CPU port, and just use the NPI port to carry the traffic. Therefore, eliminate the "ocelot->cpu" variable which was holding the index of the NPI port for Felix, and the index of the CPU port module for Ocelot, so the variable was actually configuring different things for different drivers and causing at least part of the confusion. Also remove the "ocelot->num_cpu_ports" variable, which is the result of another confusion. The 2 CPU ports mentioned in the datasheet are because there are two frame extraction channels (register based or DMA based). This is of no relevance to the driver at the moment, and invisible to the analyzer module. Signed-off-by: Vladimir Oltean Suggested-by: Allan W. Nielsen Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 7 ++-- drivers/net/ethernet/mscc/ocelot.c | 62 ++++++++++++++++++-------------- drivers/net/ethernet/mscc/ocelot_board.c | 7 ++-- include/soc/mscc/ocelot.h | 12 ++++--- net/dsa/tag_ocelot.c | 3 +- 5 files changed, 52 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index c0acd7dc7f48..3e87ac6c07e6 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -516,10 +516,11 @@ static int felix_setup(struct dsa_switch *ds) for (port = 0; port < ds->num_ports; port++) { ocelot_init_port(ocelot, port); + /* Bring up the CPU port module and configure the NPI port */ if (dsa_is_cpu_port(ds, port)) - ocelot_set_cpu_port(ocelot, port, - OCELOT_TAG_PREFIX_NONE, - OCELOT_TAG_PREFIX_LONG); + ocelot_configure_cpu(ocelot, port, + OCELOT_TAG_PREFIX_NONE, + OCELOT_TAG_PREFIX_LONG); } /* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040) diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index ac4cf34d3af5..06f9d013f807 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -1413,7 +1413,7 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) * a source for the other ports. */ for (p = 0; p < ocelot->num_phys_ports; p++) { - if (p == ocelot->cpu || (ocelot->bridge_fwd_mask & BIT(p))) { + if (ocelot->bridge_fwd_mask & BIT(p)) { unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(p); for (i = 0; i < ocelot->num_phys_ports; i++) { @@ -1428,18 +1428,10 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) } } - /* Avoid the NPI port from looping back to itself */ - if (p != ocelot->cpu) - mask |= BIT(ocelot->cpu); - ocelot_write_rix(ocelot, mask, ANA_PGID_PGID, PGID_SRC + p); } else { - /* Only the CPU port, this is compatible with link - * aggregation. - */ - ocelot_write_rix(ocelot, - BIT(ocelot->cpu), + ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + p); } } @@ -2308,27 +2300,34 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port, } EXPORT_SYMBOL(ocelot_probe_port); -void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, - enum ocelot_tag_prefix injection, - enum ocelot_tag_prefix extraction) +/* Configure and enable the CPU port module, which is a set of queues. + * If @npi contains a valid port index, the CPU port module is connected + * to the Node Processor Interface (NPI). This is the mode through which + * frames can be injected from and extracted to an external CPU, + * over Ethernet. + */ +void ocelot_configure_cpu(struct ocelot *ocelot, int npi, + enum ocelot_tag_prefix injection, + enum ocelot_tag_prefix extraction) { - /* Configure and enable the CPU port. */ + int cpu = ocelot->num_phys_ports; + + /* The unicast destination PGID for the CPU port module is unused */ ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu); + /* Instead set up a multicast destination PGID for traffic copied to + * the CPU. Whitelisted MAC addresses like the port netdevice MAC + * addresses will be copied to the CPU via this PGID. + */ ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU); ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA | ANA_PORT_PORT_CFG_PORTID_VAL(cpu), ANA_PORT_PORT_CFG, cpu); - /* If the CPU port is a physical port, set up the port in Node - * Processor Interface (NPI) mode. This is the mode through which - * frames can be injected from and extracted to an external CPU. - * Only one port can be an NPI at the same time. - */ - if (cpu < ocelot->num_phys_ports) { + if (npi >= 0 && npi < ocelot->num_phys_ports) { int mtu = VLAN_ETH_FRAME_LEN + OCELOT_TAG_LEN; ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | - QSYS_EXT_CPU_CFG_EXT_CPU_PORT(cpu), + QSYS_EXT_CPU_CFG_EXT_CPU_PORT(npi), QSYS_EXT_CPU_CFG); if (injection == OCELOT_TAG_PREFIX_SHORT) @@ -2336,14 +2335,27 @@ void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, else if (injection == OCELOT_TAG_PREFIX_LONG) mtu += OCELOT_LONG_PREFIX_LEN; - ocelot_port_set_mtu(ocelot, cpu, mtu); + ocelot_port_set_mtu(ocelot, npi, mtu); + + /* Enable NPI port */ + ocelot_write_rix(ocelot, + QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | + QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | + QSYS_SWITCH_PORT_MODE_PORT_ENA, + QSYS_SWITCH_PORT_MODE, npi); + /* NPI port Injection/Extraction configuration */ + ocelot_write_rix(ocelot, + SYS_PORT_MODE_INCL_XTR_HDR(extraction) | + SYS_PORT_MODE_INCL_INJ_HDR(injection), + SYS_PORT_MODE, npi); } - /* CPU port Injection/Extraction configuration */ + /* Enable CPU port module */ ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | QSYS_SWITCH_PORT_MODE_PORT_ENA, QSYS_SWITCH_PORT_MODE, cpu); + /* CPU port Injection/Extraction configuration */ ocelot_write_rix(ocelot, SYS_PORT_MODE_INCL_XTR_HDR(extraction) | SYS_PORT_MODE_INCL_INJ_HDR(injection), SYS_PORT_MODE, cpu); @@ -2353,10 +2365,8 @@ void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), ANA_PORT_VLAN_CFG, cpu); - - ocelot->cpu = cpu; } -EXPORT_SYMBOL(ocelot_set_cpu_port); +EXPORT_SYMBOL(ocelot_configure_cpu); int ocelot_init(struct ocelot *ocelot) { diff --git a/drivers/net/ethernet/mscc/ocelot_board.c b/drivers/net/ethernet/mscc/ocelot_board.c index c343ca5276ef..0ac9fbf77a01 100644 --- a/drivers/net/ethernet/mscc/ocelot_board.c +++ b/drivers/net/ethernet/mscc/ocelot_board.c @@ -453,8 +453,6 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ocelot->ptp = 1; } - ocelot->num_cpu_ports = 1; /* 1 port on the switch, two groups */ - ports = of_get_child_by_name(np, "ethernet-ports"); if (!ports) { dev_err(&pdev->dev, "no ethernet-ports child node found\n"); @@ -471,8 +469,9 @@ static int mscc_ocelot_probe(struct platform_device *pdev) ocelot->vcap = vsc7514_vcap_props; ocelot_init(ocelot); - ocelot_set_cpu_port(ocelot, ocelot->num_phys_ports, - OCELOT_TAG_PREFIX_NONE, OCELOT_TAG_PREFIX_NONE); + /* No NPI port */ + ocelot_configure_cpu(ocelot, -1, OCELOT_TAG_PREFIX_NONE, + OCELOT_TAG_PREFIX_NONE); for_each_available_child_of_node(ports, portnp) { struct ocelot_port_private *priv; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 5b037f976245..23dd4ad31a32 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -451,9 +451,11 @@ struct ocelot { /* Keep track of the vlan port masks */ u32 vlan_mask[VLAN_N_VID]; + /* In tables like ANA:PORT and the ANA:PGID:PGID mask, + * the CPU is located after the physical ports (at the + * num_phys_ports index). + */ u8 num_phys_ports; - u8 num_cpu_ports; - u8 cpu; u32 *lags; @@ -508,9 +510,9 @@ void __ocelot_rmw_ix(struct ocelot *ocelot, u32 val, u32 mask, u32 reg, int ocelot_regfields_init(struct ocelot *ocelot, const struct reg_field *const regfields); struct regmap *ocelot_regmap_init(struct ocelot *ocelot, struct resource *res); -void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, - enum ocelot_tag_prefix injection, - enum ocelot_tag_prefix extraction); +void ocelot_configure_cpu(struct ocelot *ocelot, int npi, + enum ocelot_tag_prefix injection, + enum ocelot_tag_prefix extraction); int ocelot_init(struct ocelot *ocelot); void ocelot_deinit(struct ocelot *ocelot); void ocelot_init_port(struct ocelot *ocelot, int port); diff --git a/net/dsa/tag_ocelot.c b/net/dsa/tag_ocelot.c index 8e3e7283d430..59de1315100f 100644 --- a/net/dsa/tag_ocelot.c +++ b/net/dsa/tag_ocelot.c @@ -153,7 +153,8 @@ static struct sk_buff *ocelot_xmit(struct sk_buff *skb, memset(injection, 0, OCELOT_TAG_LEN); - src = dsa_upstream_port(ds, port); + /* Set the source port as the CPU port module and not the NPI port */ + src = ocelot->num_phys_ports; dest = BIT(port); bypass = true; qos_class = skb->priority; -- cgit v1.2.3 From 1cf3299b038b9083cb62012d6050ac565d277f59 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Sat, 29 Feb 2020 16:50:03 +0200 Subject: net: dsa: felix: Allow unknown unicast traffic towards the CPU port module Compared to other DSA switches, in the Ocelot cores, the RX filtering is a much more important concern. Firstly, the primary use case for Ocelot is non-DSA, so there isn't any secondary Ethernet MAC [the DSA master's one] to implicitly drop frames having a DMAC we are not interested in. So the switch driver itself needs to install FDB entries towards the CPU port module (PGID_CPU) for the MAC address of each switch port, in each VLAN installed on the port. Every address that is not whitelisted is implicitly dropped. This is in order to achieve a behavior similar to N standalone net devices. Secondly, even in the secondary use case of DSA, such as illustrated by Felix with the NPI port mode, that secondary Ethernet MAC is present, but its RX filter is bypassed. This is because the DSA tags themselves are placed before Ethernet, so the DMAC that the switch ports see is not seen by the DSA master too (since it's shifter to the right). So RX filtering is pretty important. A good RX filter won't bother the CPU in case the switch port receives a frame that it's not interested in, and there exists no other line of defense. Ocelot is pretty strict when it comes to RX filtering: non-IP multicast and broadcast traffic is allowed to go to the CPU port module, but unknown unicast isn't. This means that traffic reception for any other MAC addresses than the ones configured on each switch port net device won't work. This includes use cases such as macvlan or bridging with a non-Ocelot (so-called "foreign") interface. But this seems to be fine for the scenarios that the Linux system embedded inside an Ocelot switch is intended for - it is simply not interested in unknown unicast traffic, as explained in Allan Nielsen's presentation [0]. On the other hand, the Felix DSA switch is integrated in more general-purpose Linux systems, so it can't afford to drop that sort of traffic in hardware, even if it will end up doing so later, in software. Actually, unknown unicast means more for Felix than it does for Ocelot. Felix doesn't attempt to perform the whitelisting of switch port MAC addresses towards PGID_CPU at all, mainly because it is too complicated to be feasible: while the MAC addresses are unique in Ocelot, by default in DSA all ports are equal and inherited from the DSA master. This adds into account the question of reference counting MAC addresses (delayed ocelot_mact_forget), not to mention reference counting for the VLAN IDs that those MAC addresses are installed in. This reference counting should be done in the DSA core, and the fact that it wasn't needed so far is due to the fact that the other DSA switches don't have the DSA tag placed before Ethernet, so the DSA master is able to whitelist the MAC addresses in hardware. So this means that even regular traffic termination on a Felix switch port happens through flooding (because neither Felix nor Ocelot learn source MAC addresses from CPU-injected frames). So far we've explained that whitelisting towards PGID_CPU: - helps to reduce the likelihood of spamming the CPU with frames it won't process very far anyway - is implemented in the ocelot driver - is sufficient for the ocelot use cases - is not feasible in DSA - breaks use cases in DSA, in the current status (whitelisting enabled but no MAC address whitelisted) So the proposed patch allows unknown unicast frames to be sent to the CPU port module. This is done for the Felix DSA driver only, as Ocelot seems to be happy without it. [0]: https://www.youtube.com/watch?v=B1HhxEcU7Jg Suggested-by: Allan W. Nielsen Signed-off-by: Vladimir Oltean Reviewed-by: Allan W. Nielsen Signed-off-by: David S. Miller --- drivers/net/dsa/ocelot/felix.c | 9 ++++++ drivers/net/ethernet/mscc/ocelot.h | 10 ------- include/soc/mscc/ocelot.h | 60 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 3e87ac6c07e6..69546383a382 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -523,6 +523,15 @@ static int felix_setup(struct dsa_switch *ds) OCELOT_TAG_PREFIX_LONG); } + /* Include the CPU port module in the forwarding mask for unknown + * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST + * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since + * Ocelot relies on whitelisting MAC addresses towards PGID_CPU. + */ + ocelot_write_rix(ocelot, + ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)), + ANA_PGID_PGID, PGID_UC); + /* It looks like the MAC/PCS interrupt register - PM0_IEVENT (0x8040) * isn't instantiated for the Felix PF. * In-band AN may take a few ms to complete, so we need to poll. diff --git a/drivers/net/ethernet/mscc/ocelot.h b/drivers/net/ethernet/mscc/ocelot.h index 04372ba72fec..e34ef8380eb3 100644 --- a/drivers/net/ethernet/mscc/ocelot.h +++ b/drivers/net/ethernet/mscc/ocelot.h @@ -28,16 +28,6 @@ #include "ocelot_tc.h" #include "ocelot_ptp.h" -#define PGID_AGGR 64 -#define PGID_SRC 80 - -/* Reserved PGIDs */ -#define PGID_CPU (PGID_AGGR - 5) -#define PGID_UC (PGID_AGGR - 4) -#define PGID_MC (PGID_AGGR - 3) -#define PGID_MCIPV4 (PGID_AGGR - 2) -#define PGID_MCIPV6 (PGID_AGGR - 1) - #define OCELOT_BUFFER_CELL_SZ 60 #define OCELOT_STATS_CHECK_DELAY (2 * HZ) diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index 23dd4ad31a32..007b584cc431 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -11,6 +11,66 @@ #include #include +/* Port Group IDs (PGID) are masks of destination ports. + * + * For L2 forwarding, the switch performs 3 lookups in the PGID table for each + * frame, and forwards the frame to the ports that are present in the logical + * AND of all 3 PGIDs. + * + * These PGID lookups are: + * - In one of PGID[0-63]: for the destination masks. There are 2 paths by + * which the switch selects a destination PGID: + * - The {DMAC, VID} is present in the MAC table. In that case, the + * destination PGID is given by the DEST_IDX field of the MAC table entry + * that matched. + * - The {DMAC, VID} is not present in the MAC table (it is unknown). The + * frame is disseminated as being either unicast, multicast or broadcast, + * and according to that, the destination PGID is chosen as being the + * value contained by ANA_FLOODING_FLD_UNICAST, + * ANA_FLOODING_FLD_MULTICAST or ANA_FLOODING_FLD_BROADCAST. + * The destination PGID can be an unicast set: the first PGIDs, 0 to + * ocelot->num_phys_ports - 1, or a multicast set: the PGIDs from + * ocelot->num_phys_ports to 63. By convention, a unicast PGID corresponds to + * a physical port and has a single bit set in the destination ports mask: + * that corresponding to the port number itself. In contrast, a multicast + * PGID will have potentially more than one single bit set in the destination + * ports mask. + * - In one of PGID[64-79]: for the aggregation mask. The switch classifier + * dissects each frame and generates a 4-bit Link Aggregation Code which is + * used for this second PGID table lookup. The goal of link aggregation is to + * hash multiple flows within the same LAG on to different destination ports. + * The first lookup will result in a PGID with all the LAG members present in + * the destination ports mask, and the second lookup, by Link Aggregation + * Code, will ensure that each flow gets forwarded only to a single port out + * of that mask (there are no duplicates). + * - In one of PGID[80-90]: for the source mask. The third time, the PGID table + * is indexed with the ingress port (plus 80). These PGIDs answer the + * question "is port i allowed to forward traffic to port j?" If yes, then + * BIT(j) of PGID 80+i will be found set. The third PGID lookup can be used + * to enforce the L2 forwarding matrix imposed by e.g. a Linux bridge. + */ + +/* Reserve some destination PGIDs at the end of the range: + * PGID_CPU: used for whitelisting certain MAC addresses, such as the addresses + * of the switch port net devices, towards the CPU port module. + * PGID_UC: the flooding destinations for unknown unicast traffic. + * PGID_MC: the flooding destinations for broadcast and non-IP multicast + * traffic. + * PGID_MCIPV4: the flooding destinations for IPv4 multicast traffic. + * PGID_MCIPV6: the flooding destinations for IPv6 multicast traffic. + */ +#define PGID_CPU 59 +#define PGID_UC 60 +#define PGID_MC 61 +#define PGID_MCIPV4 62 +#define PGID_MCIPV6 63 + +/* Aggregation PGIDs, one per Link Aggregation Code */ +#define PGID_AGGR 64 + +/* Source PGIDs, one per physical port */ +#define PGID_SRC 80 + #define IFH_INJ_BYPASS BIT(31) #define IFH_INJ_POP_CNT_DISABLE (3 << 28) -- cgit v1.2.3 From d6e055e8733da5ce53fc69c77e379915400068c5 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 29 Feb 2020 23:23:44 +0100 Subject: PCI: Add constant PCI_STATUS_ERROR_BITS This collection of PCI error bits is used in more than one driver, so move it to the PCI core. Signed-off-by: Heiner Kallweit Acked-by: Bjorn Helgaas Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/skge.h | 7 ------- drivers/net/ethernet/marvell/sky2.h | 7 ------- include/linux/pci.h | 7 +++++++ 3 files changed, 7 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/marvell/skge.h b/drivers/net/ethernet/marvell/skge.h index 8ef19b3aa8ff..6928abcec0a3 100644 --- a/drivers/net/ethernet/marvell/skge.h +++ b/drivers/net/ethernet/marvell/skge.h @@ -15,13 +15,6 @@ #define PCI_VPD_ROM_SZ 7L<<14 /* VPD ROM size 0=256, 1=512, ... */ #define PCI_REV_DESC 1<<2 /* Reverse Descriptor bytes */ -#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ - PCI_STATUS_SIG_SYSTEM_ERROR | \ - PCI_STATUS_REC_MASTER_ABORT | \ - PCI_STATUS_REC_TARGET_ABORT | \ - PCI_STATUS_SIG_TARGET_ABORT | \ - PCI_STATUS_PARITY) - enum csr_regs { B0_RAP = 0x0000, B0_CTST = 0x0004, diff --git a/drivers/net/ethernet/marvell/sky2.h b/drivers/net/ethernet/marvell/sky2.h index 2474d326319e..b2dddd8a246c 100644 --- a/drivers/net/ethernet/marvell/sky2.h +++ b/drivers/net/ethernet/marvell/sky2.h @@ -252,13 +252,6 @@ enum { }; -#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ - PCI_STATUS_SIG_SYSTEM_ERROR | \ - PCI_STATUS_REC_MASTER_ABORT | \ - PCI_STATUS_REC_TARGET_ABORT | \ - PCI_STATUS_SIG_TARGET_ABORT | \ - PCI_STATUS_PARITY) - enum csr_regs { B0_RAP = 0x0000, B0_CTST = 0x0004, diff --git a/include/linux/pci.h b/include/linux/pci.h index 3840a541a9de..101d71e0ad0d 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -42,6 +42,13 @@ #include +#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ + PCI_STATUS_SIG_SYSTEM_ERROR | \ + PCI_STATUS_REC_MASTER_ABORT | \ + PCI_STATUS_REC_TARGET_ABORT | \ + PCI_STATUS_SIG_TARGET_ABORT | \ + PCI_STATUS_PARITY) + /* * The PCI interface treats multi-function devices as independent * devices. The slot/function address of each device is encoded -- cgit v1.2.3 From ec5d9e87842a43be3a10ada0d5f560bbd3f31d5d Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 29 Feb 2020 23:24:23 +0100 Subject: PCI: Add pci_status_get_and_clear_errors Several drivers use the following code sequence: 1. Read PCI_STATUS 2. Mask out non-error bits 3. Action based on error bits set 4. Write back set error bits to clear them As this is a repeated pattern, add a helper to the PCI core. Signed-off-by: Heiner Kallweit Acked-by: Bjorn Helgaas Signed-off-by: David S. Miller --- drivers/pci/pci.c | 23 +++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 24 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d828ca835a98..c16b0ba2a895 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -173,6 +173,29 @@ unsigned char pci_bus_max_busnr(struct pci_bus *bus) } EXPORT_SYMBOL_GPL(pci_bus_max_busnr); +/** + * pci_status_get_and_clear_errors - return and clear error bits in PCI_STATUS + * @pdev: the PCI device + * + * Returns error bits set in PCI_STATUS and clears them. + */ +int pci_status_get_and_clear_errors(struct pci_dev *pdev) +{ + u16 status; + int ret; + + ret = pci_read_config_word(pdev, PCI_STATUS, &status); + if (ret != PCIBIOS_SUCCESSFUL) + return -EIO; + + status &= PCI_STATUS_ERROR_BITS; + if (status) + pci_write_config_word(pdev, PCI_STATUS, status); + + return status; +} +EXPORT_SYMBOL_GPL(pci_status_get_and_clear_errors); + #ifdef CONFIG_HAS_IOMEM void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) { diff --git a/include/linux/pci.h b/include/linux/pci.h index 101d71e0ad0d..7beaf51e98ec 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1210,6 +1210,7 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); struct pci_dev *pci_real_dma_dev(struct pci_dev *dev); +int pci_status_get_and_clear_errors(struct pci_dev *pdev); int __printf(6, 7) pci_request_irq(struct pci_dev *dev, unsigned int nr, irq_handler_t handler, irq_handler_t thread_fn, void *dev_id, -- cgit v1.2.3 From 51891498f2da78ee64dfad88fa53c9e85fb50abf Mon Sep 17 00:00:00 2001 From: Tycho Andersen Date: Wed, 4 Mar 2020 11:05:17 -0700 Subject: seccomp: allow TSYNC and USER_NOTIF together The restriction introduced in 7a0df7fbc145 ("seccomp: Make NEW_LISTENER and TSYNC flags exclusive") is mostly artificial: there is enough information in a seccomp user notification to tell which thread triggered a notification. The reason it was introduced is because TSYNC makes the syscall return a thread-id on failure, and NEW_LISTENER returns an fd, and there's no way to distinguish between these two cases (well, I suppose the caller could check all fds it has, then do the syscall, and if the return value was an fd that already existed, then it must be a thread id, but bleh). Matthew would like to use these two flags together in the Chrome sandbox which wants to use TSYNC for video drivers and NEW_LISTENER to proxy syscalls. So, let's fix this ugliness by adding another flag, TSYNC_ESRCH, which tells the kernel to just return -ESRCH on a TSYNC error. This way, NEW_LISTENER (and any subsequent seccomp() commands that want to return positive values) don't conflict with each other. Suggested-by: Matthew Denton Signed-off-by: Tycho Andersen Link: https://lore.kernel.org/r/20200304180517.23867-1-tycho@tycho.ws Signed-off-by: Kees Cook --- include/linux/seccomp.h | 3 +- include/uapi/linux/seccomp.h | 1 + kernel/seccomp.c | 14 +++-- tools/testing/selftests/seccomp/seccomp_bpf.c | 74 ++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 03583b6d1416..4192369b8418 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -7,7 +7,8 @@ #define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC | \ SECCOMP_FILTER_FLAG_LOG | \ SECCOMP_FILTER_FLAG_SPEC_ALLOW | \ - SECCOMP_FILTER_FLAG_NEW_LISTENER) + SECCOMP_FILTER_FLAG_NEW_LISTENER | \ + SECCOMP_FILTER_FLAG_TSYNC_ESRCH) #ifdef CONFIG_SECCOMP diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index be84d87f1f46..c1735455bc53 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -22,6 +22,7 @@ #define SECCOMP_FILTER_FLAG_LOG (1UL << 1) #define SECCOMP_FILTER_FLAG_SPEC_ALLOW (1UL << 2) #define SECCOMP_FILTER_FLAG_NEW_LISTENER (1UL << 3) +#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4) /* * All BPF programs must return a 32-bit value. diff --git a/kernel/seccomp.c b/kernel/seccomp.c index b6ea3dcb57bf..29022c1bbe18 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -528,8 +528,12 @@ static long seccomp_attach_filter(unsigned int flags, int ret; ret = seccomp_can_sync_threads(); - if (ret) - return ret; + if (ret) { + if (flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH) + return -ESRCH; + else + return ret; + } } /* Set log flag, if present. */ @@ -1288,10 +1292,12 @@ static long seccomp_set_mode_filter(unsigned int flags, * In the successful case, NEW_LISTENER returns the new listener fd. * But in the failure case, TSYNC returns the thread that died. If you * combine these two flags, there's no way to tell whether something - * succeeded or failed. So, let's disallow this combination. + * succeeded or failed. So, let's disallow this combination if the user + * has not explicitly requested no errors from TSYNC. */ if ((flags & SECCOMP_FILTER_FLAG_TSYNC) && - (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER)) + (flags & SECCOMP_FILTER_FLAG_NEW_LISTENER) && + ((flags & SECCOMP_FILTER_FLAG_TSYNC_ESRCH) == 0)) return -EINVAL; /* Prepare the new filter before holding any locks. */ diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index ee1b727ede04..a9ad3bd8b2ad 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -212,6 +212,10 @@ struct seccomp_notif_sizes { #define SECCOMP_USER_NOTIF_FLAG_CONTINUE 0x00000001 #endif +#ifndef SECCOMP_FILTER_FLAG_TSYNC_ESRCH +#define SECCOMP_FILTER_FLAG_TSYNC_ESRCH (1UL << 4) +#endif + #ifndef seccomp int seccomp(unsigned int op, unsigned int flags, void *args) { @@ -2187,7 +2191,8 @@ TEST(detect_seccomp_filter_flags) unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC, SECCOMP_FILTER_FLAG_LOG, SECCOMP_FILTER_FLAG_SPEC_ALLOW, - SECCOMP_FILTER_FLAG_NEW_LISTENER }; + SECCOMP_FILTER_FLAG_NEW_LISTENER, + SECCOMP_FILTER_FLAG_TSYNC_ESRCH }; unsigned int exclusive[] = { SECCOMP_FILTER_FLAG_TSYNC, SECCOMP_FILTER_FLAG_NEW_LISTENER }; @@ -2645,6 +2650,55 @@ TEST_F(TSYNC, two_siblings_with_one_divergence) EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); } +TEST_F(TSYNC, two_siblings_with_one_divergence_no_tid_in_err) +{ + long ret, flags; + void *status; + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + + ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog); + ASSERT_NE(ENOSYS, errno) { + TH_LOG("Kernel does not support seccomp syscall!"); + } + ASSERT_EQ(0, ret) { + TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!"); + } + self->sibling[0].diverge = 1; + tsync_start_sibling(&self->sibling[0]); + tsync_start_sibling(&self->sibling[1]); + + while (self->sibling_count < TSYNC_SIBLINGS) { + sem_wait(&self->started); + self->sibling_count++; + } + + flags = SECCOMP_FILTER_FLAG_TSYNC | \ + SECCOMP_FILTER_FLAG_TSYNC_ESRCH; + ret = seccomp(SECCOMP_SET_MODE_FILTER, flags, &self->apply_prog); + ASSERT_EQ(ESRCH, errno) { + TH_LOG("Did not return ESRCH for diverged sibling."); + } + ASSERT_EQ(-1, ret) { + TH_LOG("Did not fail on diverged sibling."); + } + + /* Wake the threads */ + pthread_mutex_lock(&self->mutex); + ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) { + TH_LOG("cond broadcast non-zero"); + } + pthread_mutex_unlock(&self->mutex); + + /* Ensure they are both unkilled. */ + PTHREAD_JOIN(self->sibling[0].tid, &status); + EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); + PTHREAD_JOIN(self->sibling[1].tid, &status); + EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); +} + TEST_F(TSYNC, two_siblings_not_under_filter) { long ret, sib; @@ -3196,6 +3250,24 @@ TEST(user_notification_basic) EXPECT_EQ(0, WEXITSTATUS(status)); } +TEST(user_notification_with_tsync) +{ + int ret; + unsigned int flags; + + /* these were exclusive */ + flags = SECCOMP_FILTER_FLAG_NEW_LISTENER | + SECCOMP_FILTER_FLAG_TSYNC; + ASSERT_EQ(-1, user_trap_syscall(__NR_getppid, flags)); + ASSERT_EQ(EINVAL, errno); + + /* but now they're not */ + flags |= SECCOMP_FILTER_FLAG_TSYNC_ESRCH; + ret = user_trap_syscall(__NR_getppid, flags); + close(ret); + ASSERT_LE(0, ret); +} + TEST(user_notification_kill_in_middle) { pid_t pid; -- cgit v1.2.3 From 1579f1bc3b753d17a44de3457d5c6f4a5b14c752 Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sun, 1 Mar 2020 22:52:35 +0800 Subject: crypto: x86/curve25519 - support assemblers with no adx support Some older version of GAS do not support the ADX instructions, similarly to how they also don't support AVX and such. This commit adds the same build-time detection mechanisms we use for AVX and others for ADX, and then makes sure that the curve25519 library dispatcher calls the right functions. Reported-by: Willy Tarreau Signed-off-by: Jason A. Donenfeld Signed-off-by: Herbert Xu --- arch/x86/Makefile | 5 +++-- arch/x86/crypto/Makefile | 7 ++++++- include/crypto/curve25519.h | 6 ++++-- 3 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 94df0868804b..513a55562d75 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -194,9 +194,10 @@ avx2_instr :=$(call as-instr,vpbroadcastb %xmm0$(comma)%ymm1,-DCONFIG_AS_AVX2=1) avx512_instr :=$(call as-instr,vpmovm2b %k1$(comma)%zmm5,-DCONFIG_AS_AVX512=1) sha1_ni_instr :=$(call as-instr,sha1msg1 %xmm0$(comma)%xmm1,-DCONFIG_AS_SHA1_NI=1) sha256_ni_instr :=$(call as-instr,sha256msg1 %xmm0$(comma)%xmm1,-DCONFIG_AS_SHA256_NI=1) +adx_instr := $(call as-instr,adox %r10$(comma)%r10,-DCONFIG_AS_ADX=1) -KBUILD_AFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr) $(avx512_instr) $(sha1_ni_instr) $(sha256_ni_instr) -KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr) $(avx512_instr) $(sha1_ni_instr) $(sha256_ni_instr) +KBUILD_AFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr) $(avx512_instr) $(sha1_ni_instr) $(sha256_ni_instr) $(adx_instr) +KBUILD_CFLAGS += $(cfi) $(cfi-sigframe) $(cfi-sections) $(asinstr) $(avx_instr) $(avx2_instr) $(avx512_instr) $(sha1_ni_instr) $(sha256_ni_instr) $(adx_instr) KBUILD_LDFLAGS := -m elf_$(UTS_MACHINE) diff --git a/arch/x86/crypto/Makefile b/arch/x86/crypto/Makefile index b69e00bf20b8..8c2e9eadee8a 100644 --- a/arch/x86/crypto/Makefile +++ b/arch/x86/crypto/Makefile @@ -11,6 +11,7 @@ avx2_supported := $(call as-instr,vpgatherdd %ymm0$(comma)(%eax$(comma)%ymm1\ avx512_supported :=$(call as-instr,vpmovm2b %k1$(comma)%zmm5,yes,no) sha1_ni_supported :=$(call as-instr,sha1msg1 %xmm0$(comma)%xmm1,yes,no) sha256_ni_supported :=$(call as-instr,sha256msg1 %xmm0$(comma)%xmm1,yes,no) +adx_supported := $(call as-instr,adox %r10$(comma)%r10,yes,no) obj-$(CONFIG_CRYPTO_GLUE_HELPER_X86) += glue_helper.o @@ -39,7 +40,11 @@ obj-$(CONFIG_CRYPTO_AEGIS128_AESNI_SSE2) += aegis128-aesni.o obj-$(CONFIG_CRYPTO_NHPOLY1305_SSE2) += nhpoly1305-sse2.o obj-$(CONFIG_CRYPTO_NHPOLY1305_AVX2) += nhpoly1305-avx2.o -obj-$(CONFIG_CRYPTO_CURVE25519_X86) += curve25519-x86_64.o + +# These modules require the assembler to support ADX. +ifeq ($(adx_supported),yes) + obj-$(CONFIG_CRYPTO_CURVE25519_X86) += curve25519-x86_64.o +endif # These modules require assembler to support AVX. ifeq ($(avx_supported),yes) diff --git a/include/crypto/curve25519.h b/include/crypto/curve25519.h index 4e6dc840b159..9ecb3c1f0f15 100644 --- a/include/crypto/curve25519.h +++ b/include/crypto/curve25519.h @@ -33,7 +33,8 @@ bool __must_check curve25519(u8 mypublic[CURVE25519_KEY_SIZE], const u8 secret[CURVE25519_KEY_SIZE], const u8 basepoint[CURVE25519_KEY_SIZE]) { - if (IS_ENABLED(CONFIG_CRYPTO_ARCH_HAVE_LIB_CURVE25519)) + if (IS_ENABLED(CONFIG_CRYPTO_ARCH_HAVE_LIB_CURVE25519) && + (!IS_ENABLED(CONFIG_CRYPTO_CURVE25519_X86) || IS_ENABLED(CONFIG_AS_ADX))) curve25519_arch(mypublic, secret, basepoint); else curve25519_generic(mypublic, secret, basepoint); @@ -49,7 +50,8 @@ __must_check curve25519_generate_public(u8 pub[CURVE25519_KEY_SIZE], CURVE25519_KEY_SIZE))) return false; - if (IS_ENABLED(CONFIG_CRYPTO_ARCH_HAVE_LIB_CURVE25519)) + if (IS_ENABLED(CONFIG_CRYPTO_ARCH_HAVE_LIB_CURVE25519) && + (!IS_ENABLED(CONFIG_CRYPTO_CURVE25519_X86) || IS_ENABLED(CONFIG_AS_ADX))) curve25519_base_arch(pub, secret); else curve25519_generic(pub, secret, curve25519_base_point); -- cgit v1.2.3 From 1326034b3ce7073e3ed74bd0f4d24afee96a9e07 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Wed, 19 Feb 2020 21:05:17 +0200 Subject: net/mlx5: Expose raw packet pacing APIs Expose raw packet pacing APIs to be used by DEVX based applications. The existing code was refactored to have a single flow with the new raw APIs. The new raw APIs considered the input of 'pp_rate_limit_context', uid, 'dedicated', upon looking for an existing entry. This raw mode enables future device specification data in the raw context without changing the existing logic and code. The ability to ask for a dedicated entry gives control for application to allocate entries according to its needs. A dedicated entry may not be used by some other process and it also enables the process spreading its resources to some different entries for use different hardware resources as part of enforcing the rate. The counter per entry was changed to be u64 to prevent any option to overflow. Signed-off-by: Yishai Hadas Acked-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/net/ethernet/mellanox/mlx5/core/rl.c | 130 ++++++++++++++++++++------- include/linux/mlx5/driver.h | 11 ++- include/linux/mlx5/mlx5_ifc.h | 26 +++--- 3 files changed, 122 insertions(+), 45 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/rl.c b/drivers/net/ethernet/mellanox/mlx5/core/rl.c index 01c380425f9d..f3b29d9ade1f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/rl.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/rl.c @@ -101,22 +101,39 @@ int mlx5_destroy_scheduling_element_cmd(struct mlx5_core_dev *dev, u8 hierarchy, return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } +static bool mlx5_rl_are_equal_raw(struct mlx5_rl_entry *entry, void *rl_in, + u16 uid) +{ + return (!memcmp(entry->rl_raw, rl_in, sizeof(entry->rl_raw)) && + entry->uid == uid); +} + /* Finds an entry where we can register the given rate * If the rate already exists, return the entry where it is registered, * otherwise return the first available entry. * If the table is full, return NULL */ static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table, - struct mlx5_rate_limit *rl) + void *rl_in, u16 uid, bool dedicated) { struct mlx5_rl_entry *ret_entry = NULL; bool empty_found = false; int i; for (i = 0; i < table->max_size; i++) { - if (mlx5_rl_are_equal(&table->rl_entry[i].rl, rl)) - return &table->rl_entry[i]; - if (!empty_found && !table->rl_entry[i].rl.rate) { + if (dedicated) { + if (!table->rl_entry[i].refcount) + return &table->rl_entry[i]; + continue; + } + + if (table->rl_entry[i].refcount) { + if (table->rl_entry[i].dedicated) + continue; + if (mlx5_rl_are_equal_raw(&table->rl_entry[i], rl_in, + uid)) + return &table->rl_entry[i]; + } else if (!empty_found) { empty_found = true; ret_entry = &table->rl_entry[i]; } @@ -126,18 +143,19 @@ static struct mlx5_rl_entry *find_rl_entry(struct mlx5_rl_table *table, } static int mlx5_set_pp_rate_limit_cmd(struct mlx5_core_dev *dev, - u16 index, - struct mlx5_rate_limit *rl) + struct mlx5_rl_entry *entry, bool set) { - u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {0}; - u32 out[MLX5_ST_SZ_DW(set_pp_rate_limit_out)] = {0}; + u32 in[MLX5_ST_SZ_DW(set_pp_rate_limit_in)] = {}; + u32 out[MLX5_ST_SZ_DW(set_pp_rate_limit_out)] = {}; + void *pp_context; + pp_context = MLX5_ADDR_OF(set_pp_rate_limit_in, in, ctx); MLX5_SET(set_pp_rate_limit_in, in, opcode, MLX5_CMD_OP_SET_PP_RATE_LIMIT); - MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, index); - MLX5_SET(set_pp_rate_limit_in, in, rate_limit, rl->rate); - MLX5_SET(set_pp_rate_limit_in, in, burst_upper_bound, rl->max_burst_sz); - MLX5_SET(set_pp_rate_limit_in, in, typical_packet_size, rl->typical_pkt_sz); + MLX5_SET(set_pp_rate_limit_in, in, uid, entry->uid); + MLX5_SET(set_pp_rate_limit_in, in, rate_limit_index, entry->index); + if (set) + memcpy(pp_context, entry->rl_raw, sizeof(entry->rl_raw)); return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } @@ -158,23 +176,25 @@ bool mlx5_rl_are_equal(struct mlx5_rate_limit *rl_0, } EXPORT_SYMBOL(mlx5_rl_are_equal); -int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u16 *index, - struct mlx5_rate_limit *rl) +int mlx5_rl_add_rate_raw(struct mlx5_core_dev *dev, void *rl_in, u16 uid, + bool dedicated_entry, u16 *index) { struct mlx5_rl_table *table = &dev->priv.rl_table; struct mlx5_rl_entry *entry; int err = 0; + u32 rate; + rate = MLX5_GET(set_pp_rate_limit_context, rl_in, rate_limit); mutex_lock(&table->rl_lock); - if (!rl->rate || !mlx5_rl_is_in_range(dev, rl->rate)) { + if (!rate || !mlx5_rl_is_in_range(dev, rate)) { mlx5_core_err(dev, "Invalid rate: %u, should be %u to %u\n", - rl->rate, table->min_rate, table->max_rate); + rate, table->min_rate, table->max_rate); err = -EINVAL; goto out; } - entry = find_rl_entry(table, rl); + entry = find_rl_entry(table, rl_in, uid, dedicated_entry); if (!entry) { mlx5_core_err(dev, "Max number of %u rates reached\n", table->max_size); @@ -185,16 +205,24 @@ int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u16 *index, /* rate already configured */ entry->refcount++; } else { + memcpy(entry->rl_raw, rl_in, sizeof(entry->rl_raw)); + entry->uid = uid; /* new rate limit */ - err = mlx5_set_pp_rate_limit_cmd(dev, entry->index, rl); + err = mlx5_set_pp_rate_limit_cmd(dev, entry, true); if (err) { - mlx5_core_err(dev, "Failed configuring rate limit(err %d): rate %u, max_burst_sz %u, typical_pkt_sz %u\n", - err, rl->rate, rl->max_burst_sz, - rl->typical_pkt_sz); + mlx5_core_err( + dev, + "Failed configuring rate limit(err %d): rate %u, max_burst_sz %u, typical_pkt_sz %u\n", + err, rate, + MLX5_GET(set_pp_rate_limit_context, rl_in, + burst_upper_bound), + MLX5_GET(set_pp_rate_limit_context, rl_in, + typical_packet_size)); goto out; } - entry->rl = *rl; + entry->refcount = 1; + entry->dedicated = dedicated_entry; } *index = entry->index; @@ -202,20 +230,61 @@ out: mutex_unlock(&table->rl_lock); return err; } +EXPORT_SYMBOL(mlx5_rl_add_rate_raw); + +void mlx5_rl_remove_rate_raw(struct mlx5_core_dev *dev, u16 index) +{ + struct mlx5_rl_table *table = &dev->priv.rl_table; + struct mlx5_rl_entry *entry; + + mutex_lock(&table->rl_lock); + entry = &table->rl_entry[index - 1]; + entry->refcount--; + if (!entry->refcount) + /* need to remove rate */ + mlx5_set_pp_rate_limit_cmd(dev, entry, false); + mutex_unlock(&table->rl_lock); +} +EXPORT_SYMBOL(mlx5_rl_remove_rate_raw); + +int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u16 *index, + struct mlx5_rate_limit *rl) +{ + u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)] = {}; + + MLX5_SET(set_pp_rate_limit_context, rl_raw, rate_limit, rl->rate); + MLX5_SET(set_pp_rate_limit_context, rl_raw, burst_upper_bound, + rl->max_burst_sz); + MLX5_SET(set_pp_rate_limit_context, rl_raw, typical_packet_size, + rl->typical_pkt_sz); + + return mlx5_rl_add_rate_raw(dev, rl_raw, + MLX5_CAP_QOS(dev, packet_pacing_uid) ? + MLX5_SHARED_RESOURCE_UID : 0, + false, index); +} EXPORT_SYMBOL(mlx5_rl_add_rate); void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, struct mlx5_rate_limit *rl) { + u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)] = {}; struct mlx5_rl_table *table = &dev->priv.rl_table; struct mlx5_rl_entry *entry = NULL; - struct mlx5_rate_limit reset_rl = {0}; /* 0 is a reserved value for unlimited rate */ if (rl->rate == 0) return; + MLX5_SET(set_pp_rate_limit_context, rl_raw, rate_limit, rl->rate); + MLX5_SET(set_pp_rate_limit_context, rl_raw, burst_upper_bound, + rl->max_burst_sz); + MLX5_SET(set_pp_rate_limit_context, rl_raw, typical_packet_size, + rl->typical_pkt_sz); + mutex_lock(&table->rl_lock); - entry = find_rl_entry(table, rl); + entry = find_rl_entry(table, rl_raw, + MLX5_CAP_QOS(dev, packet_pacing_uid) ? + MLX5_SHARED_RESOURCE_UID : 0, false); if (!entry || !entry->refcount) { mlx5_core_warn(dev, "Rate %u, max_burst_sz %u typical_pkt_sz %u are not configured\n", rl->rate, rl->max_burst_sz, rl->typical_pkt_sz); @@ -223,11 +292,9 @@ void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, struct mlx5_rate_limit *rl) } entry->refcount--; - if (!entry->refcount) { + if (!entry->refcount) /* need to remove rate */ - mlx5_set_pp_rate_limit_cmd(dev, entry->index, &reset_rl); - entry->rl = reset_rl; - } + mlx5_set_pp_rate_limit_cmd(dev, entry, false); out: mutex_unlock(&table->rl_lock); @@ -273,14 +340,13 @@ int mlx5_init_rl_table(struct mlx5_core_dev *dev) void mlx5_cleanup_rl_table(struct mlx5_core_dev *dev) { struct mlx5_rl_table *table = &dev->priv.rl_table; - struct mlx5_rate_limit rl = {0}; int i; /* Clear all configured rates */ for (i = 0; i < table->max_size; i++) - if (table->rl_entry[i].rl.rate) - mlx5_set_pp_rate_limit_cmd(dev, table->rl_entry[i].index, - &rl); + if (table->rl_entry[i].refcount) + mlx5_set_pp_rate_limit_cmd(dev, &table->rl_entry[i], + false); kfree(dev->priv.rl_table.rl_entry); } diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 277a51d3ec40..f2b4225ed650 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -518,9 +518,11 @@ struct mlx5_rate_limit { }; struct mlx5_rl_entry { - struct mlx5_rate_limit rl; - u16 index; - u16 refcount; + u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)]; + u16 index; + u64 refcount; + u16 uid; + u8 dedicated : 1; }; struct mlx5_rl_table { @@ -1007,6 +1009,9 @@ int mlx5_rl_add_rate(struct mlx5_core_dev *dev, u16 *index, struct mlx5_rate_limit *rl); void mlx5_rl_remove_rate(struct mlx5_core_dev *dev, struct mlx5_rate_limit *rl); bool mlx5_rl_is_in_range(struct mlx5_core_dev *dev, u32 rate); +int mlx5_rl_add_rate_raw(struct mlx5_core_dev *dev, void *rl_in, u16 uid, + bool dedicated_entry, u16 *index); +void mlx5_rl_remove_rate_raw(struct mlx5_core_dev *dev, u16 index); bool mlx5_rl_are_equal(struct mlx5_rate_limit *rl_0, struct mlx5_rate_limit *rl_1); int mlx5_alloc_bfreg(struct mlx5_core_dev *mdev, struct mlx5_sq_bfreg *bfreg, diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index ff8c9d527bb4..7d89ab64b372 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -810,7 +810,9 @@ struct mlx5_ifc_qos_cap_bits { u8 reserved_at_4[0x1]; u8 packet_pacing_burst_bound[0x1]; u8 packet_pacing_typical_size[0x1]; - u8 reserved_at_7[0x19]; + u8 reserved_at_7[0x4]; + u8 packet_pacing_uid[0x1]; + u8 reserved_at_c[0x14]; u8 reserved_at_20[0x20]; @@ -8262,9 +8264,20 @@ struct mlx5_ifc_set_pp_rate_limit_out_bits { u8 reserved_at_40[0x40]; }; +struct mlx5_ifc_set_pp_rate_limit_context_bits { + u8 rate_limit[0x20]; + + u8 burst_upper_bound[0x20]; + + u8 reserved_at_40[0x10]; + u8 typical_packet_size[0x10]; + + u8 reserved_at_60[0x120]; +}; + struct mlx5_ifc_set_pp_rate_limit_in_bits { u8 opcode[0x10]; - u8 reserved_at_10[0x10]; + u8 uid[0x10]; u8 reserved_at_20[0x10]; u8 op_mod[0x10]; @@ -8274,14 +8287,7 @@ struct mlx5_ifc_set_pp_rate_limit_in_bits { u8 reserved_at_60[0x20]; - u8 rate_limit[0x20]; - - u8 burst_upper_bound[0x20]; - - u8 reserved_at_c0[0x10]; - u8 typical_packet_size[0x10]; - - u8 reserved_at_e0[0x120]; + struct mlx5_ifc_set_pp_rate_limit_context_bits ctx; }; struct mlx5_ifc_access_register_out_bits { -- cgit v1.2.3 From 6a726824aaa3adaaf3bcfca3b471408e225f33d6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Thu, 5 Mar 2020 00:00:39 +0200 Subject: spi: Do spi_take_timestamp_pre for as many times as necessary When dealing with a SPI controller driver that is sending more than 1 byte at once (or the entire buffer at once), and the SPI peripheral driver has requested timestamping for a byte in the middle of the buffer, we find that spi_take_timestamp_pre never records a "pre" timestamp. This happens because the function currently expects to be called with the "progress" argument >= to what the peripheral has requested to be timestamped. But clearly there are cases when that isn't going to fly. And since we can't change the past when we realize that the opportunity to take a "pre" timestamp has just passed and there isn't going to be another one, the approach taken is to keep recording the "pre" timestamp on each call, overwriting the previously recorded one until the "post" timestamp is also taken. Signed-off-by: Vladimir Oltean Link: https://lore.kernel.org/r/20200304220044.11193-8-olteanv@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi.c | 19 +++++++------------ include/linux/spi/spi.h | 3 +-- 2 files changed, 8 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 292f26807b41..6c223f7d1ddc 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1515,17 +1515,15 @@ void spi_take_timestamp_pre(struct spi_controller *ctlr, if (!xfer->ptp_sts) return; - if (xfer->timestamped_pre) + if (xfer->timestamped) return; - if (progress < xfer->ptp_sts_word_pre) + if (progress > xfer->ptp_sts_word_pre) return; /* Capture the resolution of the timestamp */ xfer->ptp_sts_word_pre = progress; - xfer->timestamped_pre = true; - if (irqs_off) { local_irq_save(ctlr->irq_flags); preempt_disable(); @@ -1554,7 +1552,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr, if (!xfer->ptp_sts) return; - if (xfer->timestamped_post) + if (xfer->timestamped) return; if (progress < xfer->ptp_sts_word_post) @@ -1570,7 +1568,7 @@ void spi_take_timestamp_post(struct spi_controller *ctlr, /* Capture the resolution of the timestamp */ xfer->ptp_sts_word_post = progress; - xfer->timestamped_post = true; + xfer->timestamped = true; } EXPORT_SYMBOL_GPL(spi_take_timestamp_post); @@ -1675,12 +1673,9 @@ void spi_finalize_current_message(struct spi_controller *ctlr) } } - if (unlikely(ctlr->ptp_sts_supported)) { - list_for_each_entry(xfer, &mesg->transfers, transfer_list) { - WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_pre); - WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped_post); - } - } + if (unlikely(ctlr->ptp_sts_supported)) + list_for_each_entry(xfer, &mesg->transfers, transfer_list) + WARN_ON_ONCE(xfer->ptp_sts && !xfer->timestamped); spi_unmap_msg(ctlr, mesg); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 600e3793303e..87105272879b 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -933,8 +933,7 @@ struct spi_transfer { struct ptp_system_timestamp *ptp_sts; - bool timestamped_pre; - bool timestamped_post; + bool timestamped; struct list_head transfer_list; }; -- cgit v1.2.3 From 95cddcb5cc202d3f2499596b9af5b77536c5f86a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 4 Mar 2020 21:15:31 -0800 Subject: ethtool: add infrastructure for centralized checking of coalescing parameters Linux supports 22 different interrupt coalescing parameters. No driver implements them all. Some drivers just ignore the ones they don't support, while others have to carry a long list of checks to reject unsupported settings. To simplify the drivers add the ability to specify inside ethtool_ops which parameters are supported and let the core reject attempts to set any other one. This commit makes the mechanism an opt-in, only drivers which set ethtool_opts->coalesce_types to a non-zero value will have the checks enforced. The same mask is used for global and per queue settings. v3: - move the (temporary) check if driver defines types earlier (Michal) - rename used_types -> nonzero_params, and coalesce_types -> supported_coalesce_params (Alex) - use EOPNOTSUPP instead of EINVAL (Andrew, Michal) Leaving the long series of ifs for now, it seems nice to be able to grep for the field and flag names. This will probably have to be revisited once netlink support lands. Signed-off-by: Jakub Kicinski Reviewed-by: Jacob Keller Reviewed-by: Michal Kubecek Reviewed-by: Andrew Lunn Reviewed-by: Alexander Duyck Signed-off-by: David S. Miller --- include/linux/ethtool.h | 45 ++++++++++++++++++++++++++++++--- net/ethtool/ioctl.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 23373978cb3c..e464c946bca4 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -177,8 +177,44 @@ void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst, bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, const unsigned long *src); +#define ETHTOOL_COALESCE_RX_USECS BIT(0) +#define ETHTOOL_COALESCE_RX_MAX_FRAMES BIT(1) +#define ETHTOOL_COALESCE_RX_USECS_IRQ BIT(2) +#define ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ BIT(3) +#define ETHTOOL_COALESCE_TX_USECS BIT(4) +#define ETHTOOL_COALESCE_TX_MAX_FRAMES BIT(5) +#define ETHTOOL_COALESCE_TX_USECS_IRQ BIT(6) +#define ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ BIT(7) +#define ETHTOOL_COALESCE_STATS_BLOCK_USECS BIT(8) +#define ETHTOOL_COALESCE_USE_ADAPTIVE_RX BIT(9) +#define ETHTOOL_COALESCE_USE_ADAPTIVE_TX BIT(10) +#define ETHTOOL_COALESCE_PKT_RATE_LOW BIT(11) +#define ETHTOOL_COALESCE_RX_USECS_LOW BIT(12) +#define ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW BIT(13) +#define ETHTOOL_COALESCE_TX_USECS_LOW BIT(14) +#define ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW BIT(15) +#define ETHTOOL_COALESCE_PKT_RATE_HIGH BIT(16) +#define ETHTOOL_COALESCE_RX_USECS_HIGH BIT(17) +#define ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH BIT(18) +#define ETHTOOL_COALESCE_TX_USECS_HIGH BIT(19) +#define ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH BIT(20) +#define ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL BIT(21) + +#define ETHTOOL_COALESCE_USECS \ + (ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS) +#define ETHTOOL_COALESCE_MAX_FRAMES \ + (ETHTOOL_COALESCE_RX_MAX_FRAMES | ETHTOOL_COALESCE_TX_MAX_FRAMES) +#define ETHTOOL_COALESCE_USECS_IRQ \ + (ETHTOOL_COALESCE_RX_USECS_IRQ | ETHTOOL_COALESCE_TX_USECS_IRQ) +#define ETHTOOL_COALESCE_MAX_FRAMES_IRQ \ + (ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | \ + ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ) +#define ETHTOOL_COALESCE_USE_ADAPTIVE \ + (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | ETHTOOL_COALESCE_USE_ADAPTIVE_TX) + /** * struct ethtool_ops - optional netdev operations + * @supported_coalesce_params: supported types of interrupt coalescing. * @get_drvinfo: Report driver/device information. Should only set the * @driver, @version, @fw_version and @bus_info fields. If not * implemented, the @driver and @bus_info fields will be filled in @@ -207,8 +243,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, * or zero. * @get_coalesce: Get interrupt coalescing parameters. Returns a negative * error code or zero. - * @set_coalesce: Set interrupt coalescing parameters. Returns a negative - * error code or zero. + * @set_coalesce: Set interrupt coalescing parameters. Supported coalescing + * types should be set in @supported_coalesce_params. + * Returns a negative error code or zero. * @get_ringparam: Report ring sizes * @set_ringparam: Set ring sizes. Returns a negative error code or zero. * @get_pauseparam: Report pause parameters @@ -292,7 +329,8 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, * @set_per_queue_coalesce: Set interrupt coalescing parameters per queue. * It must check that the given queue number is valid. If neither a RX nor * a TX queue has this number, return -EINVAL. If only a RX queue or a TX - * queue has this number, ignore the inapplicable fields. + * queue has this number, ignore the inapplicable fields. Supported + * coalescing types should be set in @supported_coalesce_params. * Returns a negative error code or zero. * @get_link_ksettings: Get various device settings including Ethernet link * settings. The %cmd and %link_mode_masks_nwords fields should be @@ -323,6 +361,7 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, * of the generic netdev features interface. */ struct ethtool_ops { + u32 supported_coalesce_params; void (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *); int (*get_regs_len)(struct net_device *); void (*get_regs)(struct net_device *, struct ethtool_regs *, void *); diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index f2fe8e5896dc..b2684ffa26de 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1544,6 +1544,64 @@ static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, return 0; } +static bool +ethtool_set_coalesce_supported(struct net_device *dev, + struct ethtool_coalesce *coalesce) +{ + u32 supported_params = dev->ethtool_ops->supported_coalesce_params; + u32 nonzero_params = 0; + + if (!supported_params) + return true; + + if (coalesce->rx_coalesce_usecs) + nonzero_params |= ETHTOOL_COALESCE_RX_USECS; + if (coalesce->rx_max_coalesced_frames) + nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES; + if (coalesce->rx_coalesce_usecs_irq) + nonzero_params |= ETHTOOL_COALESCE_RX_USECS_IRQ; + if (coalesce->rx_max_coalesced_frames_irq) + nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ; + if (coalesce->tx_coalesce_usecs) + nonzero_params |= ETHTOOL_COALESCE_TX_USECS; + if (coalesce->tx_max_coalesced_frames) + nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES; + if (coalesce->tx_coalesce_usecs_irq) + nonzero_params |= ETHTOOL_COALESCE_TX_USECS_IRQ; + if (coalesce->tx_max_coalesced_frames_irq) + nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ; + if (coalesce->stats_block_coalesce_usecs) + nonzero_params |= ETHTOOL_COALESCE_STATS_BLOCK_USECS; + if (coalesce->use_adaptive_rx_coalesce) + nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_RX; + if (coalesce->use_adaptive_tx_coalesce) + nonzero_params |= ETHTOOL_COALESCE_USE_ADAPTIVE_TX; + if (coalesce->pkt_rate_low) + nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_LOW; + if (coalesce->rx_coalesce_usecs_low) + nonzero_params |= ETHTOOL_COALESCE_RX_USECS_LOW; + if (coalesce->rx_max_coalesced_frames_low) + nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW; + if (coalesce->tx_coalesce_usecs_low) + nonzero_params |= ETHTOOL_COALESCE_TX_USECS_LOW; + if (coalesce->tx_max_coalesced_frames_low) + nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW; + if (coalesce->pkt_rate_high) + nonzero_params |= ETHTOOL_COALESCE_PKT_RATE_HIGH; + if (coalesce->rx_coalesce_usecs_high) + nonzero_params |= ETHTOOL_COALESCE_RX_USECS_HIGH; + if (coalesce->rx_max_coalesced_frames_high) + nonzero_params |= ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH; + if (coalesce->tx_coalesce_usecs_high) + nonzero_params |= ETHTOOL_COALESCE_TX_USECS_HIGH; + if (coalesce->tx_max_coalesced_frames_high) + nonzero_params |= ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH; + if (coalesce->rate_sample_interval) + nonzero_params |= ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL; + + return (supported_params & nonzero_params) == nonzero_params; +} + static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr) { @@ -1555,6 +1613,9 @@ static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) return -EFAULT; + if (!ethtool_set_coalesce_supported(dev, &coalesce)) + return -EOPNOTSUPP; + return dev->ethtool_ops->set_coalesce(dev, &coalesce); } @@ -2336,6 +2397,11 @@ ethtool_set_per_queue_coalesce(struct net_device *dev, goto roll_back; } + if (!ethtool_set_coalesce_supported(dev, &coalesce)) { + ret = -EOPNOTSUPP; + goto roll_back; + } + ret = dev->ethtool_ops->set_per_queue_coalesce(dev, bit, &coalesce); if (ret != 0) goto roll_back; -- cgit v1.2.3 From 780d2a9c86dc12594e263752cd8426a5794f1cc8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 4 Mar 2020 15:09:19 +0100 Subject: include/bitmap.h: add missing parameter in docs bitmap_find_next_zero_area_off() has an additional parameter which was not specified in the list of functions. Add it. Fixes: 5e19b013f55a ("lib: bitmap: add alignment offset for bitmap_find_next_zero_area()") Signed-off-by: Wolfram Sang Signed-off-by: Dennis Zhou --- include/linux/bitmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index e52ceb1a73d3..804600f7dc35 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -50,7 +50,7 @@ * bitmap_set(dst, pos, nbits) Set specified bit area * bitmap_clear(dst, pos, nbits) Clear specified bit area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area - * bitmap_find_next_zero_area_off(buf, len, pos, n, mask) as above + * bitmap_find_next_zero_area_off(buf, len, pos, n, mask, mask_off) as above * bitmap_shift_right(dst, src, n, nbits) *dst = *src >> n * bitmap_shift_left(dst, src, n, nbits) *dst = *src << n * bitmap_cut(dst, src, first, n, nbits) Cut n bits from first, copy rest -- cgit v1.2.3 From a392d26f32cdd87e09b1ea3849db79cfc4eae745 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 4 Mar 2020 15:09:20 +0100 Subject: include/bitmap.h: add new functions to documentation I found these functions only by chance although I was looking exactly for something like them. So, add them to the list of functions to make them more visible. Fixes: e837dfde15a4 ("bitmap: genericize percpu bitmap region iterators") Signed-off-by: Wolfram Sang Signed-off-by: Dennis Zhou --- include/linux/bitmap.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 804600f7dc35..99058eb81042 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -51,6 +51,12 @@ * bitmap_clear(dst, pos, nbits) Clear specified bit area * bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area * bitmap_find_next_zero_area_off(buf, len, pos, n, mask, mask_off) as above + * bitmap_next_clear_region(map, &start, &end, nbits) Find next clear region + * bitmap_next_set_region(map, &start, &end, nbits) Find next set region + * bitmap_for_each_clear_region(map, rs, re, start, end) + * Iterate over all clear regions + * bitmap_for_each_set_region(map, rs, re, start, end) + * Iterate over all set regions * bitmap_shift_right(dst, src, n, nbits) *dst = *src >> n * bitmap_shift_left(dst, src, n, nbits) *dst = *src << n * bitmap_cut(dst, src, first, n, nbits) Cut n bits from first, copy rest -- cgit v1.2.3 From 60d70d75c12db7979da56cf02b4c8a4fcb8efb46 Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Thu, 27 Feb 2020 21:42:36 +0100 Subject: media: v4l2: Switch to new kerneldoc syntax for named variable macro arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new syntax is available since commit 43756e347f21 ("scripts/kernel-doc: Add support for named variable macro arguments"). The same HTML output is produced with and without this patch. Signed-off-by: Jonathan Neuschäfer Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-device.h | 16 ++++++++-------- include/media/v4l2-subdev.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h index 95353ae476a1..7c912b7d2870 100644 --- a/include/media/v4l2-device.h +++ b/include/media/v4l2-device.h @@ -240,7 +240,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -265,7 +265,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -293,7 +293,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * @@ -328,7 +328,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * @@ -359,7 +359,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -388,7 +388,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * @@ -419,7 +419,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Ignore any errors. * @@ -447,7 +447,7 @@ static inline bool v4l2_device_supports_requests(struct v4l2_device *v4l2_dev) * @f: operation function that will be called if @cond matches. * The operation functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Return: * diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 761aa83a3f3c..a4848de59852 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -1093,7 +1093,7 @@ extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers; * @f: callback function to be called. * The callback functions are defined in groups, according to * each element at &struct v4l2_subdev_ops. - * @args...: arguments for @f. + * @args: arguments for @f. * * Example: err = v4l2_subdev_call(sd, video, s_std, norm); */ -- cgit v1.2.3 From 2b48e113866a6735de3a99531183afb6217c2a60 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 3 Mar 2020 15:33:17 +0100 Subject: media: v4l2-mem2mem: handle draining, stopped and next-buf-is-last states Since the draining and stop phase of the HW decoder mem2mem bahaviour is now clearly defined, we can move handling of the following states to the common v4l2-mem2mem core code: - draining - stopped - next-buf-is-last By introducing the following v4l2-mem2mem APIs: - v4l2_m2m_encoder_cmd/v4l2_m2m_ioctl_encoder_cmd to handle start/stop command - v4l2_m2m_decoder_cmd/v4l2_m2m_ioctl_decoder_cmd to handle start/stop command - v4l2_m2m_update_start_streaming_state to update state on start of streaming of the de/encoder queue - v4l2_m2m_update_stop_streaming_state to update state on stop of streaming of the de/encoder queue - v4l2_m2m_last_buffer_done to make the current dest buffer as the last one And inline helpers: - v4l2_m2m_mark_stopped to mark the de/encoding process as stopped - v4l2_m2m_clear_state to clear the de/encoding state - v4l2_m2m_dst_buf_is_last to detect the current dequeued dst_buf is the last - v4l2_m2m_has_stopped to detect the de/encoding stopped state - v4l2_m2m_is_last_draining_src_buf to detect the current source buffer should be the last processing before stopping the de/encoding process The special next-buf-is-last when min_buffers != 1 case is also handled in v4l2_m2m_qbuf() by reusing the other introduced APIs. This state management has been stolen from the vicodec implementation, and is no-op for drivers not calling the v4l2_m2m_encoder_cmd or v4l2_m2m_decoder_cmd and v4l2_m2m_update_start/stop_streaming_state. The vicodec will be the first one to be converted as an example. Signed-off-by: Neil Armstrong Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 221 ++++++++++++++++++++++++++++++++- include/media/v4l2-mem2mem.h | 133 ++++++++++++++++++++ 2 files changed, 352 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index cc34c5ab7009..8986c31176e9 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -340,6 +340,11 @@ static void __v4l2_m2m_try_queue(struct v4l2_m2m_dev *m2m_dev, m2m_ctx->new_frame = !dst->vb2_buf.copied_timestamp || dst->vb2_buf.timestamp != src->vb2_buf.timestamp; + if (m2m_ctx->has_stopped) { + dprintk("Device has stopped\n"); + goto job_unlock; + } + if (m2m_dev->m2m_ops->job_ready && (!m2m_dev->m2m_ops->job_ready(m2m_ctx->priv))) { dprintk("Driver not ready\n"); @@ -556,6 +561,140 @@ int v4l2_m2m_querybuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, } EXPORT_SYMBOL_GPL(v4l2_m2m_querybuf); +/* + * This will add the LAST flag and mark the buffer management + * state as stopped. + * This is called when the last capture buffer must be flagged as LAST + * in draining mode from the encoder/decoder driver buf_queue() callback + * or from v4l2_update_last_buf_state() when a capture buffer is available. + */ +void v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + vbuf->flags |= V4L2_BUF_FLAG_LAST; + vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE); + + v4l2_m2m_mark_stopped(m2m_ctx); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_last_buffer_done); + +/* When stop command is issued, update buffer management state */ +static int v4l2_update_last_buf_state(struct v4l2_m2m_ctx *m2m_ctx) +{ + struct vb2_v4l2_buffer *next_dst_buf; + + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + return 0; + + m2m_ctx->last_src_buf = v4l2_m2m_last_src_buf(m2m_ctx); + m2m_ctx->is_draining = true; + + /* + * The processing of the last output buffer queued before + * the STOP command is expected to mark the buffer management + * state as stopped with v4l2_m2m_mark_stopped(). + */ + if (m2m_ctx->last_src_buf) + return 0; + + /* + * In case the output queue is empty, try to mark the last capture + * buffer as LAST. + */ + next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!next_dst_buf) { + /* + * Wait for the next queued one in encoder/decoder driver + * buf_queue() callback using the v4l2_m2m_dst_buf_is_last() + * helper or in v4l2_m2m_qbuf() if encoder/decoder is not yet + * streaming. + */ + m2m_ctx->next_buf_last = true; + return 0; + } + + v4l2_m2m_last_buffer_done(m2m_ctx, next_dst_buf); + + return 0; +} + +/* + * Updates the encoding/decoding buffer management state, should + * be called from encoder/decoder drivers start_streaming() + */ +void v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q) +{ + /* If start streaming again, untag the last output buffer */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) + m2m_ctx->last_src_buf = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_update_start_streaming_state); + +/* + * Updates the encoding/decoding buffer management state, should + * be called from encoder/decoder driver stop_streaming() + */ +void v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q) +{ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + /* + * If in draining state, either mark next dst buffer as + * done or flag next one to be marked as done either + * in encoder/decoder driver buf_queue() callback using + * the v4l2_m2m_dst_buf_is_last() helper or in v4l2_m2m_qbuf() + * if encoder/decoder is not yet streaming + */ + if (m2m_ctx->is_draining) { + struct vb2_v4l2_buffer *next_dst_buf; + + m2m_ctx->last_src_buf = NULL; + next_dst_buf = v4l2_m2m_dst_buf_remove(m2m_ctx); + if (!next_dst_buf) + m2m_ctx->next_buf_last = true; + else + v4l2_m2m_last_buffer_done(m2m_ctx, + next_dst_buf); + } + } else { + v4l2_m2m_clear_state(m2m_ctx); + } +} +EXPORT_SYMBOL_GPL(v4l2_m2m_update_stop_streaming_state); + +static void v4l2_m2m_force_last_buf_done(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q) +{ + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vbuf; + unsigned int i; + + if (WARN_ON(q->is_output)) + return; + if (list_empty(&q->queued_list)) + return; + + vb = list_first_entry(&q->queued_list, struct vb2_buffer, queued_entry); + for (i = 0; i < vb->num_planes; i++) + vb2_set_plane_payload(vb, i, 0); + + /* + * Since the buffer hasn't been queued to the ready queue, + * mark is active and owned before marking it LAST and DONE + */ + vb->state = VB2_BUF_STATE_ACTIVE; + atomic_inc(&q->owned_by_drv_count); + + vbuf = to_vb2_v4l2_buffer(vb); + vbuf->field = V4L2_FIELD_NONE; + + v4l2_m2m_last_buffer_done(m2m_ctx, vbuf); +} + int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, struct v4l2_buffer *buf) { @@ -570,11 +709,25 @@ int v4l2_m2m_qbuf(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, __func__); return -EPERM; } + ret = vb2_qbuf(vq, vdev->v4l2_dev->mdev, buf); - if (!ret && !(buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) + if (ret) + return ret; + + /* + * If the capture queue is streaming, but streaming hasn't started + * on the device, but was asked to stop, mark the previously queued + * buffer as DONE with LAST flag since it won't be queued on the + * device. + */ + if (!V4L2_TYPE_IS_OUTPUT(vq->type) && + vb2_is_streaming(vq) && !vb2_start_streaming_called(vq) && + (v4l2_m2m_has_stopped(m2m_ctx) || v4l2_m2m_dst_buf_is_last(m2m_ctx))) + v4l2_m2m_force_last_buf_done(m2m_ctx, vq); + else if (!(buf->flags & V4L2_BUF_FLAG_IN_REQUEST)) v4l2_m2m_try_schedule(m2m_ctx); - return ret; + return 0; } EXPORT_SYMBOL_GPL(v4l2_m2m_qbuf); @@ -1225,6 +1378,70 @@ int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, } EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_try_decoder_cmd); +/* + * Updates the encoding state on ENC_CMD_STOP/ENC_CMD_START + * Should be called from the encoder driver encoder_cmd() callback + */ +int v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_encoder_cmd *ec) +{ + if (ec->cmd != V4L2_ENC_CMD_STOP && ec->cmd != V4L2_ENC_CMD_START) + return -EINVAL; + + if (ec->cmd == V4L2_ENC_CMD_STOP) + return v4l2_update_last_buf_state(m2m_ctx); + + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + m2m_ctx->has_stopped = false; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_encoder_cmd); + +/* + * Updates the decoding state on DEC_CMD_STOP/DEC_CMD_START + * Should be called from the decoder driver decoder_cmd() callback + */ +int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_decoder_cmd *dc) +{ + if (dc->cmd != V4L2_DEC_CMD_STOP && dc->cmd != V4L2_DEC_CMD_START) + return -EINVAL; + + if (dc->cmd == V4L2_DEC_CMD_STOP) + return v4l2_update_last_buf_state(m2m_ctx); + + if (m2m_ctx->is_draining) + return -EBUSY; + + if (m2m_ctx->has_stopped) + m2m_ctx->has_stopped = false; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_m2m_decoder_cmd); + +int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *ec) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_encoder_cmd(file, fh->m2m_ctx, ec); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_encoder_cmd); + +int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *dc) +{ + struct v4l2_fh *fh = file->private_data; + + return v4l2_m2m_decoder_cmd(file, fh->m2m_ctx, dc); +} +EXPORT_SYMBOL_GPL(v4l2_m2m_ioctl_decoder_cmd); + int v4l2_m2m_ioctl_stateless_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dc) { diff --git a/include/media/v4l2-mem2mem.h b/include/media/v4l2-mem2mem.h index 1d85e24791e4..98753f00df7e 100644 --- a/include/media/v4l2-mem2mem.h +++ b/include/media/v4l2-mem2mem.h @@ -80,6 +80,10 @@ struct v4l2_m2m_queue_ctx { * for an existing frame. This is always true unless * V4L2_BUF_CAP_SUPPORTS_M2M_HOLD_CAPTURE_BUF is set, which * indicates slicing support. + * @is_draining: indicates device is in draining phase + * @last_src_buf: indicate the last source buffer for draining + * @next_buf_last: next capture queud buffer will be tagged as last + * @has_stopped: indicate the device has been stopped * @m2m_dev: opaque pointer to the internal data to handle M2M context * @cap_q_ctx: Capture (output to memory) queue context * @out_q_ctx: Output (input from memory) queue context @@ -98,6 +102,11 @@ struct v4l2_m2m_ctx { bool new_frame; + bool is_draining; + struct vb2_v4l2_buffer *last_src_buf; + bool next_buf_last; + bool has_stopped; + /* internal use only */ struct v4l2_m2m_dev *m2m_dev; @@ -215,6 +224,86 @@ v4l2_m2m_buf_done(struct vb2_v4l2_buffer *buf, enum vb2_buffer_state state) vb2_buffer_done(&buf->vb2_buf, state); } +/** + * v4l2_m2m_clear_state() - clear encoding/decoding state + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void +v4l2_m2m_clear_state(struct v4l2_m2m_ctx *m2m_ctx) +{ + m2m_ctx->next_buf_last = false; + m2m_ctx->is_draining = false; + m2m_ctx->has_stopped = false; +} + +/** + * v4l2_m2m_mark_stopped() - set current encoding/decoding state as stopped + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline void +v4l2_m2m_mark_stopped(struct v4l2_m2m_ctx *m2m_ctx) +{ + m2m_ctx->next_buf_last = false; + m2m_ctx->is_draining = false; + m2m_ctx->has_stopped = true; +} + +/** + * v4l2_m2m_dst_buf_is_last() - return the current encoding/decoding session + * draining management state of next queued capture buffer + * + * This last capture buffer should be tagged with V4L2_BUF_FLAG_LAST to notify + * the end of the capture session. + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline bool +v4l2_m2m_dst_buf_is_last(struct v4l2_m2m_ctx *m2m_ctx) +{ + return m2m_ctx->is_draining && m2m_ctx->next_buf_last; +} + +/** + * v4l2_m2m_has_stopped() - return the current encoding/decoding session + * stopped state + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + */ +static inline bool +v4l2_m2m_has_stopped(struct v4l2_m2m_ctx *m2m_ctx) +{ + return m2m_ctx->has_stopped; +} + +/** + * v4l2_m2m_is_last_draining_src_buf() - return the output buffer draining + * state in the current encoding/decoding session + * + * This will identify the last output buffer queued before a session stop + * was required, leading to an actual encoding/decoding session stop state + * in the encoding/decoding process after being processed. + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @vbuf: pointer to struct &v4l2_buffer + */ +static inline bool +v4l2_m2m_is_last_draining_src_buf(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf) +{ + return m2m_ctx->is_draining && vbuf == m2m_ctx->last_src_buf; +} + +/** + * v4l2_m2m_last_buffer_done() - marks the buffer with LAST flag and DONE + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @vbuf: pointer to struct &v4l2_buffer + */ +void v4l2_m2m_last_buffer_done(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_v4l2_buffer *vbuf); + /** * v4l2_m2m_reqbufs() - multi-queue-aware REQBUFS multiplexer * @@ -312,6 +401,46 @@ int v4l2_m2m_streamon(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, int v4l2_m2m_streamoff(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, enum v4l2_buf_type type); +/** + * v4l2_m2m_update_start_streaming_state() - update the encoding/decoding + * session state when a start of streaming of a video queue is requested + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @q: queue + */ +void v4l2_m2m_update_start_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q); + +/** + * v4l2_m2m_update_stop_streaming_state() - update the encoding/decoding + * session state when a stop of streaming of a video queue is requested + * + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @q: queue + */ +void v4l2_m2m_update_stop_streaming_state(struct v4l2_m2m_ctx *m2m_ctx, + struct vb2_queue *q); + +/** + * v4l2_m2m_encoder_cmd() - execute an encoder command + * + * @file: pointer to struct &file + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @ec: pointer to the encoder command + */ +int v4l2_m2m_encoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_encoder_cmd *ec); + +/** + * v4l2_m2m_decoder_cmd() - execute a decoder command + * + * @file: pointer to struct &file + * @m2m_ctx: m2m context assigned to the instance given by struct &v4l2_m2m_ctx + * @dc: pointer to the decoder command + */ +int v4l2_m2m_decoder_cmd(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, + struct v4l2_decoder_cmd *dc); + /** * v4l2_m2m_poll() - poll replacement, for destination buffers only * @@ -704,6 +833,10 @@ int v4l2_m2m_ioctl_streamon(struct file *file, void *fh, enum v4l2_buf_type type); int v4l2_m2m_ioctl_streamoff(struct file *file, void *fh, enum v4l2_buf_type type); +int v4l2_m2m_ioctl_encoder_cmd(struct file *file, void *fh, + struct v4l2_encoder_cmd *ec); +int v4l2_m2m_ioctl_decoder_cmd(struct file *file, void *fh, + struct v4l2_decoder_cmd *dc); int v4l2_m2m_ioctl_try_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *ec); int v4l2_m2m_ioctl_try_decoder_cmd(struct file *file, void *fh, -- cgit v1.2.3 From aaca9408078914380fbfd8aef3c38a34b515a654 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Thu, 5 Mar 2020 09:16:40 +0200 Subject: net: sched: Make FIFO Qdisc offloadable Invoke ndo_setup_tc() as appropriate to signal init / replacement, destroying and dumping of pFIFO / bFIFO Qdisc. A lot of the FIFO logic is used for pFIFO_head_drop as well, but that's a semantically very different Qdisc that isn't really in the same boat as pFIFO / bFIFO. Split some of the functions to keep the Qdisc intact. Signed-off-by: Petr Machata Signed-off-by: Ido Schimmel Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/pkt_cls.h | 15 ++++++++ net/sched/sch_fifo.c | 97 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 107 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b6fedd54cd8e..654808bfad83 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -853,6 +853,7 @@ enum tc_setup_type { TC_SETUP_FT, TC_SETUP_QDISC_ETS, TC_SETUP_QDISC_TBF, + TC_SETUP_QDISC_FIFO, }; /* These structures hold the attributes of bpf state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 53946b509b51..341a66af8d59 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -881,4 +881,19 @@ struct tc_tbf_qopt_offload { }; }; +enum tc_fifo_command { + TC_FIFO_REPLACE, + TC_FIFO_DESTROY, + TC_FIFO_STATS, +}; + +struct tc_fifo_qopt_offload { + enum tc_fifo_command command; + u32 handle; + u32 parent; + union { + struct tc_qopt_offload_stats stats; + }; +}; + #endif diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index 37c8aa75d70c..a579a4131d22 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -12,6 +12,7 @@ #include #include #include +#include /* 1 band FIFO pseudo-"scheduler" */ @@ -51,8 +52,49 @@ static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch, return NET_XMIT_CN; } -static int fifo_init(struct Qdisc *sch, struct nlattr *opt, - struct netlink_ext_ack *extack) +static void fifo_offload_init(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct tc_fifo_qopt_offload qopt; + + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) + return; + + qopt.command = TC_FIFO_REPLACE; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt); +} + +static void fifo_offload_destroy(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct tc_fifo_qopt_offload qopt; + + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) + return; + + qopt.command = TC_FIFO_DESTROY; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_FIFO, &qopt); +} + +static int fifo_offload_dump(struct Qdisc *sch) +{ + struct tc_fifo_qopt_offload qopt; + + qopt.command = TC_FIFO_STATS; + qopt.handle = sch->handle; + qopt.parent = sch->parent; + qopt.stats.bstats = &sch->bstats; + qopt.stats.qstats = &sch->qstats; + + return qdisc_offload_dump_helper(sch, TC_SETUP_QDISC_FIFO, &qopt); +} + +static int __fifo_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) { bool bypass; bool is_bfifo = sch->ops == &bfifo_qdisc_ops; @@ -82,10 +124,35 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt, sch->flags |= TCQ_F_CAN_BYPASS; else sch->flags &= ~TCQ_F_CAN_BYPASS; + return 0; } -static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb) +static int fifo_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) +{ + int err; + + err = __fifo_init(sch, opt, extack); + if (err) + return err; + + fifo_offload_init(sch); + return 0; +} + +static int fifo_hd_init(struct Qdisc *sch, struct nlattr *opt, + struct netlink_ext_ack *extack) +{ + return __fifo_init(sch, opt, extack); +} + +static void fifo_destroy(struct Qdisc *sch) +{ + fifo_offload_destroy(sch); +} + +static int __fifo_dump(struct Qdisc *sch, struct sk_buff *skb) { struct tc_fifo_qopt opt = { .limit = sch->limit }; @@ -97,6 +164,22 @@ nla_put_failure: return -1; } +static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + int err; + + err = fifo_offload_dump(sch); + if (err) + return err; + + return __fifo_dump(sch, skb); +} + +static int fifo_hd_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + return __fifo_dump(sch, skb); +} + struct Qdisc_ops pfifo_qdisc_ops __read_mostly = { .id = "pfifo", .priv_size = 0, @@ -104,6 +187,7 @@ struct Qdisc_ops pfifo_qdisc_ops __read_mostly = { .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, .init = fifo_init, + .destroy = fifo_destroy, .reset = qdisc_reset_queue, .change = fifo_init, .dump = fifo_dump, @@ -118,6 +202,7 @@ struct Qdisc_ops bfifo_qdisc_ops __read_mostly = { .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, .init = fifo_init, + .destroy = fifo_destroy, .reset = qdisc_reset_queue, .change = fifo_init, .dump = fifo_dump, @@ -131,10 +216,10 @@ struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = { .enqueue = pfifo_tail_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, - .init = fifo_init, + .init = fifo_hd_init, .reset = qdisc_reset_queue, - .change = fifo_init, - .dump = fifo_dump, + .change = fifo_hd_init, + .dump = fifo_hd_dump, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 5a8a076506af03f83f83d80412a7c0b06b687ee1 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 24 Feb 2020 10:21:00 -0600 Subject: crypto: Replace zero-length array with flexible-array member MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Signed-off-by: Gustavo A. R. Silva Reviewed-by: Horia Geantă Signed-off-by: Herbert Xu --- drivers/crypto/caam/caamalg.c | 2 +- drivers/crypto/caam/caamalg_qi.c | 4 ++-- drivers/crypto/caam/caamalg_qi2.h | 6 +++--- drivers/crypto/caam/caamhash.c | 2 +- drivers/crypto/cavium/nitrox/nitrox_main.c | 2 +- drivers/crypto/chelsio/chcr_core.h | 2 +- drivers/crypto/mediatek/mtk-sha.c | 2 +- drivers/crypto/nx/nx.h | 2 +- drivers/crypto/omap-sham.c | 4 ++-- include/crypto/if_alg.h | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c index 03797f9b1050..b7bb7c30adeb 100644 --- a/drivers/crypto/caam/caamalg.c +++ b/drivers/crypto/caam/caamalg.c @@ -909,7 +909,7 @@ struct skcipher_edesc { bool bklog; dma_addr_t sec4_sg_dma; struct sec4_sg_entry *sec4_sg; - u32 hw_desc[0]; + u32 hw_desc[]; }; static void caam_unmap(struct device *dev, struct scatterlist *src, diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c index 4a29e0ef9d63..27e36bdf6163 100644 --- a/drivers/crypto/caam/caamalg_qi.c +++ b/drivers/crypto/caam/caamalg_qi.c @@ -783,7 +783,7 @@ struct aead_edesc { unsigned int assoclen; dma_addr_t assoclen_dma; struct caam_drv_req drv_req; - struct qm_sg_entry sgt[0]; + struct qm_sg_entry sgt[]; }; /* @@ -803,7 +803,7 @@ struct skcipher_edesc { int qm_sg_bytes; dma_addr_t qm_sg_dma; struct caam_drv_req drv_req; - struct qm_sg_entry sgt[0]; + struct qm_sg_entry sgt[]; }; static struct caam_drv_ctx *get_drv_ctx(struct caam_ctx *ctx, diff --git a/drivers/crypto/caam/caamalg_qi2.h b/drivers/crypto/caam/caamalg_qi2.h index 706736776b47..f29cb7bd7dd3 100644 --- a/drivers/crypto/caam/caamalg_qi2.h +++ b/drivers/crypto/caam/caamalg_qi2.h @@ -114,7 +114,7 @@ struct aead_edesc { dma_addr_t qm_sg_dma; unsigned int assoclen; dma_addr_t assoclen_dma; - struct dpaa2_sg_entry sgt[0]; + struct dpaa2_sg_entry sgt[]; }; /* @@ -132,7 +132,7 @@ struct skcipher_edesc { dma_addr_t iv_dma; int qm_sg_bytes; dma_addr_t qm_sg_dma; - struct dpaa2_sg_entry sgt[0]; + struct dpaa2_sg_entry sgt[]; }; /* @@ -146,7 +146,7 @@ struct ahash_edesc { dma_addr_t qm_sg_dma; int src_nents; int qm_sg_bytes; - struct dpaa2_sg_entry sgt[0]; + struct dpaa2_sg_entry sgt[]; }; /** diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index 2fe852853d40..943bc0296267 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -536,7 +536,7 @@ struct ahash_edesc { int sec4_sg_bytes; bool bklog; u32 hw_desc[DESC_JOB_IO_LEN_MAX / sizeof(u32)] ____cacheline_aligned; - struct sec4_sg_entry sec4_sg[0]; + struct sec4_sg_entry sec4_sg[]; }; static inline void ahash_unmap(struct device *dev, diff --git a/drivers/crypto/cavium/nitrox/nitrox_main.c b/drivers/crypto/cavium/nitrox/nitrox_main.c index c4632d84c9a1..e91be9b8b083 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_main.c +++ b/drivers/crypto/cavium/nitrox/nitrox_main.c @@ -71,7 +71,7 @@ struct ucode { char version[VERSION_LEN - 1]; __be32 code_size; u8 raz[12]; - u64 code[0]; + u64 code[]; }; /** diff --git a/drivers/crypto/chelsio/chcr_core.h b/drivers/crypto/chelsio/chcr_core.h index e51c138b0a51..a4b9337cbf2c 100644 --- a/drivers/crypto/chelsio/chcr_core.h +++ b/drivers/crypto/chelsio/chcr_core.h @@ -68,7 +68,7 @@ struct _key_ctx { __be32 ctx_hdr; u8 salt[MAX_SALT]; __be64 iv_to_auth; - unsigned char key[0]; + unsigned char key[]; }; #define KEYCTX_TX_WR_IV_S 55 diff --git a/drivers/crypto/mediatek/mtk-sha.c b/drivers/crypto/mediatek/mtk-sha.c index 9e9f48bb7f85..bd6309e57ab8 100644 --- a/drivers/crypto/mediatek/mtk-sha.c +++ b/drivers/crypto/mediatek/mtk-sha.c @@ -107,7 +107,7 @@ struct mtk_sha_ctx { u8 id; u8 buf[SHA_BUF_SIZE] __aligned(sizeof(u32)); - struct mtk_sha_hmac_ctx base[0]; + struct mtk_sha_hmac_ctx base[]; }; struct mtk_sha_drv { diff --git a/drivers/crypto/nx/nx.h b/drivers/crypto/nx/nx.h index 91c54289124a..c6233173c612 100644 --- a/drivers/crypto/nx/nx.h +++ b/drivers/crypto/nx/nx.h @@ -37,7 +37,7 @@ struct max_sync_cop { u32 fc; u32 mode; u32 triplets; - struct msc_triplet trip[0]; + struct msc_triplet trip[]; } __packed; struct alg_props { diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 4f915a4ef5b0..e4072cd38585 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -159,7 +159,7 @@ struct omap_sham_reqctx { int sg_len; unsigned int total; /* total request */ - u8 buffer[0] OMAP_ALIGNED; + u8 buffer[] OMAP_ALIGNED; }; struct omap_sham_hmac_ctx { @@ -176,7 +176,7 @@ struct omap_sham_ctx { /* fallback stuff */ struct crypto_shash *fallback; - struct omap_sham_hmac_ctx base[0]; + struct omap_sham_hmac_ctx base[]; }; #define OMAP_SHAM_QUEUE_LENGTH 10 diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 24cfa96f98ea..56527c85d122 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -66,7 +66,7 @@ struct af_alg_sgl { struct af_alg_tsgl { struct list_head list; unsigned int cur; /* Last processed SG entry */ - struct scatterlist sg[0]; /* Array of SGs forming the SGL */ + struct scatterlist sg[]; /* Array of SGs forming the SGL */ }; #define MAX_SGL_ENTS ((4096 - sizeof(struct af_alg_tsgl)) / \ -- cgit v1.2.3 From acc670dba9f57298a6afd7cdfc9a7254292bbec9 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Wed, 26 Feb 2020 15:12:06 +0800 Subject: uacce: unmap remaining mmapping from user space When uacce parent device module is removed, user app may still keep the mmaped area, which can be accessed unsafely. When rmmod, Parent device driver will call uacce_remove, which unmap all remaining mapping from user space for safety. VM_FAULT_SIGBUS is also reported to user space accordingly. Suggested-by: Dave Jiang Signed-off-by: Zhangfei Gao Signed-off-by: Herbert Xu --- drivers/misc/uacce/uacce.c | 16 ++++++++++++++++ include/linux/uacce.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index ffced4da3c2c..d39307f060bd 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -224,6 +224,7 @@ static int uacce_fops_open(struct inode *inode, struct file *filep) init_waitqueue_head(&q->wait); filep->private_data = q; + uacce->inode = inode; q->state = UACCE_Q_INIT; return 0; @@ -253,6 +254,14 @@ static int uacce_fops_release(struct inode *inode, struct file *filep) return 0; } +static vm_fault_t uacce_vma_fault(struct vm_fault *vmf) +{ + if (vmf->flags & (FAULT_FLAG_MKWRITE | FAULT_FLAG_WRITE)) + return VM_FAULT_SIGBUS; + + return 0; +} + static void uacce_vma_close(struct vm_area_struct *vma) { struct uacce_queue *q = vma->vm_private_data; @@ -265,6 +274,7 @@ static void uacce_vma_close(struct vm_area_struct *vma) } static const struct vm_operations_struct uacce_vm_ops = { + .fault = uacce_vma_fault, .close = uacce_vma_close, }; @@ -556,6 +566,12 @@ void uacce_remove(struct uacce_device *uacce) if (!uacce) return; + /* + * unmap remaining mapping from user space, preventing user still + * access the mmaped area while parent device is already removed + */ + if (uacce->inode) + unmap_mapping_range(uacce->inode->i_mapping, 0, 0, 1); /* ensure no open queue remains */ mutex_lock(&uacce->mm_lock); diff --git a/include/linux/uacce.h b/include/linux/uacce.h index 904a461591a3..0e215e6d0534 100644 --- a/include/linux/uacce.h +++ b/include/linux/uacce.h @@ -98,6 +98,7 @@ struct uacce_queue { * @priv: private pointer of the uacce * @mm_list: list head of uacce_mm->list * @mm_lock: lock for mm_list + * @inode: core vfs */ struct uacce_device { const char *algs; @@ -113,6 +114,7 @@ struct uacce_device { void *priv; struct list_head mm_list; struct mutex mm_lock; + struct inode *inode; }; /** -- cgit v1.2.3 From 70c0923b0ef10b1c8d8f78fb50fcaef8eaae619d Mon Sep 17 00:00:00 2001 From: Jacob Keller Date: Mon, 2 Mar 2020 18:25:00 -0800 Subject: PCI: Introduce pci_get_dsn Several device drivers read their Device Serial Number from the PCIe extended config space. Introduce a new helper function, pci_get_dsn(). This function reads the eight bytes of the DSN and returns them as a u64. If the capability does not exist for the device, the function returns 0. Signed-off-by: Jacob Keller Cc: Bjorn Helgaas Cc: Jeff Kirsher Cc: Michael Chan Acked-by: Bjorn Helgaas Signed-off-by: David S. Miller --- drivers/pci/pci.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/pci.h | 5 +++++ 2 files changed, 39 insertions(+) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c16b0ba2a895..86821313c007 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -580,6 +580,40 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap) } EXPORT_SYMBOL_GPL(pci_find_ext_capability); +/** + * pci_get_dsn - Read and return the 8-byte Device Serial Number + * @dev: PCI device to query + * + * Looks up the PCI_EXT_CAP_ID_DSN and reads the 8 bytes of the Device Serial + * Number. + * + * Returns the DSN, or zero if the capability does not exist. + */ +u64 pci_get_dsn(struct pci_dev *dev) +{ + u32 dword; + u64 dsn; + int pos; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DSN); + if (!pos) + return 0; + + /* + * The Device Serial Number is two dwords offset 4 bytes from the + * capability position. The specification says that the first dword is + * the lower half, and the second dword is the upper half. + */ + pos += 4; + pci_read_config_dword(dev, pos, &dword); + dsn = (u64)dword; + pci_read_config_dword(dev, pos + 4, &dword); + dsn |= ((u64)dword) << 32; + + return dsn; +} +EXPORT_SYMBOL_GPL(pci_get_dsn); + static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap) { int rc, ttl = PCI_FIND_CAP_TTL; diff --git a/include/linux/pci.h b/include/linux/pci.h index 7beaf51e98ec..fc54b8922e66 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1052,6 +1052,8 @@ int pci_find_ht_capability(struct pci_dev *dev, int ht_cap); int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap); struct pci_bus *pci_find_next_bus(const struct pci_bus *from); +u64 pci_get_dsn(struct pci_dev *dev); + struct pci_dev *pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from); struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device, @@ -1707,6 +1709,9 @@ static inline int pci_find_next_capability(struct pci_dev *dev, u8 post, static inline int pci_find_ext_capability(struct pci_dev *dev, int cap) { return 0; } +static inline u64 pci_get_dsn(struct pci_dev *dev) +{ return 0; } + /* Power management related routines */ static inline int pci_save_state(struct pci_dev *dev) { return 0; } static inline void pci_restore_state(struct pci_dev *dev) { } -- cgit v1.2.3 From f400991bf872debffb01c46da882dc97d7e3248e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 19 Feb 2020 08:39:48 +0100 Subject: vt: switch vt_dont_switch to bool vt_dont_switch is pure boolean, no need for whole char. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20200219073951.16151-6-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt_ioctl.c | 6 +++--- include/linux/vt_kern.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c index 693d9d7ffb68..38948ac5fc49 100644 --- a/drivers/tty/vt/vt_ioctl.c +++ b/drivers/tty/vt/vt_ioctl.c @@ -39,7 +39,7 @@ #include #include -char vt_dont_switch; +bool vt_dont_switch; static inline bool vt_in_use(unsigned int i) { @@ -1026,12 +1026,12 @@ int vt_ioctl(struct tty_struct *tty, case VT_LOCKSWITCH: if (!capable(CAP_SYS_TTY_CONFIG)) return -EPERM; - vt_dont_switch = 1; + vt_dont_switch = true; break; case VT_UNLOCKSWITCH: if (!capable(CAP_SYS_TTY_CONFIG)) return -EPERM; - vt_dont_switch = 0; + vt_dont_switch = false; break; case VT_GETHIFONTMASK: ret = put_user(vc->vc_hi_font_mask, diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 8dc77e40bc03..ded5c48598f3 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -135,7 +135,7 @@ extern int do_unbind_con_driver(const struct consw *csw, int first, int last, int deflt); int vty_init(const struct file_operations *console_fops); -extern char vt_dont_switch; +extern bool vt_dont_switch; extern int default_utf8; extern int global_cursor_default; -- cgit v1.2.3 From a10df4910cdbce3c606846467c7e6fc2e469951d Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 19 Feb 2020 08:39:49 +0100 Subject: vt: vt_kern.h, remove extern from functions Unify the declarations of functions in vt_kern.h: some are with extern, some are not. Remove extern from the former as it is not needed for functions. Signed-off-by: Jiri Slaby Link: https://lore.kernel.org/r/20200219073951.16151-7-jslaby@suse.cz Signed-off-by: Greg Kroah-Hartman --- include/linux/vt_kern.h | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index ded5c48598f3..abf5bccf906a 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -28,8 +28,9 @@ #define BROKEN_GRAPHICS_PROGRAMS 1 #endif -extern void kd_mksound(unsigned int hz, unsigned int ticks); -extern int kbd_rate(struct kbd_repeat *rep); +void kd_mksound(unsigned int hz, unsigned int ticks); +int kbd_rate(struct kbd_repeat *rep); + extern int fg_console, last_console, want_console; /* console.c */ @@ -131,8 +132,8 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new); int vt_waitactive(int n); void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); -extern int do_unbind_con_driver(const struct consw *csw, int first, int last, - int deflt); +int do_unbind_con_driver(const struct consw *csw, int first, int last, + int deflt); int vty_init(const struct file_operations *console_fops); extern bool vt_dont_switch; @@ -146,7 +147,7 @@ struct vt_spawn_console { }; extern struct vt_spawn_console vt_spawn_con; -extern int vt_move_to_console(unsigned int vt, int alloc); +int vt_move_to_console(unsigned int vt, int alloc); /* Interfaces for VC notification of character events (for accessibility etc) */ @@ -155,35 +156,34 @@ struct vt_notifier_param { unsigned int c; /* Printed char */ }; -extern int register_vt_notifier(struct notifier_block *nb); -extern int unregister_vt_notifier(struct notifier_block *nb); +int register_vt_notifier(struct notifier_block *nb); +int unregister_vt_notifier(struct notifier_block *nb); -extern void hide_boot_cursor(bool hide); +void hide_boot_cursor(bool hide); /* keyboard provided interfaces */ -extern int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm); -extern int vt_do_kdskbmode(int console, unsigned int arg); -extern int vt_do_kdskbmeta(int console, unsigned int arg); -extern int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, - int perm); -extern int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, - int perm, int console); -extern int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, - int perm); -extern int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm); -extern int vt_do_kdgkbmode(int console); -extern int vt_do_kdgkbmeta(int console); -extern void vt_reset_unicode(int console); -extern int vt_get_shift_state(void); -extern void vt_reset_keyboard(int console); -extern int vt_get_leds(int console, int flag); -extern int vt_get_kbd_mode_bit(int console, int bit); -extern void vt_set_kbd_mode_bit(int console, int bit); -extern void vt_clr_kbd_mode_bit(int console, int bit); -extern void vt_set_led_state(int console, int leds); -extern void vt_set_led_state(int console, int leds); -extern void vt_kbd_con_start(int console); -extern void vt_kbd_con_stop(int console); +int vt_do_diacrit(unsigned int cmd, void __user *up, int eperm); +int vt_do_kdskbmode(int console, unsigned int arg); +int vt_do_kdskbmeta(int console, unsigned int arg); +int vt_do_kbkeycode_ioctl(int cmd, struct kbkeycode __user *user_kbkc, + int perm); +int vt_do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, + int console); +int vt_do_kdgkb_ioctl(int cmd, struct kbsentry __user *user_kdgkb, int perm); +int vt_do_kdskled(int console, int cmd, unsigned long arg, int perm); +int vt_do_kdgkbmode(int console); +int vt_do_kdgkbmeta(int console); +void vt_reset_unicode(int console); +int vt_get_shift_state(void); +void vt_reset_keyboard(int console); +int vt_get_leds(int console, int flag); +int vt_get_kbd_mode_bit(int console, int bit); +void vt_set_kbd_mode_bit(int console, int bit); +void vt_clr_kbd_mode_bit(int console, int bit); +void vt_set_led_state(int console, int leds); +void vt_set_led_state(int console, int leds); +void vt_kbd_con_start(int console); +void vt_kbd_con_stop(int console); void vc_scrolldelta_helper(struct vc_data *c, int lines, unsigned int rolled_over, void *_base, unsigned int size); -- cgit v1.2.3 From 8019ad13ef7f64be44d4f892af9c840179009254 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 4 Mar 2020 11:28:31 +0100 Subject: futex: Fix inode life-time issue As reported by Jann, ihold() does not in fact guarantee inode persistence. And instead of making it so, replace the usage of inode pointers with a per boot, machine wide, unique inode identifier. This sequence number is global, but shared (file backed) futexes are rare enough that this should not become a performance issue. Reported-by: Jann Horn Suggested-by: Linus Torvalds Signed-off-by: Peter Zijlstra (Intel) --- fs/inode.c | 1 + include/linux/fs.h | 1 + include/linux/futex.h | 17 ++++++---- kernel/futex.c | 89 ++++++++++++++++++++++++++++++--------------------- 4 files changed, 65 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/fs/inode.c b/fs/inode.c index 7d57068b6b7a..93d9252a00ab 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -138,6 +138,7 @@ int inode_init_always(struct super_block *sb, struct inode *inode) inode->i_sb = sb; inode->i_blkbits = sb->s_blocksize_bits; inode->i_flags = 0; + atomic64_set(&inode->i_sequence, 0); atomic_set(&inode->i_count, 1); inode->i_op = &empty_iops; inode->i_fop = &no_open_fops; diff --git a/include/linux/fs.h b/include/linux/fs.h index 3cd4fe6b845e..abedbffe2c9e 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -698,6 +698,7 @@ struct inode { struct rcu_head i_rcu; }; atomic64_t i_version; + atomic64_t i_sequence; /* see futex */ atomic_t i_count; atomic_t i_dio_count; atomic_t i_writecount; diff --git a/include/linux/futex.h b/include/linux/futex.h index 5cc3fed27d4c..b70df27d7e85 100644 --- a/include/linux/futex.h +++ b/include/linux/futex.h @@ -31,23 +31,26 @@ struct task_struct; union futex_key { struct { + u64 i_seq; unsigned long pgoff; - struct inode *inode; - int offset; + unsigned int offset; } shared; struct { + union { + struct mm_struct *mm; + u64 __tmp; + }; unsigned long address; - struct mm_struct *mm; - int offset; + unsigned int offset; } private; struct { + u64 ptr; unsigned long word; - void *ptr; - int offset; + unsigned int offset; } both; }; -#define FUTEX_KEY_INIT (union futex_key) { .both = { .ptr = NULL } } +#define FUTEX_KEY_INIT (union futex_key) { .both = { .ptr = 0ULL } } #ifdef CONFIG_FUTEX enum { diff --git a/kernel/futex.c b/kernel/futex.c index 0cf84c8664f2..e14f7cd45dbd 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -429,7 +429,7 @@ static void get_futex_key_refs(union futex_key *key) switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) { case FUT_OFF_INODE: - ihold(key->shared.inode); /* implies smp_mb(); (B) */ + smp_mb(); /* explicit smp_mb(); (B) */ break; case FUT_OFF_MMSHARED: futex_get_mm(key); /* implies smp_mb(); (B) */ @@ -463,7 +463,6 @@ static void drop_futex_key_refs(union futex_key *key) switch (key->both.offset & (FUT_OFF_INODE|FUT_OFF_MMSHARED)) { case FUT_OFF_INODE: - iput(key->shared.inode); break; case FUT_OFF_MMSHARED: mmdrop(key->private.mm); @@ -505,6 +504,46 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout, return timeout; } +/* + * Generate a machine wide unique identifier for this inode. + * + * This relies on u64 not wrapping in the life-time of the machine; which with + * 1ns resolution means almost 585 years. + * + * This further relies on the fact that a well formed program will not unmap + * the file while it has a (shared) futex waiting on it. This mapping will have + * a file reference which pins the mount and inode. + * + * If for some reason an inode gets evicted and read back in again, it will get + * a new sequence number and will _NOT_ match, even though it is the exact same + * file. + * + * It is important that match_futex() will never have a false-positive, esp. + * for PI futexes that can mess up the state. The above argues that false-negatives + * are only possible for malformed programs. + */ +static u64 get_inode_sequence_number(struct inode *inode) +{ + static atomic64_t i_seq; + u64 old; + + /* Does the inode already have a sequence number? */ + old = atomic64_read(&inode->i_sequence); + if (likely(old)) + return old; + + for (;;) { + u64 new = atomic64_add_return(1, &i_seq); + if (WARN_ON_ONCE(!new)) + continue; + + old = atomic64_cmpxchg_relaxed(&inode->i_sequence, 0, new); + if (old) + return old; + return new; + } +} + /** * get_futex_key() - Get parameters which are the keys for a futex * @uaddr: virtual address of the futex @@ -517,9 +556,15 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout, * * The key words are stored in @key on success. * - * For shared mappings, it's (page->index, file_inode(vma->vm_file), - * offset_within_page). For private mappings, it's (uaddr, current->mm). - * We can usually work out the index without swapping in the page. + * For shared mappings (when @fshared), the key is: + * ( inode->i_sequence, page->index, offset_within_page ) + * [ also see get_inode_sequence_number() ] + * + * For private mappings (or when !@fshared), the key is: + * ( current->mm, address, 0 ) + * + * This allows (cross process, where applicable) identification of the futex + * without keeping the page pinned for the duration of the FUTEX_WAIT. * * lock_page() might sleep, the caller should not hold a spinlock. */ @@ -659,8 +704,6 @@ again: key->private.mm = mm; key->private.address = address; - get_futex_key_refs(key); /* implies smp_mb(); (B) */ - } else { struct inode *inode; @@ -692,40 +735,14 @@ again: goto again; } - /* - * Take a reference unless it is about to be freed. Previously - * this reference was taken by ihold under the page lock - * pinning the inode in place so i_lock was unnecessary. The - * only way for this check to fail is if the inode was - * truncated in parallel which is almost certainly an - * application bug. In such a case, just retry. - * - * We are not calling into get_futex_key_refs() in file-backed - * cases, therefore a successful atomic_inc return below will - * guarantee that get_futex_key() will still imply smp_mb(); (B). - */ - if (!atomic_inc_not_zero(&inode->i_count)) { - rcu_read_unlock(); - put_page(page); - - goto again; - } - - /* Should be impossible but lets be paranoid for now */ - if (WARN_ON_ONCE(inode->i_mapping != mapping)) { - err = -EFAULT; - rcu_read_unlock(); - iput(inode); - - goto out; - } - key->both.offset |= FUT_OFF_INODE; /* inode-based key */ - key->shared.inode = inode; + key->shared.i_seq = get_inode_sequence_number(inode); key->shared.pgoff = basepage_index(tail); rcu_read_unlock(); } + get_futex_key_refs(key); /* implies smp_mb(); (B) */ + out: put_page(page); return err; -- cgit v1.2.3 From 5d0c9b0eb8ab2dd540919174abe75aa3b86b802f Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Thu, 13 Feb 2020 09:39:27 +0000 Subject: asm-generic/bitops: Update stale comment The comment in 'asm-generic/bitops.h' states that you should "recode these in the native assembly language, if at all possible". This is pretty crappy advice now that the generic implementation is defined in terms of atomic_long_t rather than a spinlock, so update the comment and hopefully save future architecture maintainers a bit of work. Reported-by: Stefan Asserhall Signed-off-by: Will Deacon Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20200213093927.1836-1-will@kernel.org --- include/asm-generic/bitops.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/asm-generic/bitops.h b/include/asm-generic/bitops.h index bfc96bf6606e..df9b5bc3d282 100644 --- a/include/asm-generic/bitops.h +++ b/include/asm-generic/bitops.h @@ -4,8 +4,9 @@ /* * For the benefit of those who are trying to port Linux to another - * architecture, here are some C-language equivalents. You should - * recode these in the native assembly language, if at all possible. + * architecture, here are some C-language equivalents. They should + * generate reasonable code, so take a look at what your compiler spits + * out before rolling your own buggy implementation in assembly language. * * C language equivalents written by Theodore Ts'o, 9/26/92 */ -- cgit v1.2.3 From 6e24628d78e4785385876125cba62315ca3b04b9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 13 Feb 2020 23:51:29 -0800 Subject: lib: Introduce generic min-heap Supports push, pop and converting an array into a heap. If the sense of the compare function is inverted then it can provide a max-heap. Based-on-work-by: Peter Zijlstra (Intel) Signed-off-by: Ian Rogers Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200214075133.181299-3-irogers@google.com --- include/linux/min_heap.h | 134 ++++++++++++++++++++++++++++++++ lib/Kconfig.debug | 10 +++ lib/Makefile | 1 + lib/test_min_heap.c | 194 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+) create mode 100644 include/linux/min_heap.h create mode 100644 lib/test_min_heap.c (limited to 'include') diff --git a/include/linux/min_heap.h b/include/linux/min_heap.h new file mode 100644 index 000000000000..44077837385f --- /dev/null +++ b/include/linux/min_heap.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_MIN_HEAP_H +#define _LINUX_MIN_HEAP_H + +#include +#include +#include + +/** + * struct min_heap - Data structure to hold a min-heap. + * @data: Start of array holding the heap elements. + * @nr: Number of elements currently in the heap. + * @size: Maximum number of elements that can be held in current storage. + */ +struct min_heap { + void *data; + int nr; + int size; +}; + +/** + * struct min_heap_callbacks - Data/functions to customise the min_heap. + * @elem_size: The nr of each element in bytes. + * @less: Partial order function for this heap. + * @swp: Swap elements function. + */ +struct min_heap_callbacks { + int elem_size; + bool (*less)(const void *lhs, const void *rhs); + void (*swp)(void *lhs, void *rhs); +}; + +/* Sift the element at pos down the heap. */ +static __always_inline +void min_heapify(struct min_heap *heap, int pos, + const struct min_heap_callbacks *func) +{ + void *left, *right, *parent, *smallest; + void *data = heap->data; + + for (;;) { + if (pos * 2 + 1 >= heap->nr) + break; + + left = data + ((pos * 2 + 1) * func->elem_size); + parent = data + (pos * func->elem_size); + smallest = parent; + if (func->less(left, smallest)) + smallest = left; + + if (pos * 2 + 2 < heap->nr) { + right = data + ((pos * 2 + 2) * func->elem_size); + if (func->less(right, smallest)) + smallest = right; + } + if (smallest == parent) + break; + func->swp(smallest, parent); + if (smallest == left) + pos = (pos * 2) + 1; + else + pos = (pos * 2) + 2; + } +} + +/* Floyd's approach to heapification that is O(nr). */ +static __always_inline +void min_heapify_all(struct min_heap *heap, + const struct min_heap_callbacks *func) +{ + int i; + + for (i = heap->nr / 2; i >= 0; i--) + min_heapify(heap, i, func); +} + +/* Remove minimum element from the heap, O(log2(nr)). */ +static __always_inline +void min_heap_pop(struct min_heap *heap, + const struct min_heap_callbacks *func) +{ + void *data = heap->data; + + if (WARN_ONCE(heap->nr <= 0, "Popping an empty heap")) + return; + + /* Place last element at the root (position 0) and then sift down. */ + heap->nr--; + memcpy(data, data + (heap->nr * func->elem_size), func->elem_size); + min_heapify(heap, 0, func); +} + +/* + * Remove the minimum element and then push the given element. The + * implementation performs 1 sift (O(log2(nr))) and is therefore more + * efficient than a pop followed by a push that does 2. + */ +static __always_inline +void min_heap_pop_push(struct min_heap *heap, + const void *element, + const struct min_heap_callbacks *func) +{ + memcpy(heap->data, element, func->elem_size); + min_heapify(heap, 0, func); +} + +/* Push an element on to the heap, O(log2(nr)). */ +static __always_inline +void min_heap_push(struct min_heap *heap, const void *element, + const struct min_heap_callbacks *func) +{ + void *data = heap->data; + void *child, *parent; + int pos; + + if (WARN_ONCE(heap->nr >= heap->size, "Pushing on a full heap")) + return; + + /* Place at the end of data. */ + pos = heap->nr; + memcpy(data + (pos * func->elem_size), element, func->elem_size); + heap->nr++; + + /* Sift child at pos up. */ + for (; pos > 0; pos = (pos - 1) / 2) { + child = data + (pos * func->elem_size); + parent = data + ((pos - 1) / 2) * func->elem_size; + if (func->less(parent, child)) + break; + func->swp(parent, child); + } +} + +#endif /* _LINUX_MIN_HEAP_H */ diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 69def4a9df00..f04b61c1a1cc 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1769,6 +1769,16 @@ config TEST_LIST_SORT If unsure, say N. +config TEST_MIN_HEAP + tristate "Min heap test" + depends on DEBUG_KERNEL || m + help + Enable this to turn on min heap function tests. This test is + executed only once during system boot (so affects only boot time), + or at module load time. + + If unsure, say N. + config TEST_SORT tristate "Array-based sort test" depends on DEBUG_KERNEL || m diff --git a/lib/Makefile b/lib/Makefile index 611872c06926..09a8acb0cf92 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -67,6 +67,7 @@ CFLAGS_test_ubsan.o += $(call cc-disable-warning, vla) UBSAN_SANITIZE_test_ubsan.o := y obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o +obj-$(CONFIG_TEST_MIN_HEAP) += test_min_heap.o obj-$(CONFIG_TEST_LKM) += test_module.o obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o obj-$(CONFIG_TEST_OVERFLOW) += test_overflow.o diff --git a/lib/test_min_heap.c b/lib/test_min_heap.c new file mode 100644 index 000000000000..d19c8080fd4d --- /dev/null +++ b/lib/test_min_heap.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define pr_fmt(fmt) "min_heap_test: " fmt + +/* + * Test cases for the min max heap. + */ + +#include +#include +#include +#include +#include + +static __init bool less_than(const void *lhs, const void *rhs) +{ + return *(int *)lhs < *(int *)rhs; +} + +static __init bool greater_than(const void *lhs, const void *rhs) +{ + return *(int *)lhs > *(int *)rhs; +} + +static __init void swap_ints(void *lhs, void *rhs) +{ + int temp = *(int *)lhs; + + *(int *)lhs = *(int *)rhs; + *(int *)rhs = temp; +} + +static __init int pop_verify_heap(bool min_heap, + struct min_heap *heap, + const struct min_heap_callbacks *funcs) +{ + int *values = heap->data; + int err = 0; + int last; + + last = values[0]; + min_heap_pop(heap, funcs); + while (heap->nr > 0) { + if (min_heap) { + if (last > values[0]) { + pr_err("error: expected %d <= %d\n", last, + values[0]); + err++; + } + } else { + if (last < values[0]) { + pr_err("error: expected %d >= %d\n", last, + values[0]); + err++; + } + } + last = values[0]; + min_heap_pop(heap, funcs); + } + return err; +} + +static __init int test_heapify_all(bool min_heap) +{ + int values[] = { 3, 1, 2, 4, 0x8000000, 0x7FFFFFF, 0, + -3, -1, -2, -4, 0x8000000, 0x7FFFFFF }; + struct min_heap heap = { + .data = values, + .nr = ARRAY_SIZE(values), + .size = ARRAY_SIZE(values), + }; + struct min_heap_callbacks funcs = { + .elem_size = sizeof(int), + .less = min_heap ? less_than : greater_than, + .swp = swap_ints, + }; + int i, err; + + /* Test with known set of values. */ + min_heapify_all(&heap, &funcs); + err = pop_verify_heap(min_heap, &heap, &funcs); + + + /* Test with randomly generated values. */ + heap.nr = ARRAY_SIZE(values); + for (i = 0; i < heap.nr; i++) + values[i] = get_random_int(); + + min_heapify_all(&heap, &funcs); + err += pop_verify_heap(min_heap, &heap, &funcs); + + return err; +} + +static __init int test_heap_push(bool min_heap) +{ + const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0, + -3, -1, -2, -4, 0x80000000, 0x7FFFFFFF }; + int values[ARRAY_SIZE(data)]; + struct min_heap heap = { + .data = values, + .nr = 0, + .size = ARRAY_SIZE(values), + }; + struct min_heap_callbacks funcs = { + .elem_size = sizeof(int), + .less = min_heap ? less_than : greater_than, + .swp = swap_ints, + }; + int i, temp, err; + + /* Test with known set of values copied from data. */ + for (i = 0; i < ARRAY_SIZE(data); i++) + min_heap_push(&heap, &data[i], &funcs); + + err = pop_verify_heap(min_heap, &heap, &funcs); + + /* Test with randomly generated values. */ + while (heap.nr < heap.size) { + temp = get_random_int(); + min_heap_push(&heap, &temp, &funcs); + } + err += pop_verify_heap(min_heap, &heap, &funcs); + + return err; +} + +static __init int test_heap_pop_push(bool min_heap) +{ + const int data[] = { 3, 1, 2, 4, 0x80000000, 0x7FFFFFFF, 0, + -3, -1, -2, -4, 0x80000000, 0x7FFFFFFF }; + int values[ARRAY_SIZE(data)]; + struct min_heap heap = { + .data = values, + .nr = 0, + .size = ARRAY_SIZE(values), + }; + struct min_heap_callbacks funcs = { + .elem_size = sizeof(int), + .less = min_heap ? less_than : greater_than, + .swp = swap_ints, + }; + int i, temp, err; + + /* Fill values with data to pop and replace. */ + temp = min_heap ? 0x80000000 : 0x7FFFFFFF; + for (i = 0; i < ARRAY_SIZE(data); i++) + min_heap_push(&heap, &temp, &funcs); + + /* Test with known set of values copied from data. */ + for (i = 0; i < ARRAY_SIZE(data); i++) + min_heap_pop_push(&heap, &data[i], &funcs); + + err = pop_verify_heap(min_heap, &heap, &funcs); + + heap.nr = 0; + for (i = 0; i < ARRAY_SIZE(data); i++) + min_heap_push(&heap, &temp, &funcs); + + /* Test with randomly generated values. */ + for (i = 0; i < ARRAY_SIZE(data); i++) { + temp = get_random_int(); + min_heap_pop_push(&heap, &temp, &funcs); + } + err += pop_verify_heap(min_heap, &heap, &funcs); + + return err; +} + +static int __init test_min_heap_init(void) +{ + int err = 0; + + err += test_heapify_all(true); + err += test_heapify_all(false); + err += test_heap_push(true); + err += test_heap_push(false); + err += test_heap_pop_push(true); + err += test_heap_pop_push(false); + if (err) { + pr_err("test failed with %d errors\n", err); + return -EINVAL; + } + pr_info("test passed\n"); + return 0; +} +module_init(test_min_heap_init); + +static void __exit test_min_heap_exit(void) +{ + /* do nothing */ +} +module_exit(test_min_heap_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 836196beb377e59e54ec9e04f7402076ef7a8bd8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 13 Feb 2020 23:51:31 -0800 Subject: perf/core: Add per perf_cpu_context min_heap storage The storage required for visit_groups_merge's min heap needs to vary in order to support more iterators, such as when multiple nested cgroups' events are being visited. This change allows for 2 iterators and doesn't support growth. Based-on-work-by: Peter Zijlstra (Intel) Signed-off-by: Ian Rogers Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200214075133.181299-5-irogers@google.com --- include/linux/perf_event.h | 7 +++++++ kernel/events/core.c | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 68e21e828893..8768a39b5258 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -862,6 +862,13 @@ struct perf_cpu_context { int sched_cb_usage; int online; + /* + * Per-CPU storage for iterators used in visit_groups_merge. The default + * storage is of size 2 to hold the CPU and any CPU event iterators. + */ + int heap_size; + struct perf_event **heap; + struct perf_event *heap_default[2]; }; struct perf_output_handle { diff --git a/kernel/events/core.c b/kernel/events/core.c index ddfb06c9f367..7529e76a2e36 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3423,22 +3423,34 @@ static void __heap_add(struct min_heap *heap, struct perf_event *event) } } -static noinline int visit_groups_merge(struct perf_event_groups *groups, - int cpu, +static noinline int visit_groups_merge(struct perf_cpu_context *cpuctx, + struct perf_event_groups *groups, int cpu, int (*func)(struct perf_event *, void *), void *data) { /* Space for per CPU and/or any CPU event iterators. */ struct perf_event *itrs[2]; - struct min_heap event_heap = { - .data = itrs, - .nr = 0, - .size = ARRAY_SIZE(itrs), - }; - struct perf_event **evt = event_heap.data; + struct min_heap event_heap; + struct perf_event **evt; int ret; - __heap_add(&event_heap, perf_event_groups_first(groups, -1)); + if (cpuctx) { + event_heap = (struct min_heap){ + .data = cpuctx->heap, + .nr = 0, + .size = cpuctx->heap_size, + }; + } else { + event_heap = (struct min_heap){ + .data = itrs, + .nr = 0, + .size = ARRAY_SIZE(itrs), + }; + /* Events not within a CPU context may be on any CPU. */ + __heap_add(&event_heap, perf_event_groups_first(groups, -1)); + } + evt = event_heap.data; + __heap_add(&event_heap, perf_event_groups_first(groups, cpu)); min_heapify_all(&event_heap, &perf_min_heap); @@ -3492,7 +3504,10 @@ ctx_pinned_sched_in(struct perf_event_context *ctx, { int can_add_hw = 1; - visit_groups_merge(&ctx->pinned_groups, + if (ctx != &cpuctx->ctx) + cpuctx = NULL; + + visit_groups_merge(cpuctx, &ctx->pinned_groups, smp_processor_id(), merge_sched_in, &can_add_hw); } @@ -3503,7 +3518,10 @@ ctx_flexible_sched_in(struct perf_event_context *ctx, { int can_add_hw = 1; - visit_groups_merge(&ctx->flexible_groups, + if (ctx != &cpuctx->ctx) + cpuctx = NULL; + + visit_groups_merge(cpuctx, &ctx->flexible_groups, smp_processor_id(), merge_sched_in, &can_add_hw); } @@ -10364,6 +10382,9 @@ skip_type: cpuctx->online = cpumask_test_cpu(cpu, perf_online_mask); __perf_mux_hrtimer_init(cpuctx, cpu); + + cpuctx->heap_size = ARRAY_SIZE(cpuctx->heap_default); + cpuctx->heap = cpuctx->heap_default; } got_cpu_context: -- cgit v1.2.3 From 765047932f153265db6ef15be208d6cbfc03dc62 Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Fri, 21 Feb 2020 19:52:05 -0500 Subject: sched/pelt: Add support to track thermal pressure Extrapolating on the existing framework to track rt/dl utilization using pelt signals, add a similar mechanism to track thermal pressure. The difference here from rt/dl utilization tracking is that, instead of tracking time spent by a CPU running a RT/DL task through util_avg, the average thermal pressure is tracked through load_avg. This is because thermal pressure signal is weighted time "delta" capacity unlike util_avg which is binary. "delta capacity" here means delta between the actual capacity of a CPU and the decreased capacity a CPU due to a thermal event. In order to track average thermal pressure, a new sched_avg variable avg_thermal is introduced. Function update_thermal_load_avg can be called to do the periodic bookkeeping (accumulate, decay and average) of the thermal pressure. Reviewed-by: Vincent Guittot Signed-off-by: Thara Gopinath Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200222005213.3873-2-thara.gopinath@linaro.org --- include/trace/events/sched.h | 4 ++++ init/Kconfig | 4 ++++ kernel/sched/pelt.c | 31 +++++++++++++++++++++++++++++++ kernel/sched/pelt.h | 31 +++++++++++++++++++++++++++++++ kernel/sched/sched.h | 3 +++ 5 files changed, 73 insertions(+) (limited to 'include') diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h index 9c3ebb7c83a5..ed168b0e2c53 100644 --- a/include/trace/events/sched.h +++ b/include/trace/events/sched.h @@ -618,6 +618,10 @@ DECLARE_TRACE(pelt_dl_tp, TP_PROTO(struct rq *rq), TP_ARGS(rq)); +DECLARE_TRACE(pelt_thermal_tp, + TP_PROTO(struct rq *rq), + TP_ARGS(rq)); + DECLARE_TRACE(pelt_irq_tp, TP_PROTO(struct rq *rq), TP_ARGS(rq)); diff --git a/init/Kconfig b/init/Kconfig index 20a6ac33761c..275c848b02c6 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -451,6 +451,10 @@ config HAVE_SCHED_AVG_IRQ depends on IRQ_TIME_ACCOUNTING || PARAVIRT_TIME_ACCOUNTING depends on SMP +config SCHED_THERMAL_PRESSURE + bool "Enable periodic averaging of thermal pressure" + depends on SMP + config BSD_PROCESS_ACCT bool "BSD Process Accounting" depends on MULTIUSER diff --git a/kernel/sched/pelt.c b/kernel/sched/pelt.c index c40d57a2a248..b647d04d9c8b 100644 --- a/kernel/sched/pelt.c +++ b/kernel/sched/pelt.c @@ -368,6 +368,37 @@ int update_dl_rq_load_avg(u64 now, struct rq *rq, int running) return 0; } +#ifdef CONFIG_SCHED_THERMAL_PRESSURE +/* + * thermal: + * + * load_sum = \Sum se->avg.load_sum but se->avg.load_sum is not tracked + * + * util_avg and runnable_load_avg are not supported and meaningless. + * + * Unlike rt/dl utilization tracking that track time spent by a cpu + * running a rt/dl task through util_avg, the average thermal pressure is + * tracked through load_avg. This is because thermal pressure signal is + * time weighted "delta" capacity unlike util_avg which is binary. + * "delta capacity" = actual capacity - + * capped capacity a cpu due to a thermal event. + */ + +int update_thermal_load_avg(u64 now, struct rq *rq, u64 capacity) +{ + if (___update_load_sum(now, &rq->avg_thermal, + capacity, + capacity, + capacity)) { + ___update_load_avg(&rq->avg_thermal, 1); + trace_pelt_thermal_tp(rq); + return 1; + } + + return 0; +} +#endif + #ifdef CONFIG_HAVE_SCHED_AVG_IRQ /* * irq: diff --git a/kernel/sched/pelt.h b/kernel/sched/pelt.h index afff644da065..eb034d9f024d 100644 --- a/kernel/sched/pelt.h +++ b/kernel/sched/pelt.h @@ -7,6 +7,26 @@ int __update_load_avg_cfs_rq(u64 now, struct cfs_rq *cfs_rq); int update_rt_rq_load_avg(u64 now, struct rq *rq, int running); int update_dl_rq_load_avg(u64 now, struct rq *rq, int running); +#ifdef CONFIG_SCHED_THERMAL_PRESSURE +int update_thermal_load_avg(u64 now, struct rq *rq, u64 capacity); + +static inline u64 thermal_load_avg(struct rq *rq) +{ + return READ_ONCE(rq->avg_thermal.load_avg); +} +#else +static inline int +update_thermal_load_avg(u64 now, struct rq *rq, u64 capacity) +{ + return 0; +} + +static inline u64 thermal_load_avg(struct rq *rq) +{ + return 0; +} +#endif + #ifdef CONFIG_HAVE_SCHED_AVG_IRQ int update_irq_load_avg(struct rq *rq, u64 running); #else @@ -158,6 +178,17 @@ update_dl_rq_load_avg(u64 now, struct rq *rq, int running) return 0; } +static inline int +update_thermal_load_avg(u64 now, struct rq *rq, u64 capacity) +{ + return 0; +} + +static inline u64 thermal_load_avg(struct rq *rq) +{ + return 0; +} + static inline int update_irq_load_avg(struct rq *rq, u64 running) { diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 2a0caf394dd4..6c839f829a25 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -960,6 +960,9 @@ struct rq { struct sched_avg avg_dl; #ifdef CONFIG_HAVE_SCHED_AVG_IRQ struct sched_avg avg_irq; +#endif +#ifdef CONFIG_SCHED_THERMAL_PRESSURE + struct sched_avg avg_thermal; #endif u64 idle_stamp; u64 avg_idle; -- cgit v1.2.3 From 36a0df85d2e85e1929e8cd607e19243e5a2754e7 Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Fri, 21 Feb 2020 19:52:06 -0500 Subject: sched/topology: Add callback to read per CPU thermal pressure Introduce the arch_scale_thermal_pressure() callback to retrieve per CPU thermal pressure. Signed-off-by: Thara Gopinath Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200222005213.3873-3-thara.gopinath@linaro.org --- include/linux/sched/topology.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/sched/topology.h b/include/linux/sched/topology.h index f341163fedc9..af9319e4cfb9 100644 --- a/include/linux/sched/topology.h +++ b/include/linux/sched/topology.h @@ -225,6 +225,14 @@ unsigned long arch_scale_cpu_capacity(int cpu) } #endif +#ifndef arch_scale_thermal_pressure +static __always_inline +unsigned long arch_scale_thermal_pressure(int cpu) +{ + return 0; +} +#endif + static inline int task_node(const struct task_struct *p) { return cpu_to_node(task_cpu(p)); -- cgit v1.2.3 From ad58cc5cc50ca8423cf630778594bd38252a0a58 Mon Sep 17 00:00:00 2001 From: Thara Gopinath Date: Fri, 21 Feb 2020 19:52:07 -0500 Subject: drivers/base/arch_topology: Add infrastructure to store and update instantaneous thermal pressure Add architecture specific APIs to update and track thermal pressure on a per CPU basis. A per CPU variable thermal_pressure is introduced to keep track of instantaneous per CPU thermal pressure. Thermal pressure is the delta between maximum capacity and capped capacity due to a thermal event. topology_get_thermal_pressure can be hooked into the scheduler specified arch_scale_thermal_pressure to retrieve instantaneous thermal pressure of a CPU. arch_set_thermal_pressure can be used to update the thermal pressure. Considering topology_get_thermal_pressure reads thermal_pressure and arch_set_thermal_pressure writes into thermal_pressure, one can argue for some sort of locking mechanism to avoid a stale value. But considering topology_get_thermal_pressure can be called from a system critical path like scheduler tick function, a locking mechanism is not ideal. This means that it is possible the thermal_pressure value used to calculate average thermal pressure for a CPU can be stale for up to 1 tick period. Signed-off-by: Thara Gopinath Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lkml.kernel.org/r/20200222005213.3873-4-thara.gopinath@linaro.org --- drivers/base/arch_topology.c | 11 +++++++++++ include/linux/arch_topology.h | 10 ++++++++++ 2 files changed, 21 insertions(+) (limited to 'include') diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 6119e11a9f95..68dfa49d3b63 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -42,6 +42,17 @@ void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity) per_cpu(cpu_scale, cpu) = capacity; } +DEFINE_PER_CPU(unsigned long, thermal_pressure); + +void arch_set_thermal_pressure(struct cpumask *cpus, + unsigned long th_pressure) +{ + int cpu; + + for_each_cpu(cpu, cpus) + WRITE_ONCE(per_cpu(thermal_pressure, cpu), th_pressure); +} + static ssize_t cpu_capacity_show(struct device *dev, struct device_attribute *attr, char *buf) diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 3015ecbb90b1..88a115e81f27 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -33,6 +33,16 @@ unsigned long topology_get_freq_scale(int cpu) return per_cpu(freq_scale, cpu); } +DECLARE_PER_CPU(unsigned long, thermal_pressure); + +static inline unsigned long topology_get_thermal_pressure(int cpu) +{ + return per_cpu(thermal_pressure, cpu); +} + +void arch_set_thermal_pressure(struct cpumask *cpus, + unsigned long th_pressure); + struct cpu_topology { int thread_id; int core_id; -- cgit v1.2.3 From 4769bfb9dada678b31a2ec275372624dbfeed9d1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 4 Mar 2020 23:11:41 -0600 Subject: ALSA: pcm: Add a standalone version of snd_pcm_limit_hw_rates It can be useful to derive min/max rates of a snd_pcm_hardware without having a snd_pcm_runtime, such as before constructing an ASoC DAI link. Create a new helper that takes a pointer to a snd_pcm_hardware directly, and refactor the original function as a wrapper around it, to avoid needing to update any call sites. Signed-off-by: Samuel Holland Reviewed-by: Takashi Iwai Link: https://lore.kernel.org/r/20200305051143.60691-2-samuel@sholland.org Signed-off-by: Mark Brown --- include/sound/pcm.h | 9 ++++++++- sound/core/pcm_misc.c | 18 +++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 2628246b76fa..f7a95b711100 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -1127,7 +1127,14 @@ snd_pcm_kernel_readv(struct snd_pcm_substream *substream, return __snd_pcm_lib_xfer(substream, bufs, false, frames, true); } -int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime); +int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw); + +static inline int +snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) +{ + return snd_pcm_hw_limit_rates(&runtime->hw); +} + unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate); unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit); unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a, diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c index a6a541511534..5dd2e5335900 100644 --- a/sound/core/pcm_misc.c +++ b/sound/core/pcm_misc.c @@ -474,32 +474,32 @@ int snd_pcm_format_set_silence(snd_pcm_format_t format, void *data, unsigned int EXPORT_SYMBOL(snd_pcm_format_set_silence); /** - * snd_pcm_limit_hw_rates - determine rate_min/rate_max fields - * @runtime: the runtime instance + * snd_pcm_hw_limit_rates - determine rate_min/rate_max fields + * @hw: the pcm hw instance * * Determines the rate_min and rate_max fields from the rates bits of - * the given runtime->hw. + * the given hw. * * Return: Zero if successful. */ -int snd_pcm_limit_hw_rates(struct snd_pcm_runtime *runtime) +int snd_pcm_hw_limit_rates(struct snd_pcm_hardware *hw) { int i; for (i = 0; i < (int)snd_pcm_known_rates.count; i++) { - if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_min = snd_pcm_known_rates.list[i]; + if (hw->rates & (1 << i)) { + hw->rate_min = snd_pcm_known_rates.list[i]; break; } } for (i = (int)snd_pcm_known_rates.count - 1; i >= 0; i--) { - if (runtime->hw.rates & (1 << i)) { - runtime->hw.rate_max = snd_pcm_known_rates.list[i]; + if (hw->rates & (1 << i)) { + hw->rate_max = snd_pcm_known_rates.list[i]; break; } } return 0; } -EXPORT_SYMBOL(snd_pcm_limit_hw_rates); +EXPORT_SYMBOL(snd_pcm_hw_limit_rates); /** * snd_pcm_rate_to_rate_bit - converts sample rate to SNDRV_PCM_RATE_xxx bit -- cgit v1.2.3 From 5854a46486ad5b8d73766735fb0d77f05956b22c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 4 Mar 2020 23:11:42 -0600 Subject: ASoC: pcm: Export parameter intersection logic The logic to calculate the subset of stream parameters supported by all DAIs associated with a PCM stream is nontrivial. Export a helper function so it can be used to set up simple codec2codec DAI links. Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200305051143.60691-3-samuel@sholland.org Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ sound/soc/soc-pcm.c | 56 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 41 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 81e5d17be935..9543d9246ca4 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -471,6 +471,9 @@ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd); void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream); void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream); +int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hardware *hw, int stream); + int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, unsigned int dai_fmt); diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e3a2c4f7757b..de4226357e2b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -587,11 +587,18 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream) soc_pcm_set_msb(substream, cpu_bits); } -static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) +/** + * snd_soc_runtime_calc_hw() - Calculate hw limits for a PCM stream + * @rtd: ASoC PCM runtime + * @hw: PCM hardware parameters (output) + * @stream: Direction of the PCM stream + * + * Calculates the subset of stream parameters supported by all DAIs + * associated with the PCM stream. + */ +int snd_soc_runtime_calc_hw(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hardware *hw, int stream) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_pcm_hardware *hw = &runtime->hw; - struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; struct snd_soc_pcm_stream *codec_stream; @@ -602,7 +609,6 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) unsigned int cpu_rate_min = 0, cpu_rate_max = UINT_MAX; unsigned int rates = UINT_MAX, cpu_rates = UINT_MAX; u64 formats = ULLONG_MAX; - int stream = substream->stream; int i; /* first calculate min/max only for CPUs in the DAI link */ @@ -613,12 +619,8 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) * Otherwise, since the rate, channel, and format values will * zero in that case, we would have no usable settings left, * causing the resulting setup to fail. - * At least one CPU should match, otherwise we should have - * bailed out on a higher level, since there would be no - * CPU to support the transfer direction in that case. */ - if (!snd_soc_dai_stream_valid(cpu_dai, - substream->stream)) + if (!snd_soc_dai_stream_valid(cpu_dai, stream)) continue; cpu_stream = snd_soc_dai_get_pcm_stream(cpu_dai, stream); @@ -640,9 +642,6 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) * Otherwise, since the rate, channel, and format values will * zero in that case, we would have no usable settings left, * causing the resulting setup to fail. - * At least one CODEC should match, otherwise we should have - * bailed out on a higher level, since there would be no - * CODEC to support the transfer direction in that case. */ if (!snd_soc_dai_stream_valid(codec_dai, stream)) continue; @@ -657,6 +656,10 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates); } + /* Verify both a valid CPU DAI and a valid CODEC DAI were found */ + if (!chan_min || !cpu_chan_min) + return -EINVAL; + /* * chan min/max cannot be enforced if there are multiple CODEC DAIs * connected to CPU DAI(s), use CPU DAI's directly and let @@ -670,18 +673,35 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) /* finally find a intersection between CODECs and CPUs */ hw->channels_min = max(chan_min, cpu_chan_min); hw->channels_max = min(chan_max, cpu_chan_max); - if (hw->formats) - hw->formats &= formats; - else - hw->formats = formats; + hw->formats = formats; hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_rates); - snd_pcm_limit_hw_rates(runtime); + snd_pcm_hw_limit_rates(hw); hw->rate_min = max(hw->rate_min, cpu_rate_min); hw->rate_min = max(hw->rate_min, rate_min); hw->rate_max = min_not_zero(hw->rate_max, cpu_rate_max); hw->rate_max = min_not_zero(hw->rate_max, rate_max); + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_runtime_calc_hw); + +static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) +{ + struct snd_pcm_hardware *hw = &substream->runtime->hw; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + u64 formats = hw->formats; + + /* + * At least one CPU and one CODEC should match. Otherwise, we should + * have bailed out on a higher level, since there would be no CPU or + * CODEC to support the transfer direction in that case. + */ + snd_soc_runtime_calc_hw(rtd, hw, substream->stream); + + if (formats) + hw->formats &= formats; } static int soc_pcm_components_open(struct snd_pcm_substream *substream) -- cgit v1.2.3 From bbce8eaa603236bf958b0d24e6377b3f3b623991 Mon Sep 17 00:00:00 2001 From: Ionela Voinescu Date: Thu, 5 Mar 2020 09:06:25 +0000 Subject: cpufreq: add function to get the hardware max frequency Add weak function to return the hardware maximum frequency of a CPU, with the default implementation returning cpuinfo.max_freq, which is the best information we can generically get from the cpufreq framework. The default can be overwritten by a strong function in platforms that want to provide an alternative implementation, with more accurate information, obtained either from hardware or firmware. Signed-off-by: Ionela Voinescu Reviewed-by: Valentin Schneider Acked-by: Viresh Kumar Cc: Viresh Kumar Cc: Rafael J. Wysocki Signed-off-by: Catalin Marinas --- drivers/cpufreq/cpufreq.c | 20 ++++++++++++++++++++ include/linux/cpufreq.h | 5 +++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index cbe6c94bf158..985228aee46f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1725,6 +1725,26 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get_max); +/** + * cpufreq_get_hw_max_freq - get the max hardware frequency of the CPU + * @cpu: CPU number + * + * The default return value is the max_freq field of cpuinfo. + */ +__weak unsigned int cpufreq_get_hw_max_freq(unsigned int cpu) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + unsigned int ret_freq = 0; + + if (policy) { + ret_freq = policy->cpuinfo.max_freq; + cpufreq_cpu_put(policy); + } + + return ret_freq; +} +EXPORT_SYMBOL(cpufreq_get_hw_max_freq); + static unsigned int __cpufreq_get(struct cpufreq_policy *policy) { if (unlikely(policy_is_inactive(policy))) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 0fb561d1b524..f7240251a949 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -205,6 +205,7 @@ static inline bool policy_is_shared(struct cpufreq_policy *policy) unsigned int cpufreq_get(unsigned int cpu); unsigned int cpufreq_quick_get(unsigned int cpu); unsigned int cpufreq_quick_get_max(unsigned int cpu); +unsigned int cpufreq_get_hw_max_freq(unsigned int cpu); void disable_cpufreq(void); u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy); @@ -232,6 +233,10 @@ static inline unsigned int cpufreq_quick_get_max(unsigned int cpu) { return 0; } +static inline unsigned int cpufreq_get_hw_max_freq(unsigned int cpu) +{ + return 0; +} static inline void disable_cpufreq(void) { } #endif -- cgit v1.2.3 From cd0ed03a8903a0b0c6fc36e32d133d1ddfe70cd6 Mon Sep 17 00:00:00 2001 From: Ionela Voinescu Date: Thu, 5 Mar 2020 09:06:26 +0000 Subject: arm64: use activity monitors for frequency invariance The Frequency Invariance Engine (FIE) is providing a frequency scaling correction factor that helps achieve more accurate load-tracking. So far, for arm and arm64 platforms, this scale factor has been obtained based on the ratio between the current frequency and the maximum supported frequency recorded by the cpufreq policy. The setting of this scale factor is triggered from cpufreq drivers by calling arch_set_freq_scale. The current frequency used in computation is the frequency requested by a governor, but it may not be the frequency that was implemented by the platform. This correction factor can also be obtained using a core counter and a constant counter to get information on the performance (frequency based only) obtained in a period of time. This will more accurately reflect the actual current frequency of the CPU, compared with the alternative implementation that reflects the request of a performance level from the OS. Therefore, implement arch_scale_freq_tick to use activity monitors, if present, for the computation of the frequency scale factor. The use of AMU counters depends on: - CONFIG_ARM64_AMU_EXTN - depents on the AMU extension being present - CONFIG_CPU_FREQ - the current frequency obtained using counter information is divided by the maximum frequency obtained from the cpufreq policy. While it is possible to have a combination of CPUs in the system with and without support for activity monitors, the use of counters for frequency invariance is only enabled for a CPU if all related CPUs (CPUs in the same frequency domain) support and have enabled the core and constant activity monitor counters. In this way, there is a clear separation between the policies for which arch_set_freq_scale (cpufreq based FIE) is used, and the policies for which arch_scale_freq_tick (counter based FIE) is used to set the frequency scale factor. For this purpose, a late_initcall_sync is registered to trigger validation work for policies that will enable or disable the use of AMU counters for frequency invariance. If CONFIG_CPU_FREQ is not defined, the use of counters is enabled on all CPUs only if all possible CPUs correctly support the necessary counters. Signed-off-by: Ionela Voinescu Reviewed-by: Lukasz Luba Acked-by: Sudeep Holla Cc: Sudeep Holla Cc: Will Deacon Cc: Catalin Marinas Signed-off-by: Catalin Marinas --- arch/arm64/include/asm/topology.h | 9 ++ arch/arm64/kernel/cpufeature.c | 4 + arch/arm64/kernel/topology.c | 180 ++++++++++++++++++++++++++++++++++++++ drivers/base/arch_topology.c | 12 +++ include/linux/arch_topology.h | 2 + 5 files changed, 207 insertions(+) (limited to 'include') diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h index a4d945db95a2..21d4d40d6243 100644 --- a/arch/arm64/include/asm/topology.h +++ b/arch/arm64/include/asm/topology.h @@ -16,6 +16,15 @@ int pcibus_to_node(struct pci_bus *bus); #include +#ifdef CONFIG_ARM64_AMU_EXTN +/* + * Replace task scheduler's default counter-based + * frequency-invariance scale factor setting. + */ +void topology_scale_freq_tick(void); +#define arch_scale_freq_tick topology_scale_freq_tick +#endif /* CONFIG_ARM64_AMU_EXTN */ + /* Replace task scheduler's default frequency-invariant accounting */ #define arch_scale_freq_capacity topology_get_freq_scale diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 301538d3a197..bf98a2386534 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1241,12 +1241,16 @@ bool cpu_has_amu_feat(int cpu) return cpumask_test_cpu(cpu, &amu_cpus); } +/* Initialize the use of AMU counters for frequency invariance */ +extern void init_cpu_freq_invariance_counters(void); + static void cpu_amu_enable(struct arm64_cpu_capabilities const *cap) { if (has_cpuid_feature(cap, SCOPE_LOCAL_CPU)) { pr_info("detected CPU%d: Activity Monitors Unit (AMU)\n", smp_processor_id()); cpumask_set_cpu(smp_processor_id(), &amu_cpus); + init_cpu_freq_invariance_counters(); } } diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index fa9528dfd0ce..0801a0f3c156 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -120,4 +121,183 @@ int __init parse_acpi_topology(void) } #endif +#ifdef CONFIG_ARM64_AMU_EXTN +#undef pr_fmt +#define pr_fmt(fmt) "AMU: " fmt + +static DEFINE_PER_CPU_READ_MOSTLY(unsigned long, arch_max_freq_scale); +static DEFINE_PER_CPU(u64, arch_const_cycles_prev); +static DEFINE_PER_CPU(u64, arch_core_cycles_prev); +static cpumask_var_t amu_fie_cpus; + +/* Initialize counter reference per-cpu variables for the current CPU */ +void init_cpu_freq_invariance_counters(void) +{ + this_cpu_write(arch_core_cycles_prev, + read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0)); + this_cpu_write(arch_const_cycles_prev, + read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0)); +} + +static int validate_cpu_freq_invariance_counters(int cpu) +{ + u64 max_freq_hz, ratio; + + if (!cpu_has_amu_feat(cpu)) { + pr_debug("CPU%d: counters are not supported.\n", cpu); + return -EINVAL; + } + + if (unlikely(!per_cpu(arch_const_cycles_prev, cpu) || + !per_cpu(arch_core_cycles_prev, cpu))) { + pr_debug("CPU%d: cycle counters are not enabled.\n", cpu); + return -EINVAL; + } + + /* Convert maximum frequency from KHz to Hz and validate */ + max_freq_hz = cpufreq_get_hw_max_freq(cpu) * 1000; + if (unlikely(!max_freq_hz)) { + pr_debug("CPU%d: invalid maximum frequency.\n", cpu); + return -EINVAL; + } + + /* + * Pre-compute the fixed ratio between the frequency of the constant + * counter and the maximum frequency of the CPU. + * + * const_freq + * arch_max_freq_scale = ---------------- * SCHED_CAPACITY_SCALE² + * cpuinfo_max_freq + * + * We use a factor of 2 * SCHED_CAPACITY_SHIFT -> SCHED_CAPACITY_SCALE² + * in order to ensure a good resolution for arch_max_freq_scale for + * very low arch timer frequencies (down to the KHz range which should + * be unlikely). + */ + ratio = (u64)arch_timer_get_rate() << (2 * SCHED_CAPACITY_SHIFT); + ratio = div64_u64(ratio, max_freq_hz); + if (!ratio) { + WARN_ONCE(1, "System timer frequency too low.\n"); + return -EINVAL; + } + + per_cpu(arch_max_freq_scale, cpu) = (unsigned long)ratio; + + return 0; +} + +static inline bool +enable_policy_freq_counters(int cpu, cpumask_var_t valid_cpus) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + + if (!policy) { + pr_debug("CPU%d: No cpufreq policy found.\n", cpu); + return false; + } + + if (cpumask_subset(policy->related_cpus, valid_cpus)) + cpumask_or(amu_fie_cpus, policy->related_cpus, + amu_fie_cpus); + + cpufreq_cpu_put(policy); + + return true; +} + +static DEFINE_STATIC_KEY_FALSE(amu_fie_key); +#define amu_freq_invariant() static_branch_unlikely(&amu_fie_key) + +static int __init init_amu_fie(void) +{ + cpumask_var_t valid_cpus; + bool have_policy = false; + int ret = 0; + int cpu; + + if (!zalloc_cpumask_var(&valid_cpus, GFP_KERNEL)) + return -ENOMEM; + + if (!zalloc_cpumask_var(&amu_fie_cpus, GFP_KERNEL)) { + ret = -ENOMEM; + goto free_valid_mask; + } + + for_each_present_cpu(cpu) { + if (validate_cpu_freq_invariance_counters(cpu)) + continue; + cpumask_set_cpu(cpu, valid_cpus); + have_policy |= enable_policy_freq_counters(cpu, valid_cpus); + } + + /* + * If we are not restricted by cpufreq policies, we only enable + * the use of the AMU feature for FIE if all CPUs support AMU. + * Otherwise, enable_policy_freq_counters has already enabled + * policy cpus. + */ + if (!have_policy && cpumask_equal(valid_cpus, cpu_present_mask)) + cpumask_or(amu_fie_cpus, amu_fie_cpus, valid_cpus); + + if (!cpumask_empty(amu_fie_cpus)) { + pr_info("CPUs[%*pbl]: counters will be used for FIE.", + cpumask_pr_args(amu_fie_cpus)); + static_branch_enable(&amu_fie_key); + } + +free_valid_mask: + free_cpumask_var(valid_cpus); + + return ret; +} +late_initcall_sync(init_amu_fie); + +bool arch_freq_counters_available(struct cpumask *cpus) +{ + return amu_freq_invariant() && + cpumask_subset(cpus, amu_fie_cpus); +} + +void topology_scale_freq_tick(void) +{ + u64 prev_core_cnt, prev_const_cnt; + u64 core_cnt, const_cnt, scale; + int cpu = smp_processor_id(); + + if (!amu_freq_invariant()) + return; + + if (!cpumask_test_cpu(cpu, amu_fie_cpus)) + return; + + const_cnt = read_sysreg_s(SYS_AMEVCNTR0_CONST_EL0); + core_cnt = read_sysreg_s(SYS_AMEVCNTR0_CORE_EL0); + prev_const_cnt = this_cpu_read(arch_const_cycles_prev); + prev_core_cnt = this_cpu_read(arch_core_cycles_prev); + + if (unlikely(core_cnt <= prev_core_cnt || + const_cnt <= prev_const_cnt)) + goto store_and_exit; + + /* + * /\core arch_max_freq_scale + * scale = ------- * -------------------- + * /\const SCHED_CAPACITY_SCALE + * + * See validate_cpu_freq_invariance_counters() for details on + * arch_max_freq_scale and the use of SCHED_CAPACITY_SHIFT. + */ + scale = core_cnt - prev_core_cnt; + scale *= this_cpu_read(arch_max_freq_scale); + scale = div64_u64(scale >> SCHED_CAPACITY_SHIFT, + const_cnt - prev_const_cnt); + + scale = min_t(unsigned long, scale, SCHED_CAPACITY_SCALE); + this_cpu_write(freq_scale, (unsigned long)scale); + +store_and_exit: + this_cpu_write(arch_core_cycles_prev, core_cnt); + this_cpu_write(arch_const_cycles_prev, const_cnt); +} +#endif /* CONFIG_ARM64_AMU_EXTN */ diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 6119e11a9f95..8d63673c1689 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -21,6 +21,10 @@ #include #include +__weak bool arch_freq_counters_available(struct cpumask *cpus) +{ + return false; +} DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, @@ -29,6 +33,14 @@ void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, unsigned long scale; int i; + /* + * If the use of counters for FIE is enabled, just return as we don't + * want to update the scale factor with information from CPUFREQ. + * Instead the scale factor will be updated from arch_scale_freq_tick. + */ + if (arch_freq_counters_available(cpus)) + return; + scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; for_each_cpu(i, cpus) diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 3015ecbb90b1..1ccdddb541a7 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -33,6 +33,8 @@ unsigned long topology_get_freq_scale(int cpu) return per_cpu(freq_scale, cpu); } +bool arch_freq_counters_available(struct cpumask *cpus); + struct cpu_topology { int thread_id; int core_id; -- cgit v1.2.3 From 241eaabc3c315cdfea505725a43de848f498527f Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Fri, 6 Mar 2020 10:34:10 +0800 Subject: power: supply: Allow charger manager can be built as a module Allow charger manager can be built as a module like other charger drivers. Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/Kconfig | 2 +- include/linux/power/charger-manager.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 9a5591ab90d0..195bc0462d3e 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -480,7 +480,7 @@ config CHARGER_GPIO called gpio-charger. config CHARGER_MANAGER - bool "Battery charger manager for multiple chargers" + tristate "Battery charger manager for multiple chargers" depends on REGULATOR select EXTCON help diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h index ad19e68e1fc3..ae94dcebd936 100644 --- a/include/linux/power/charger-manager.h +++ b/include/linux/power/charger-manager.h @@ -248,7 +248,7 @@ struct charger_manager { u64 charging_end_time; }; -#ifdef CONFIG_CHARGER_MANAGER +#if IS_ENABLED(CONFIG_CHARGER_MANAGER) extern void cm_notify_event(struct power_supply *psy, enum cm_event_types type, char *msg); #else -- cgit v1.2.3 From aeaa925bff844d225f259215a250d2811e436499 Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Thu, 5 Mar 2020 17:05:16 +0100 Subject: rhashtable: Document the right function parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rhashtable_lookup_get_insert_key doesn't have a parameter `data`. It does have a parameter `key`, however. Signed-off-by: Jonathan Neuschäfer Acked-by: Herbert Xu Signed-off-by: David S. Miller --- include/linux/rhashtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/rhashtable.h b/include/linux/rhashtable.h index beb9a9da1699..70ebef866cc8 100644 --- a/include/linux/rhashtable.h +++ b/include/linux/rhashtable.h @@ -972,9 +972,9 @@ static inline int rhashtable_lookup_insert_key( /** * rhashtable_lookup_get_insert_key - lookup and insert object into hash table * @ht: hash table + * @key: key * @obj: pointer to hash head inside object * @params: hash table parameters - * @data: pointer to element data already in hashes * * Just like rhashtable_lookup_insert_key(), but this function returns the * object if it exists, NULL if it does not and the insertion was successful, -- cgit v1.2.3 From 058bc104f7ca5c83d81695ee96f03dbd93bae518 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 28 Feb 2020 14:31:06 +0100 Subject: serial: 8250: Generalize rs485 software emulation Commit e490c9144cfa ("tty: Add software emulated RS485 support for 8250") introduced support to use RTS as an rs485 Transmit Enable signal. So far the only drivers taking advantage of it are 8250_omap.c and 8250_of.c. We're about to make use of the feature in 8250_bcm2835aux.c as well. The bcm2835aux differs from omap chips by inverting the meaning of RTS in the MCR register. Moreover, omap achieves half-duplex mode by disabling the RX interrupt and clearing the RX FIFO when TX stops. The bcm2835aux requires disabling the receiver instead. Support these behavioral differences by generalizing the rs485 emulation: Introduce ->rs485_start_tx() and ->rs485_stop_tx() callbacks in struct uart_8250_port, provide generic implementations containing the existing code and use them as callbacks in 8250_omap.c and 8250_of.c. start_tx_rs485() is idempotent in that it recognizes whether RTS is already asserted. Achieve the same by introducing a tx_stopped flag in struct uart_8250_em485. This may even perform a little better on arches where memory access is faster than mmio access. Signed-off-by: Lukas Wunner Cc: Matwey V. Kornilov Link: https://lore.kernel.org/r/5ac0464ae4414708e723a1e0d52b0c1b2bd41b9b.1582895077.git.lukas@wunner.de Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250.h | 2 + drivers/tty/serial/8250/8250_core.c | 2 + drivers/tty/serial/8250/8250_of.c | 7 +++- drivers/tty/serial/8250/8250_omap.c | 2 + drivers/tty/serial/8250/8250_port.c | 82 ++++++++++++++++++++++++------------- include/linux/serial_8250.h | 3 ++ 6 files changed, 67 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h index 83a85210501f..52bb21205bb6 100644 --- a/drivers/tty/serial/8250/8250.h +++ b/drivers/tty/serial/8250/8250.h @@ -157,6 +157,8 @@ void serial8250_rpm_get_tx(struct uart_8250_port *p); void serial8250_rpm_put_tx(struct uart_8250_port *p); int serial8250_em485_config(struct uart_port *port, struct serial_rs485 *rs485); +void serial8250_em485_start_tx(struct uart_8250_port *p); +void serial8250_em485_stop_tx(struct uart_8250_port *p); void serial8250_em485_destroy(struct uart_8250_port *p); /* MCR <-> TIOCM conversion */ diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 9a1d09fbb836..f6eb00406ddf 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1007,6 +1007,8 @@ int serial8250_register_8250_port(struct uart_8250_port *up) uart->port.unthrottle = up->port.unthrottle; uart->port.rs485_config = up->port.rs485_config; uart->port.rs485 = up->port.rs485; + uart->rs485_start_tx = up->rs485_start_tx; + uart->rs485_stop_tx = up->rs485_stop_tx; uart->dma = up->dma; /* Take tx_loadsz from fifosize if it wasn't set separately */ diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index 4a68a8785caa..65e9045dafe6 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -29,11 +29,12 @@ struct of_serial_info { * Fill a struct uart_port for a given device node */ static int of_platform_serial_setup(struct platform_device *ofdev, - int type, struct uart_port *port, + int type, struct uart_8250_port *up, struct of_serial_info *info) { struct resource resource; struct device_node *np = ofdev->dev.of_node; + struct uart_port *port = &up->port; u32 clk, spd, prop; int ret, irq; @@ -155,6 +156,8 @@ static int of_platform_serial_setup(struct platform_device *ofdev, port->dev = &ofdev->dev; port->rs485_config = serial8250_em485_config; + up->rs485_start_tx = serial8250_em485_start_tx; + up->rs485_stop_tx = serial8250_em485_stop_tx; switch (type) { case PORT_RT2880: @@ -201,7 +204,7 @@ static int of_platform_serial_probe(struct platform_device *ofdev) return -ENOMEM; memset(&port8250, 0, sizeof(port8250)); - ret = of_platform_serial_setup(ofdev, port_type, &port8250.port, info); + ret = of_platform_serial_setup(ofdev, port_type, &port8250, info); if (ret) goto err_free; diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index fff70cb25046..dd69226ce918 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -1158,6 +1158,8 @@ static int omap8250_probe(struct platform_device *pdev) up.port.throttle = omap_8250_throttle; up.port.unthrottle = omap_8250_unthrottle; up.port.rs485_config = serial8250_em485_config; + up.rs485_start_tx = serial8250_em485_start_tx; + up.rs485_stop_tx = serial8250_em485_stop_tx; up.port.has_sysrq = IS_ENABLED(CONFIG_SERIAL_8250_CONSOLE); ret = of_alias_get_id(np, "serial"); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 1a71625a0d43..8e8cca690bf9 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -557,17 +557,6 @@ static void serial8250_clear_fifos(struct uart_8250_port *p) } } -static inline void serial8250_em485_rts_after_send(struct uart_8250_port *p) -{ - unsigned char mcr = serial8250_in_MCR(p); - - if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) - mcr |= UART_MCR_RTS; - else - mcr &= ~UART_MCR_RTS; - serial8250_out_MCR(p, mcr); -} - static enum hrtimer_restart serial8250_em485_handle_start_tx(struct hrtimer *t); static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t); @@ -632,7 +621,9 @@ static int serial8250_em485_init(struct uart_8250_port *p) p->em485->start_tx_timer.function = &serial8250_em485_handle_start_tx; p->em485->port = p; p->em485->active_timer = NULL; - serial8250_em485_rts_after_send(p); + p->em485->tx_stopped = true; + + p->rs485_stop_tx(p); return 0; } @@ -1439,9 +1430,21 @@ static void serial8250_stop_rx(struct uart_port *port) serial8250_rpm_put(up); } -static void __do_stop_tx_rs485(struct uart_8250_port *p) +/** + * serial8250_em485_stop_tx() - generic ->rs485_stop_tx() callback + * @up: uart 8250 port + * + * Generic callback usable by 8250 uart drivers to stop rs485 transmission. + */ +void serial8250_em485_stop_tx(struct uart_8250_port *p) { - serial8250_em485_rts_after_send(p); + unsigned char mcr = serial8250_in_MCR(p); + + if (p->port.rs485.flags & SER_RS485_RTS_AFTER_SEND) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + serial8250_out_MCR(p, mcr); /* * Empty the RX FIFO, we are not interested in anything @@ -1455,6 +1458,8 @@ static void __do_stop_tx_rs485(struct uart_8250_port *p) serial_port_out(&p->port, UART_IER, p->ier); } } +EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx); + static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) { struct uart_8250_em485 *em485; @@ -1467,8 +1472,9 @@ static enum hrtimer_restart serial8250_em485_handle_stop_tx(struct hrtimer *t) serial8250_rpm_get(p); spin_lock_irqsave(&p->port.lock, flags); if (em485->active_timer == &em485->stop_tx_timer) { - __do_stop_tx_rs485(p); + p->rs485_stop_tx(p); em485->active_timer = NULL; + em485->tx_stopped = true; } spin_unlock_irqrestore(&p->port.lock, flags); serial8250_rpm_put(p); @@ -1489,7 +1495,7 @@ static void __stop_tx_rs485(struct uart_8250_port *p) struct uart_8250_em485 *em485 = p->em485; /* - * __do_stop_tx_rs485 is going to set RTS according to config + * rs485_stop_tx() is going to set RTS according to config * AND flush RX FIFO if required. */ if (p->port.rs485.delay_rts_after_send > 0) { @@ -1497,8 +1503,9 @@ static void __stop_tx_rs485(struct uart_8250_port *p) start_hrtimer_ms(&em485->stop_tx_timer, p->port.rs485.delay_rts_after_send); } else { - __do_stop_tx_rs485(p); + p->rs485_stop_tx(p); em485->active_timer = NULL; + em485->tx_stopped = true; } } @@ -1572,25 +1579,42 @@ static inline void __start_tx(struct uart_port *port) } } -static inline void start_tx_rs485(struct uart_port *port) +/** + * serial8250_em485_start_tx() - generic ->rs485_start_tx() callback + * @up: uart 8250 port + * + * Generic callback usable by 8250 uart drivers to start rs485 transmission. + * Assumes that setting the RTS bit in the MCR register means RTS is high. + * (Some chips use inverse semantics.) Further assumes that reception is + * stoppable by disabling the UART_IER_RDI interrupt. (Some chips set the + * UART_LSR_DR bit even when UART_IER_RDI is disabled, foiling this approach.) + */ +void serial8250_em485_start_tx(struct uart_8250_port *up) { - struct uart_8250_port *up = up_to_u8250p(port); - struct uart_8250_em485 *em485 = up->em485; - unsigned char mcr; + unsigned char mcr = serial8250_in_MCR(up); if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX)) serial8250_stop_rx(&up->port); + if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) + mcr |= UART_MCR_RTS; + else + mcr &= ~UART_MCR_RTS; + serial8250_out_MCR(up, mcr); +} +EXPORT_SYMBOL_GPL(serial8250_em485_start_tx); + +static inline void start_tx_rs485(struct uart_port *port) +{ + struct uart_8250_port *up = up_to_u8250p(port); + struct uart_8250_em485 *em485 = up->em485; + em485->active_timer = NULL; - mcr = serial8250_in_MCR(up); - if (!!(up->port.rs485.flags & SER_RS485_RTS_ON_SEND) != - !!(mcr & UART_MCR_RTS)) { - if (up->port.rs485.flags & SER_RS485_RTS_ON_SEND) - mcr |= UART_MCR_RTS; - else - mcr &= ~UART_MCR_RTS; - serial8250_out_MCR(up, mcr); + if (em485->tx_stopped) { + em485->tx_stopped = false; + + up->rs485_start_tx(up); if (up->port.rs485.delay_rts_before_send > 0) { em485->active_timer = &em485->start_tx_timer; diff --git a/include/linux/serial_8250.h b/include/linux/serial_8250.h index 6a8e8c48c882..0901c2aa366c 100644 --- a/include/linux/serial_8250.h +++ b/include/linux/serial_8250.h @@ -81,6 +81,7 @@ struct uart_8250_em485 { struct hrtimer stop_tx_timer; /* "rs485 stop tx" timer */ struct hrtimer *active_timer; /* pointer to active timer */ struct uart_8250_port *port; /* for hrtimer callbacks */ + unsigned int tx_stopped:1; /* tx is currently stopped */ }; /* @@ -132,6 +133,8 @@ struct uart_8250_port { void (*dl_write)(struct uart_8250_port *, int); struct uart_8250_em485 *em485; + void (*rs485_start_tx)(struct uart_8250_port *); + void (*rs485_stop_tx)(struct uart_8250_port *); /* Serial port overrun backoff */ struct delayed_work overrun_backoff; -- cgit v1.2.3 From eaee41727e6d8a7d2b94421c25e82b00cb2fded5 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Mon, 2 Mar 2020 17:51:34 +0000 Subject: sysctl/sysrq: Remove __sysrq_enabled copy Many embedded boards have a disconnected TTL level serial which can generate some garbage that can lead to spurious false sysrq detects. Currently, sysrq can be either completely disabled for serial console or always disabled (with CONFIG_MAGIC_SYSRQ_SERIAL), since commit 732dbf3a6104 ("serial: do not accept sysrq characters via serial port") At Arista, we have such boards that can generate BREAK and random garbage. While disabling sysrq for serial console would solve the problem with spurious false sysrq triggers, it's also desirable to have a way to enable sysrq back. Having the way to enable sysrq was beneficial to debug lockups with a manual investigation in field and on the other side preventing false sysrq detections. As a preparation to add sysrq_toggle_support() call into uart, remove a private copy of sysrq_enabled from sysctl - it should reflect the actual status of sysrq. Furthermore, the private copy isn't correct already in case sysrq_always_enabled is true. So, remove __sysrq_enabled and use a getter-helper sysrq_mask() to check sysrq_key_op enabled status. Cc: Iurii Zaikin Cc: Jiri Slaby Cc: Luis Chamberlain Cc: Kees Cook Cc: linux-fsdevel@vger.kernel.org Signed-off-by: Dmitry Safonov Link: https://lore.kernel.org/r/20200302175135.269397-2-dima@arista.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/sysrq.c | 12 ++++++++++++ include/linux/sysrq.h | 7 +++++++ kernel/sysctl.c | 41 ++++++++++++++++++++++------------------- 3 files changed, 41 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index f724962a5906..5e0d0813da55 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -63,6 +63,18 @@ static bool sysrq_on(void) return sysrq_enabled || sysrq_always_enabled; } +/** + * sysrq_mask - Getter for sysrq_enabled mask. + * + * Return: 1 if sysrq is always enabled, enabled sysrq_key_op mask otherwise. + */ +int sysrq_mask(void) +{ + if (sysrq_always_enabled) + return 1; + return sysrq_enabled; +} + /* * A value of 1 means 'all', other nonzero values are an op mask: */ diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h index 8c71874e8485..8e159e16850f 100644 --- a/include/linux/sysrq.h +++ b/include/linux/sysrq.h @@ -50,6 +50,7 @@ int unregister_sysrq_key(int key, struct sysrq_key_op *op); struct sysrq_key_op *__sysrq_get_key_op(int key); int sysrq_toggle_support(int enable_mask); +int sysrq_mask(void); #else @@ -71,6 +72,12 @@ static inline int unregister_sysrq_key(int key, struct sysrq_key_op *op) return -EINVAL; } +static inline int sysrq_mask(void) +{ + /* Magic SysRq disabled mask */ + return 0; +} + #endif #endif /* _LINUX_SYSRQ_H */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index ad5b88a53c5a..94638f695e60 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -229,25 +229,8 @@ static int proc_dopipe_max_size(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #ifdef CONFIG_MAGIC_SYSRQ -/* Note: sysrq code uses its own private copy */ -static int __sysrq_enabled = CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE; - static int sysrq_sysctl_handler(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, - loff_t *ppos) -{ - int error; - - error = proc_dointvec(table, write, buffer, lenp, ppos); - if (error) - return error; - - if (write) - sysrq_toggle_support(__sysrq_enabled); - - return 0; -} - + void __user *buffer, size_t *lenp, loff_t *ppos); #endif static struct ctl_table kern_table[]; @@ -747,7 +730,7 @@ static struct ctl_table kern_table[] = { #ifdef CONFIG_MAGIC_SYSRQ { .procname = "sysrq", - .data = &__sysrq_enabled, + .data = NULL, .maxlen = sizeof (int), .mode = 0644, .proc_handler = sysrq_sysctl_handler, @@ -2835,6 +2818,26 @@ static int proc_dostring_coredump(struct ctl_table *table, int write, } #endif +#ifdef CONFIG_MAGIC_SYSRQ +static int sysrq_sysctl_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int tmp, ret; + + tmp = sysrq_mask(); + + ret = __do_proc_dointvec(&tmp, table, write, buffer, + lenp, ppos, NULL, NULL); + if (ret || !write) + return ret; + + if (write) + sysrq_toggle_support(tmp); + + return 0; +} +#endif + static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos, -- cgit v1.2.3 From 68af43173d3fcece70bef49cb992c64c4c68ff23 Mon Sep 17 00:00:00 2001 From: Dmitry Safonov Date: Mon, 2 Mar 2020 17:51:35 +0000 Subject: serial/sysrq: Add MAGIC_SYSRQ_SERIAL_SEQUENCE Many embedded boards have a disconnected TTL level serial which can generate some garbage that can lead to spurious false sysrq detects. Currently, sysrq can be either completely disabled for serial console or always disabled (with CONFIG_MAGIC_SYSRQ_SERIAL), since commit 732dbf3a6104 ("serial: do not accept sysrq characters via serial port") At Arista, we have such boards that can generate BREAK and random garbage. While disabling sysrq for serial console would solve the problem with spurious false sysrq triggers, it's also desirable to have a way to enable sysrq back. As a measure of balance between on and off options, add MAGIC_SYSRQ_SERIAL_SEQUENCE which is a string sequence that can enable sysrq if it follows BREAK on a serial line. The longer the string - the less likely it may be in the garbage. Having the way to enable sysrq was beneficial to debug lockups with a manual investigation in field and on the other side preventing false sysrq detections. Based-on-patch-by: Vasiliy Khoruzhick Signed-off-by: Dmitry Safonov Link: https://lore.kernel.org/r/20200302175135.269397-3-dima@arista.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 75 ++++++++++++++++++++++++++++++++++++---- include/linux/serial_core.h | 1 + lib/Kconfig.debug | 8 +++++ 3 files changed, 77 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 5444293fe2e8..863e3e0c2d39 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -20,6 +20,7 @@ #include #include /* for serial_state and serial_icounter_struct */ #include +#include #include #include #include @@ -40,6 +41,8 @@ static struct lock_class_key port_lock_key; #define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) +#define SYSRQ_TIMEOUT (HZ * 5) + static void uart_change_speed(struct tty_struct *tty, struct uart_state *state, struct ktermios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); @@ -3084,6 +3087,56 @@ void uart_insert_char(struct uart_port *port, unsigned int status, } EXPORT_SYMBOL_GPL(uart_insert_char); +#ifdef CONFIG_MAGIC_SYSRQ_SERIAL +static const char sysrq_toggle_seq[] = CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE; + +static void uart_sysrq_on(struct work_struct *w) +{ + sysrq_toggle_support(1); + pr_info("SysRq is enabled by magic sequence on serial\n"); +} +static DECLARE_WORK(sysrq_enable_work, uart_sysrq_on); + +/** + * uart_try_toggle_sysrq - Enables SysRq from serial line + * @port: uart_port structure where char(s) after BREAK met + * @ch: new character in the sequence after received BREAK + * + * Enables magic SysRq when the required sequence is met on port + * (see CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE). + * + * Returns false if @ch is out of enabling sequence and should be + * handled some other way, true if @ch was consumed. + */ +static bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) +{ + if (ARRAY_SIZE(sysrq_toggle_seq) <= 1) + return false; + + BUILD_BUG_ON(ARRAY_SIZE(sysrq_toggle_seq) >= U8_MAX); + if (sysrq_toggle_seq[port->sysrq_seq] != ch) { + port->sysrq_seq = 0; + return false; + } + + /* Without the last \0 */ + if (++port->sysrq_seq < (ARRAY_SIZE(sysrq_toggle_seq) - 1)) { + port->sysrq = jiffies + SYSRQ_TIMEOUT; + return true; + } + + schedule_work(&sysrq_enable_work); + + port->sysrq = 0; + return true; +} +#else +static inline bool uart_try_toggle_sysrq(struct uart_port *port, unsigned int ch) +{ + return false; +} +#endif + int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) { if (!IS_ENABLED(CONFIG_MAGIC_SYSRQ_SERIAL)) @@ -3093,9 +3146,13 @@ int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch) return 0; if (ch && time_before(jiffies, port->sysrq)) { - handle_sysrq(ch); - port->sysrq = 0; - return 1; + if (sysrq_mask()) { + handle_sysrq(ch); + port->sysrq = 0; + return 1; + } + if (uart_try_toggle_sysrq(port, ch)) + return 1; } port->sysrq = 0; @@ -3112,9 +3169,13 @@ int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch) return 0; if (ch && time_before(jiffies, port->sysrq)) { - port->sysrq_ch = ch; - port->sysrq = 0; - return 1; + if (sysrq_mask()) { + port->sysrq_ch = ch; + port->sysrq = 0; + return 1; + } + if (uart_try_toggle_sysrq(port, ch)) + return 1; } port->sysrq = 0; @@ -3154,7 +3215,7 @@ int uart_handle_break(struct uart_port *port) if (port->has_sysrq) { if (port->cons && port->cons->index == port->line) { if (!port->sysrq) { - port->sysrq = jiffies + HZ*5; + port->sysrq = jiffies + SYSRQ_TIMEOUT; return 1; } port->sysrq = 0; diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 52404ef1694e..1f4443db5474 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -243,6 +243,7 @@ struct uart_port { unsigned long sysrq; /* sysrq timeout */ unsigned int sysrq_ch; /* char for sysrq */ unsigned char has_sysrq; + unsigned char sysrq_seq; /* index in sysrq_toggle_seq */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 69def4a9df00..38a8f3c99579 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -431,6 +431,14 @@ config MAGIC_SYSRQ_SERIAL This option allows you to decide whether you want to enable the magic SysRq key. +config MAGIC_SYSRQ_SERIAL_SEQUENCE + string "Char sequence that enables magic SysRq over serial" + depends on MAGIC_SYSRQ_SERIAL + default "" + help + Specifies a sequence of characters that can follow BREAK to enable + SysRq on a serial console. + config DEBUG_FS bool "Debug Filesystem" help -- cgit v1.2.3 From 86f5d0f3d4995b8b8909da7eea235e5bc1ceefbe Mon Sep 17 00:00:00 2001 From: Vu Pham Date: Mon, 2 Mar 2020 16:15:19 -0800 Subject: net/mlx5: Introduce egress acl forward-to-vport capability Add HCA_CAP.egress_acl_forward_to_vport field to check whether HW supports e-switch vport's egress acl to forward packets to other e-switch vport or not. By default E-Switch egress ACL forwards eswitch vports egress packets to their corresponding NIC/VF vports. With this cap enabled, the driver is allowed to alter this behavior and forward packets to arbitrary NIC/VF vports with the following limitations: a. Multiple processing paths are supported if all of the following conditions are met: - HCA_CAP.egress_acl_forward_to_vport is set ==1. - A destination of type Flow Table only appears once, as the last destination in the list. - Vport destination is supported if HCA_CAP.egress_acl_forward_to_vport==1. Vport must not be the Uplink. b. Flow_tag not supported. c. This table is only applicable after an FDB table is created. d. Push VLAN action is not supported. e. Pop VLAN action cannot be added concurrently to this table and FDB table. This feature will be used during port failover in bonding scenario where two VFs representors are bonded to handle failover egress traffic (VM's ingress/receive traffic). Signed-off-by: Vu Pham Signed-off-by: Saeed Mahameed --- include/linux/mlx5/mlx5_ifc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 7d89ab64b372..07be46e713fc 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -738,7 +738,7 @@ struct mlx5_ifc_flow_table_eswitch_cap_bits { u8 flow_source[0x1]; u8 reserved_at_18[0x2]; u8 multi_fdb_encap[0x1]; - u8 reserved_at_1b[0x1]; + u8 egress_acl_forward_to_vport[0x1]; u8 fdb_multi_path_to_table[0x1]; u8 reserved_at_1d[0x3]; -- cgit v1.2.3 From bd673da6d933e3c8f652b011afba6cee0f8bbe45 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Mon, 2 Mar 2020 16:15:20 -0800 Subject: net/mlx5: Introduce TLS and IPSec objects enums Expose the TLS encryption key general object type enum correctly, and add the IPSec encryption key general object type enum. Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c | 2 +- include/linux/mlx5/mlx5_ifc.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c index 3fc575d1c3ec..dcea87ec5977 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/crypto.c @@ -42,7 +42,7 @@ int mlx5_create_encryption_key(struct mlx5_core_dev *mdev, MLX5_SET(encryption_key_obj, obj, key_size, general_obj_key_size); MLX5_SET(encryption_key_obj, obj, key_type, - MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_DEK); + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_TLS); MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT); MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 07be46e713fc..2e98bba12356 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -10489,7 +10489,8 @@ enum { }; enum { - MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_DEK = 0x1, + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_TLS = 0x1, + MLX5_GENERAL_OBJECT_TYPE_ENCRYPTION_KEY_TYPE_IPSEC = 0x2, }; struct mlx5_ifc_tls_static_params_bits { -- cgit v1.2.3 From dc392fc56f39a00a46d6db2d150571ccafe99734 Mon Sep 17 00:00:00 2001 From: Mark Bloch Date: Mon, 2 Mar 2020 16:15:21 -0800 Subject: net/mlx5: Expose link speed directly Expose port rate as part of the port speed register fields. Signed-off-by: Mark Bloch Signed-off-by: Saeed Mahameed --- include/linux/mlx5/mlx5_ifc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 2e98bba12356..d0a678c82ccd 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -8423,7 +8423,8 @@ struct mlx5_ifc_ptys_reg_bits { u8 proto_mask[0x3]; u8 an_status[0x4]; - u8 reserved_at_24[0x1c]; + u8 reserved_at_24[0xc]; + u8 data_rate_oper[0x10]; u8 ext_eth_proto_capability[0x20]; -- cgit v1.2.3 From e0ebd8eb36ed850a22a9a0ca83edc4a40ad67c16 Mon Sep 17 00:00:00 2001 From: Eli Cohen Date: Mon, 2 Mar 2020 16:15:22 -0800 Subject: net/mlx5: HW bit for goto chain offload support Add the HW bit definition indecating goto chain offload support. Signed-off-by: Eli Cohen Reviewed-by: Roi Dayan Signed-off-by: Saeed Mahameed --- include/linux/mlx5/mlx5_ifc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index d0a678c82ccd..9b8ff4e57002 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -414,7 +414,8 @@ struct mlx5_ifc_flow_table_prop_layout_bits { u8 reserved_at_16[0x1]; u8 table_miss_action_domain[0x1]; u8 termination_table[0x1]; - u8 reserved_at_19[0x7]; + u8 reformat_and_fwd_to_table[0x1]; + u8 reserved_at_1a[0x6]; u8 reserved_at_20[0x2]; u8 log_max_ft_size[0x6]; u8 log_max_modify_header_context[0x8]; -- cgit v1.2.3 From 00bce3fb0642b38fa2e5db3217526c3e0d5952ca Mon Sep 17 00:00:00 2001 From: Alain Michaud Date: Thu, 5 Mar 2020 16:14:59 +0000 Subject: Bluetooth: Enable erroneous data reporting if WBS is supported This change introduces a wide band speech setting which allows higher level clients to query the local controller support for wide band speech as well as set the setting state when the radio is powered off. Internally, this setting controls if erroneous data reporting is enabled on the controller. Signed-off-by: Alain Michaud Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btusb.c | 2 +- include/net/bluetooth/hci.h | 16 +++++++++- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/mgmt.h | 4 ++- net/bluetooth/hci_core.c | 23 ++++++++++++++ net/bluetooth/hci_event.c | 39 ++++++++++++++++++++++++ net/bluetooth/mgmt.c | 65 ++++++++++++++++++++++++++++++++++++++-- 7 files changed, 145 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 48e78fdc8e83..4b12c34f0b22 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -3868,7 +3868,7 @@ static int btusb_probe(struct usb_interface *intf, data->isoc = NULL; if (id->driver_info & BTUSB_WIDEBAND_SPEECH) - set_bit(HCI_QUIRK_WIDE_BAND_SPEECH_SUPPORTED, &hdev->quirks); + set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); if (id->driver_info & BTUSB_DIGIANSWER) { data->cmdreq_type = USB_TYPE_VENDOR; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 0b3ebd35681d..4e86f1bb7a87 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -213,7 +213,7 @@ enum { * * This quirk must be set before hci_register_dev is called. */ - HCI_QUIRK_WIDE_BAND_SPEECH_SUPPORTED, + HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, }; /* HCI device flags */ @@ -286,6 +286,7 @@ enum { HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, HCI_LE_SCAN_INTERRUPTED, + HCI_WIDEBAND_SPEECH_ENABLED, HCI_DUT_MODE, HCI_VENDOR_DIAG, @@ -1095,6 +1096,19 @@ struct hci_rp_read_inq_rsp_tx_power { __s8 tx_power; } __packed; +#define HCI_OP_READ_DEF_ERR_DATA_REPORTING 0x0c5a + #define ERR_DATA_REPORTING_DISABLED 0x00 + #define ERR_DATA_REPORTING_ENABLED 0x01 +struct hci_rp_read_def_err_data_reporting { + __u8 status; + __u8 err_data_reporting; +} __packed; + +#define HCI_OP_WRITE_DEF_ERR_DATA_REPORTING 0x0c5b +struct hci_cp_write_def_err_data_reporting { + __u8 err_data_reporting; +} __packed; + #define HCI_OP_SET_EVENT_MASK_PAGE_2 0x0c63 #define HCI_OP_READ_LOCATION_DATA 0x0c64 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dcc0dc6e2624..c498ac113930 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -260,6 +260,7 @@ struct hci_dev { __u8 stored_num_keys; __u8 io_capability; __s8 inq_tx_power; + __u8 err_data_reporting; __u16 page_scan_interval; __u16 page_scan_window; __u8 page_scan_type; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index f69f88e8e109..f41cd87550dc 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -102,7 +102,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_CONFIGURATION 0x00004000 #define MGMT_SETTING_STATIC_ADDRESS 0x00008000 #define MGMT_SETTING_PHY_CONFIGURATION 0x00010000 -#define MGMT_SETTING_WIDE_BAND_SPEECH 0x00020000 +#define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -672,6 +672,8 @@ struct mgmt_cp_set_blocked_keys { } __packed; #define MGMT_OP_SET_BLOCKED_KEYS_SIZE 2 +#define MGMT_OP_SET_WIDEBAND_SPEECH 0x0047 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6a88954e67c0..9ce98762559b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -603,6 +603,9 @@ static int hci_init3_req(struct hci_request *req, unsigned long opt) if (hdev->commands[8] & 0x01) hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL); + if (hdev->commands[18] & 0x02) + hci_req_add(req, HCI_OP_READ_DEF_ERR_DATA_REPORTING, 0, NULL); + /* Some older Broadcom based Bluetooth 1.2 controllers do not * support the Read Page Scan Type command. Check support for * this command in the bit mask of supported commands. @@ -838,6 +841,26 @@ static int hci_init4_req(struct hci_request *req, unsigned long opt) sizeof(support), &support); } + /* Set erroneous data reporting if supported to the wideband speech + * setting value + */ + if (hdev->commands[18] & 0x04) { + bool enabled = hci_dev_test_flag(hdev, + HCI_WIDEBAND_SPEECH_ENABLED); + + if (enabled != + (hdev->err_data_reporting == ERR_DATA_REPORTING_ENABLED)) { + struct hci_cp_write_def_err_data_reporting cp; + + cp.err_data_reporting = enabled ? + ERR_DATA_REPORTING_ENABLED : + ERR_DATA_REPORTING_DISABLED; + + hci_req_add(req, HCI_OP_WRITE_DEF_ERR_DATA_REPORTING, + sizeof(cp), &cp); + } + } + /* Set Suggested Default Data Length to maximum if supported */ if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) { struct hci_cp_le_write_def_data_len cp; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a40ed31f6eb8..b9186026508e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -901,6 +901,37 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, hdev->inq_tx_power = rp->tx_power; } +static void hci_cc_read_def_err_data_reporting(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_def_err_data_reporting *rp = (void *)skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->err_data_reporting = rp->err_data_reporting; +} + +static void hci_cc_write_def_err_data_reporting(struct hci_dev *hdev, + struct sk_buff *skb) +{ + __u8 status = *((__u8 *)skb->data); + struct hci_cp_write_def_err_data_reporting *cp; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + cp = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_ERR_DATA_REPORTING); + if (!cp) + return; + + hdev->err_data_reporting = cp->err_data_reporting; +} + static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_pin_code_reply *rp = (void *) skb->data; @@ -3302,6 +3333,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb, hci_cc_read_inq_rsp_tx_power(hdev, skb); break; + case HCI_OP_READ_DEF_ERR_DATA_REPORTING: + hci_cc_read_def_err_data_reporting(hdev, skb); + break; + + case HCI_OP_WRITE_DEF_ERR_DATA_REPORTING: + hci_cc_write_def_err_data_reporting(hdev, skb); + break; + case HCI_OP_PIN_CODE_REPLY: hci_cc_pin_code_reply(hdev, skb); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1002c657768a..4abb5daeeca8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -107,6 +107,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_READ_EXT_INFO, MGMT_OP_SET_APPEARANCE, MGMT_OP_SET_BLOCKED_KEYS, + MGMT_OP_SET_WIDEBAND_SPEECH, }; static const u16 mgmt_events[] = { @@ -763,9 +764,9 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (lmp_sc_capable(hdev)) settings |= MGMT_SETTING_SECURE_CONN; - if (test_bit(HCI_QUIRK_WIDE_BAND_SPEECH_SUPPORTED, + if (test_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks)) - settings |= MGMT_SETTING_WIDE_BAND_SPEECH; + settings |= MGMT_SETTING_WIDEBAND_SPEECH; } if (lmp_le_capable(hdev)) { @@ -850,6 +851,9 @@ static u32 get_current_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_STATIC_ADDRESS; } + if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED)) + settings |= MGMT_SETTING_WIDEBAND_SPEECH; + return settings; } @@ -3593,6 +3597,62 @@ static int set_blocked_keys(struct sock *sk, struct hci_dev *hdev, void *data, err, NULL, 0); } +static int set_wideband_speech(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_mode *cp = data; + int err; + bool changed = false; + + BT_DBG("request for %s", hdev->name); + + if (!test_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks)) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_WIDEBAND_SPEECH, + MGMT_STATUS_NOT_SUPPORTED); + + if (cp->val != 0x00 && cp->val != 0x01) + return mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_WIDEBAND_SPEECH, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (pending_find(MGMT_OP_SET_WIDEBAND_SPEECH, hdev)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_WIDEBAND_SPEECH, + MGMT_STATUS_BUSY); + goto unlock; + } + + if (hdev_is_powered(hdev) && + !!cp->val != hci_dev_test_flag(hdev, + HCI_WIDEBAND_SPEECH_ENABLED)) { + err = mgmt_cmd_status(sk, hdev->id, + MGMT_OP_SET_WIDEBAND_SPEECH, + MGMT_STATUS_REJECTED); + goto unlock; + } + + if (cp->val) + changed = !hci_dev_test_and_set_flag(hdev, + HCI_WIDEBAND_SPEECH_ENABLED); + else + changed = hci_dev_test_and_clear_flag(hdev, + HCI_WIDEBAND_SPEECH_ENABLED); + + err = send_settings_rsp(sk, MGMT_OP_SET_WIDEBAND_SPEECH, hdev); + if (err < 0) + goto unlock; + + if (changed) + err = new_settings(hdev, sk); + +unlock: + hci_dev_unlock(hdev); + return err; +} + static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status, u16 opcode, struct sk_buff *skb) { @@ -6994,6 +7054,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = { { set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE }, { set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE, HCI_MGMT_VAR_LEN }, + { set_wideband_speech, MGMT_SETTING_SIZE }, }; void mgmt_index_added(struct hci_dev *hdev) -- cgit v1.2.3 From 145720963b6c68d0c4054112c09050995259b8f8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 2 Mar 2020 16:56:19 -0800 Subject: Bluetooth: L2CAP: Add definitions for Enhanced Credit Based Mode This introduces the definitions for the new L2CAP mode called Enhanced Credit Based Mode. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 61dc731d5666..2f051d01f35d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -119,6 +119,10 @@ struct l2cap_conninfo { #define L2CAP_LE_CONN_REQ 0x14 #define L2CAP_LE_CONN_RSP 0x15 #define L2CAP_LE_CREDITS 0x16 +#define L2CAP_ECRED_CONN_REQ 0x17 +#define L2CAP_ECRED_CONN_RSP 0x18 +#define L2CAP_ECRED_RECONF_REQ 0x19 +#define L2CAP_ECRED_RECONF_RSP 0x1a /* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 @@ -359,6 +363,7 @@ struct l2cap_conf_rfc { * ever be used in the BR/EDR configuration phase. */ #define L2CAP_MODE_LE_FLOWCTL 0x80 +#define L2CAP_MODE_EXT_FLOWCTL 0x81 struct l2cap_conf_efs { __u8 id; @@ -483,6 +488,39 @@ struct l2cap_le_credits { __le16 credits; } __packed; +#define L2CAP_ECRED_MIN_MTU 64 +#define L2CAP_ECRED_MIN_MPS 64 + +struct l2cap_ecred_conn_req { + __le16 psm; + __le16 mtu; + __le16 mps; + __le16 credits; + __le16 scid[0]; +} __packed; + +struct l2cap_ecred_conn_rsp { + __le16 mtu; + __le16 mps; + __le16 credits; + __le16 result; + __le16 dcid[0]; +}; + +struct l2cap_ecred_reconf_req { + __le16 mtu; + __le16 mps; + __le16 scid[0]; +} __packed; + +#define L2CAP_RECONF_SUCCESS 0x0000 +#define L2CAP_RECONF_INVALID_MTU 0x0001 +#define L2CAP_RECONF_INVALID_MPS 0x0002 + +struct l2cap_ecred_reconf_rsp { + __le16 result; +} __packed; + /* ----- L2CAP channels and connections ----- */ struct l2cap_seq_list { __u16 head; @@ -724,6 +762,7 @@ enum { FLAG_EFS_ENABLE, FLAG_DEFER_SETUP, FLAG_LE_CONN_REQ_SENT, + FLAG_ECRED_CONN_REQ_SENT, FLAG_PENDING_SECURITY, FLAG_HOLD_HCI_CONN, }; -- cgit v1.2.3 From 15f02b91056253e8cdc592888f431da0731337b8 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 2 Mar 2020 16:56:20 -0800 Subject: Bluetooth: L2CAP: Add initial code for Enhanced Credit Based Mode This adds the initial code for Enhanced Credit Based Mode which introduces a new socket mode called L2CAP_MODE_EXT_FLOWCTL, which for the most part work the same as L2CAP_MODE_LE_FLOWCTL but uses different PDUs to setup the connections and also works over BR/EDR. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 4 + net/bluetooth/l2cap_core.c | 545 ++++++++++++++++++++++++++++++++++++++++-- net/bluetooth/l2cap_sock.c | 23 +- 3 files changed, 552 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 2f051d01f35d..f466cdcc6742 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -294,6 +294,8 @@ struct l2cap_conn_rsp { #define L2CAP_CR_LE_ENCRYPTION 0x0008 #define L2CAP_CR_LE_INVALID_SCID 0x0009 #define L2CAP_CR_LE_SCID_IN_USE 0X000A +#define L2CAP_CR_LE_UNACCEPT_PARAMS 0X000B +#define L2CAP_CR_LE_INVALID_PARAMS 0X000C /* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 @@ -962,6 +964,7 @@ void l2cap_cleanup_sockets(void); bool l2cap_is_socket(struct socket *sock); void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan); +void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); @@ -971,6 +974,7 @@ struct l2cap_chan *l2cap_chan_create(void); void l2cap_chan_close(struct l2cap_chan *chan, int reason); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); +int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu); int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan, bool initiator); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 4286483beada..6b24db77b5df 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -535,6 +535,17 @@ static void l2cap_le_flowctl_init(struct l2cap_chan *chan, u16 tx_credits) skb_queue_head_init(&chan->tx_q); } +static void l2cap_ecred_init(struct l2cap_chan *chan, u16 tx_credits) +{ + l2cap_le_flowctl_init(chan, tx_credits); + + /* L2CAP implementations shall support a minimum MPS of 64 octets */ + if (chan->mps < L2CAP_ECRED_MIN_MPS) { + chan->mps = L2CAP_ECRED_MIN_MPS; + chan->rx_credits = (chan->imtu / chan->mps) + 1; + } +} + void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) { BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, @@ -641,6 +652,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) break; case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: skb_queue_purge(&chan->tx_q); break; @@ -707,6 +719,27 @@ static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan) &rsp); } +static void l2cap_chan_ecred_connect_reject(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct l2cap_ecred_conn_rsp rsp; + u16 result; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) + result = L2CAP_CR_LE_AUTHORIZATION; + else + result = L2CAP_CR_LE_BAD_PSM; + + l2cap_state_change(chan, BT_DISCONN); + + memset(&rsp, 0, sizeof(rsp)); + + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), + &rsp); +} + static void l2cap_chan_connect_reject(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -752,8 +785,16 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) { if (conn->hcon->type == ACL_LINK) l2cap_chan_connect_reject(chan); - else if (conn->hcon->type == LE_LINK) - l2cap_chan_le_connect_reject(chan); + else if (conn->hcon->type == LE_LINK) { + switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: + l2cap_chan_le_connect_reject(chan); + break; + case L2CAP_MODE_EXT_FLOWCTL: + l2cap_chan_ecred_connect_reject(chan); + break; + } + } } l2cap_chan_del(chan, reason); @@ -1276,8 +1317,13 @@ static void l2cap_chan_ready(struct l2cap_chan *chan) chan->conf_state = 0; __clear_chan_timer(chan); - if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits) - chan->ops->suspend(chan); + switch (chan->mode) { + case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: + if (!chan->tx_credits) + chan->ops->suspend(chan); + break; + } chan->state = BT_CONNECTED; @@ -1309,6 +1355,31 @@ static void l2cap_le_connect(struct l2cap_chan *chan) sizeof(req), &req); } +static void l2cap_ecred_connect(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct { + struct l2cap_ecred_conn_req req; + __le16 scid; + } __packed pdu; + + if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) + return; + + l2cap_ecred_init(chan, 0); + + pdu.req.psm = chan->psm; + pdu.req.mtu = cpu_to_le16(chan->imtu); + pdu.req.mps = cpu_to_le16(chan->mps); + pdu.req.credits = cpu_to_le16(chan->rx_credits); + pdu.scid = cpu_to_le16(chan->scid); + + chan->ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_CONN_REQ, + sizeof(pdu), &pdu); +} + static void l2cap_le_start(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -1321,8 +1392,12 @@ static void l2cap_le_start(struct l2cap_chan *chan) return; } - if (chan->state == BT_CONNECT) - l2cap_le_connect(chan); + if (chan->state == BT_CONNECT) { + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) + l2cap_ecred_connect(chan); + else + l2cap_le_connect(chan); + } } static void l2cap_start_connection(struct l2cap_chan *chan) @@ -2508,6 +2583,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) switch (chan->mode) { case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: /* Check outgoing MTU */ if (len > chan->omtu) return -EMSGSIZE; @@ -3776,6 +3852,45 @@ void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan) &rsp); } +void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan) +{ + struct { + struct l2cap_ecred_conn_rsp rsp; + __le16 dcid[5]; + } __packed pdu; + struct l2cap_conn *conn = chan->conn; + u16 ident = chan->ident; + int i = 0; + + if (!ident) + return; + + BT_DBG("chan %p ident %d", chan, ident); + + pdu.rsp.mtu = cpu_to_le16(chan->imtu); + pdu.rsp.mps = cpu_to_le16(chan->mps); + pdu.rsp.credits = cpu_to_le16(chan->rx_credits); + pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS); + + mutex_lock(&conn->chan_lock); + + list_for_each_entry(chan, &conn->chan_l, list) { + if (chan->ident != ident) + continue; + + /* Reset ident so only one response is sent */ + chan->ident = 0; + + /* Include all channels pending with the same ident */ + pdu.dcid[i++] = cpu_to_le16(chan->scid); + } + + mutex_unlock(&conn->chan_lock); + + l2cap_send_cmd(conn, ident, L2CAP_ECRED_CONN_RSP, + sizeof(pdu.rsp) + i * sizeof(__le16), &pdu); +} + void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) { struct l2cap_conn_rsp rsp; @@ -5718,6 +5833,351 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn, return 0; } +static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_ecred_conn_req *req = (void *) data; + struct { + struct l2cap_ecred_conn_rsp rsp; + __le16 dcid[5]; + } __packed pdu; + struct l2cap_chan *chan, *pchan; + u16 credits, mtu, mps; + __le16 psm; + u8 result, len = 0; + int i, num_scid; + bool defer = false; + + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { + result = L2CAP_CR_LE_INVALID_PARAMS; + goto response; + } + + mtu = __le16_to_cpu(req->mtu); + mps = __le16_to_cpu(req->mps); + + if (mtu < L2CAP_ECRED_MIN_MTU || mps < L2CAP_ECRED_MIN_MPS) { + result = L2CAP_CR_LE_UNACCEPT_PARAMS; + goto response; + } + + psm = req->psm; + credits = 0; + + BT_DBG("psm 0x%2.2x mtu %u mps %u", __le16_to_cpu(psm), mtu, mps); + + memset(&pdu, 0, sizeof(pdu)); + + /* Check if we have socket listening on psm */ + pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, + &conn->hcon->dst, LE_LINK); + if (!pchan) { + result = L2CAP_CR_LE_BAD_PSM; + goto response; + } + + mutex_lock(&conn->chan_lock); + l2cap_chan_lock(pchan); + + if (!smp_sufficient_security(conn->hcon, pchan->sec_level, + SMP_ALLOW_STK)) { + result = L2CAP_CR_LE_AUTHENTICATION; + goto unlock; + } + + result = L2CAP_CR_LE_SUCCESS; + cmd_len -= sizeof(req); + num_scid = cmd_len / sizeof(u16); + + for (i = 0; i < num_scid; i++) { + u16 scid = __le16_to_cpu(req->scid[i]); + + BT_DBG("scid[%d] 0x%4.4x", i, scid); + + pdu.dcid[i] = 0x0000; + len += sizeof(*pdu.dcid); + + /* Check for valid dynamic CID range */ + if (scid < L2CAP_CID_DYN_START || scid > L2CAP_CID_LE_DYN_END) { + result = L2CAP_CR_LE_INVALID_SCID; + continue; + } + + /* Check if we already have channel with that dcid */ + if (__l2cap_get_chan_by_dcid(conn, scid)) { + result = L2CAP_CR_LE_SCID_IN_USE; + continue; + } + + chan = pchan->ops->new_connection(pchan); + if (!chan) { + result = L2CAP_CR_LE_NO_MEM; + continue; + } + + bacpy(&chan->src, &conn->hcon->src); + bacpy(&chan->dst, &conn->hcon->dst); + chan->src_type = bdaddr_src_type(conn->hcon); + chan->dst_type = bdaddr_dst_type(conn->hcon); + chan->psm = psm; + chan->dcid = scid; + chan->omtu = mtu; + chan->remote_mps = mps; + + __l2cap_chan_add(conn, chan); + + l2cap_ecred_init(chan, __le16_to_cpu(req->credits)); + + /* Init response */ + if (!pdu.rsp.credits) { + pdu.rsp.mtu = cpu_to_le16(chan->imtu); + pdu.rsp.mps = cpu_to_le16(chan->mps); + pdu.rsp.credits = cpu_to_le16(chan->rx_credits); + } + + pdu.dcid[i] = cpu_to_le16(chan->scid); + + __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); + + chan->ident = cmd->ident; + + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { + l2cap_state_change(chan, BT_CONNECT2); + defer = true; + chan->ops->defer(chan); + } else { + l2cap_chan_ready(chan); + } + } + +unlock: + l2cap_chan_unlock(pchan); + mutex_unlock(&conn->chan_lock); + l2cap_chan_put(pchan); + +response: + pdu.rsp.result = cpu_to_le16(result); + + if (defer) + return 0; + + l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_CONN_RSP, + sizeof(pdu.rsp) + len, &pdu); + + return 0; +} + +static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_ecred_conn_rsp *rsp = (void *) data; + struct hci_conn *hcon = conn->hcon; + u16 mtu, mps, credits, result; + struct l2cap_chan *chan; + int err = 0, sec_level; + int i = 0; + + if (cmd_len < sizeof(*rsp)) + return -EPROTO; + + mtu = __le16_to_cpu(rsp->mtu); + mps = __le16_to_cpu(rsp->mps); + credits = __le16_to_cpu(rsp->credits); + result = __le16_to_cpu(rsp->result); + + BT_DBG("mtu %u mps %u credits %u result 0x%4.4x", mtu, mps, credits, + result); + + mutex_lock(&conn->chan_lock); + + cmd_len -= sizeof(*rsp); + + list_for_each_entry(chan, &conn->chan_l, list) { + u16 dcid; + + if (chan->ident != cmd->ident || + chan->mode != L2CAP_MODE_EXT_FLOWCTL || + chan->state == BT_CONNECTED) + continue; + + l2cap_chan_lock(chan); + + /* Check that there is a dcid for each pending channel */ + if (cmd_len < sizeof(dcid)) { + l2cap_chan_del(chan, ECONNREFUSED); + l2cap_chan_unlock(chan); + continue; + } + + dcid = __le16_to_cpu(rsp->dcid[i++]); + cmd_len -= sizeof(u16); + + BT_DBG("dcid[%d] 0x%4.4x", i, dcid); + + /* Check if dcid is already in use */ + if (dcid && __l2cap_get_chan_by_dcid(conn, dcid)) { + /* If a device receives a + * L2CAP_CREDIT_BASED_CONNECTION_RSP packet with an + * already-assigned Destination CID, then both the + * original channel and the new channel shall be + * immediately discarded and not used. + */ + l2cap_chan_del(chan, ECONNREFUSED); + l2cap_chan_unlock(chan); + chan = __l2cap_get_chan_by_dcid(conn, dcid); + l2cap_chan_lock(chan); + l2cap_chan_del(chan, ECONNRESET); + l2cap_chan_unlock(chan); + continue; + } + + switch (result) { + case L2CAP_CR_LE_AUTHENTICATION: + case L2CAP_CR_LE_ENCRYPTION: + /* If we already have MITM protection we can't do + * anything. + */ + if (hcon->sec_level > BT_SECURITY_MEDIUM) { + l2cap_chan_del(chan, ECONNREFUSED); + break; + } + + sec_level = hcon->sec_level + 1; + if (chan->sec_level < sec_level) + chan->sec_level = sec_level; + + /* We'll need to send a new Connect Request */ + clear_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags); + + smp_conn_security(hcon, chan->sec_level); + break; + + case L2CAP_CR_LE_BAD_PSM: + l2cap_chan_del(chan, ECONNREFUSED); + break; + + default: + /* If dcid was not set it means channels was refused */ + if (!dcid) { + l2cap_chan_del(chan, ECONNREFUSED); + break; + } + + chan->ident = 0; + chan->dcid = dcid; + chan->omtu = mtu; + chan->remote_mps = mps; + chan->tx_credits = credits; + l2cap_chan_ready(chan); + break; + } + + l2cap_chan_unlock(chan); + } + + mutex_unlock(&conn->chan_lock); + + return err; +} + +static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_ecred_reconf_req *req = (void *) data; + struct l2cap_ecred_reconf_rsp rsp; + u16 mtu, mps, result; + struct l2cap_chan *chan; + int i, num_scid; + + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { + result = L2CAP_CR_LE_INVALID_PARAMS; + goto respond; + } + + mtu = __le16_to_cpu(req->mtu); + mps = __le16_to_cpu(req->mps); + + BT_DBG("mtu %u mps %u", mtu, mps); + + if (mtu < L2CAP_ECRED_MIN_MTU) { + result = L2CAP_RECONF_INVALID_MTU; + goto respond; + } + + if (mps < L2CAP_ECRED_MIN_MPS) { + result = L2CAP_RECONF_INVALID_MPS; + goto respond; + } + + cmd_len -= sizeof(*req); + num_scid = cmd_len / sizeof(u16); + result = L2CAP_RECONF_SUCCESS; + + for (i = 0; i < num_scid; i++) { + u16 scid; + + scid = __le16_to_cpu(req->scid[i]); + if (!scid) + return -EPROTO; + + chan = __l2cap_get_chan_by_dcid(conn, scid); + if (!chan) + continue; + + /* If the MTU value is decreased for any of the included + * channels, then the receiver shall disconnect all + * included channels. + */ + if (chan->omtu > mtu) { + BT_ERR("chan %p decreased MTU %u -> %u", chan, + chan->omtu, mtu); + result = L2CAP_RECONF_INVALID_MTU; + } + + chan->omtu = mtu; + chan->remote_mps = mps; + } + +respond: + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_ECRED_RECONF_RSP, sizeof(rsp), + &rsp); + + return 0; +} + +static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + u8 *data) +{ + struct l2cap_chan *chan; + struct l2cap_ecred_conn_rsp *rsp = (void *) data; + u16 result; + + if (cmd_len < sizeof(*rsp)) + return -EPROTO; + + result = __le16_to_cpu(rsp->result); + + BT_DBG("result 0x%4.4x", rsp->result); + + if (!result) + return 0; + + list_for_each_entry(chan, &conn->chan_l, list) { + if (chan->ident != cmd->ident) + continue; + + l2cap_chan_del(chan, ECONNRESET); + } + + return 0; +} + static inline int l2cap_le_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -5773,6 +6233,22 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, err = l2cap_le_credits(conn, cmd, cmd_len, data); break; + case L2CAP_ECRED_CONN_REQ: + err = l2cap_ecred_conn_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_ECRED_CONN_RSP: + err = l2cap_ecred_conn_rsp(conn, cmd, cmd_len, data); + break; + + case L2CAP_ECRED_RECONF_REQ: + err = l2cap_ecred_reconf_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_ECRED_RECONF_RSP: + err = l2cap_ecred_reconf_rsp(conn, cmd, cmd_len, data); + break; + case L2CAP_DISCONN_REQ: err = l2cap_disconnect_req(conn, cmd, cmd_len, data); break; @@ -6815,11 +7291,13 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) struct l2cap_le_credits pkt; u16 return_credits; - return_credits = ((chan->imtu / chan->mps) + 1) - chan->rx_credits; + return_credits = (chan->imtu / chan->mps) + 1; - if (!return_credits) + if (chan->rx_credits >= return_credits) return; + return_credits -= chan->rx_credits; + BT_DBG("chan %p returning %u credits to sender", chan, return_credits); chan->rx_credits += return_credits; @@ -6832,7 +7310,7 @@ static void l2cap_chan_le_send_credits(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt); } -static int l2cap_le_recv(struct l2cap_chan *chan, struct sk_buff *skb) +static int l2cap_ecred_recv(struct l2cap_chan *chan, struct sk_buff *skb) { int err; @@ -6847,7 +7325,7 @@ static int l2cap_le_recv(struct l2cap_chan *chan, struct sk_buff *skb) return err; } -static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) +static int l2cap_ecred_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) { int err; @@ -6895,7 +7373,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) } if (skb->len == sdu_len) - return l2cap_le_recv(chan, skb); + return l2cap_ecred_recv(chan, skb); chan->sdu = skb; chan->sdu_len = sdu_len; @@ -6927,7 +7405,7 @@ static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) skb = NULL; if (chan->sdu->len == chan->sdu_len) { - err = l2cap_le_recv(chan, chan->sdu); + err = l2cap_ecred_recv(chan, chan->sdu); if (!err) { chan->sdu = NULL; chan->sdu_last_frag = NULL; @@ -6988,7 +7466,8 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid, switch (chan->mode) { case L2CAP_MODE_LE_FLOWCTL: - if (l2cap_le_data_rcv(chan, skb) < 0) + case L2CAP_MODE_EXT_FLOWCTL: + if (l2cap_ecred_data_rcv(chan, skb) < 0) goto drop; goto done; @@ -7215,8 +7694,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, struct hci_dev *hdev; int err; - BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst, - dst_type, __le16_to_cpu(psm)); + BT_DBG("%pMR -> %pMR (type %u) psm 0x%4.4x mode 0x%2.2x", &chan->src, + dst, dst_type, __le16_to_cpu(psm), chan->mode); hdev = hci_get_route(dst, &chan->src, chan->src_type); if (!hdev) @@ -7244,6 +7723,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, case L2CAP_MODE_BASIC: break; case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -7369,6 +7849,38 @@ done: } EXPORT_SYMBOL_GPL(l2cap_chan_connect); +static void l2cap_ecred_reconfigure(struct l2cap_chan *chan) +{ + struct l2cap_conn *conn = chan->conn; + struct { + struct l2cap_ecred_reconf_req req; + __le16 scid; + } pdu; + + pdu.req.mtu = cpu_to_le16(chan->imtu); + pdu.req.mps = cpu_to_le16(chan->mps); + pdu.scid = cpu_to_le16(chan->scid); + + chan->ident = l2cap_get_ident(conn); + + l2cap_send_cmd(conn, chan->ident, L2CAP_ECRED_RECONF_REQ, + sizeof(pdu), &pdu); +} + +int l2cap_chan_reconfigure(struct l2cap_chan *chan, __u16 mtu) +{ + if (chan->imtu > mtu) + return -EINVAL; + + BT_DBG("chan %p mtu 0x%4.4x", chan, mtu); + + chan->imtu = mtu; + + l2cap_ecred_reconfigure(chan); + + return 0; +} + /* ---- L2CAP interface with lower layer (HCI) ---- */ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) @@ -7580,7 +8092,8 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) else __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } else if (chan->state == BT_CONNECT2 && - chan->mode != L2CAP_MODE_LE_FLOWCTL) { + !(chan->mode == L2CAP_MODE_EXT_FLOWCTL || + chan->mode == L2CAP_MODE_LE_FLOWCTL)) { struct l2cap_conn_rsp rsp; __u16 res, stat; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 305710446e66..44114db219e1 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -232,7 +232,7 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, return -EINVAL; } - if (chan->psm && bdaddr_type_is_le(chan->src_type)) + if (chan->psm && bdaddr_type_is_le(chan->src_type) && !chan->mode) chan->mode = L2CAP_MODE_LE_FLOWCTL; err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), @@ -273,6 +273,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) switch (chan->mode) { case L2CAP_MODE_BASIC: case L2CAP_MODE_LE_FLOWCTL: + case L2CAP_MODE_EXT_FLOWCTL: break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -427,6 +428,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, opts.max_tx = chan->max_tx; opts.txwin_size = chan->tx_win; + BT_DBG("mode 0x%2.2x", chan->mode); + len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) err = -EFAULT; @@ -707,6 +710,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, break; } + BT_DBG("mode 0x%2.2x", chan->mode); + chan->imtu = opts.imtu; chan->omtu = opts.omtu; chan->fcs = opts.fcs; @@ -939,7 +944,8 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; } - if (sk->sk_state == BT_CONNECTED) { + if (chan->mode == L2CAP_MODE_LE_FLOWCTL && + sk->sk_state == BT_CONNECTED) { err = -EISCONN; break; } @@ -949,7 +955,12 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; } - chan->imtu = opt; + if (chan->mode == L2CAP_MODE_EXT_FLOWCTL && + sk->sk_state == BT_CONNECTED) + err = l2cap_chan_reconfigure(chan, opt); + else + chan->imtu = opt; + break; default: @@ -1004,7 +1015,11 @@ static int l2cap_sock_recvmsg(struct socket *sock, struct msghdr *msg, if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - if (bdaddr_type_is_le(pi->chan->src_type)) { + if (pi->chan->mode == L2CAP_MODE_EXT_FLOWCTL) { + sk->sk_state = BT_CONNECTED; + pi->chan->state = BT_CONNECTED; + __l2cap_ecred_conn_rsp_defer(pi->chan); + } if (bdaddr_type_is_le(pi->chan->src_type)) { sk->sk_state = BT_CONNECTED; pi->chan->state = BT_CONNECTED; __l2cap_le_connect_rsp_defer(pi->chan); -- cgit v1.2.3 From 4be5ca67d59d707a4b1c8608ca230ad65aa4f232 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Mon, 2 Mar 2020 16:56:21 -0800 Subject: Bluetooth: L2CAP: Add module option to enable ECRED mode This should make it safe to have the code upstream without affecting stable systems since there are a few details not sort out with ECRED mode e.g: how to initiate multiple connections at once. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_core.c | 15 +++++++++++++++ net/bluetooth/l2cap_sock.c | 5 +++++ 3 files changed, 21 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f466cdcc6742..537aaead259f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -958,6 +958,7 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan) } extern bool disable_ertm; +extern bool enable_ecred; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6b24db77b5df..697c0f7f2c1a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -45,6 +45,7 @@ #define LE_FLOWCTL_MAX_CREDITS 65535 bool disable_ertm; +bool enable_ecred; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; @@ -5849,6 +5850,9 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn, int i, num_scid; bool defer = false; + if (!enable_ecred) + return -EINVAL; + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { result = L2CAP_CR_LE_INVALID_PARAMS; goto response; @@ -6092,6 +6096,9 @@ static inline int l2cap_ecred_reconf_req(struct l2cap_conn *conn, struct l2cap_chan *chan; int i, num_scid; + if (!enable_ecred) + return -EINVAL; + if (cmd_len < sizeof(*req) || cmd_len - sizeof(*req) % sizeof(u16)) { result = L2CAP_CR_LE_INVALID_PARAMS; goto respond; @@ -7723,7 +7730,12 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, case L2CAP_MODE_BASIC: break; case L2CAP_MODE_LE_FLOWCTL: + break; case L2CAP_MODE_EXT_FLOWCTL: + if (!enable_ecred) { + err = -EOPNOTSUPP; + goto done; + } break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: @@ -8301,3 +8313,6 @@ void l2cap_exit(void) module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); + +module_param(enable_ecred, bool, 0644); +MODULE_PARM_DESC(enable_ecred, "Enable enhanced credit flow control mode"); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 44114db219e1..0c636be3469e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -273,7 +273,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog) switch (chan->mode) { case L2CAP_MODE_BASIC: case L2CAP_MODE_LE_FLOWCTL: + break; case L2CAP_MODE_EXT_FLOWCTL: + if (!enable_ecred) { + err = -EOPNOTSUPP; + goto done; + } break; case L2CAP_MODE_ERTM: case L2CAP_MODE_STREAMING: -- cgit v1.2.3 From c16816acd08697b02a53f56f8936497a9f6f6e7a Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 6 Mar 2020 14:03:43 +0100 Subject: genirq: Add protection against unsafe usage of generic_handle_irq() In general calling generic_handle_irq() with interrupts disabled from non interrupt context is harmless. For some interrupt controllers like the x86 trainwrecks this is outright dangerous as it might corrupt state if an interrupt affinity change is pending. Add infrastructure which allows to mark interrupts as unsafe and catch such usage in generic_handle_irq(). Reported-by: sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Thomas Gleixner Acked-by: Marc Zyngier Link: https://lkml.kernel.org/r/20200306130623.590923677@linutronix.de --- include/linux/irq.h | 13 +++++++++++++ kernel/irq/internals.h | 8 ++++++++ kernel/irq/irqdesc.c | 6 ++++++ kernel/irq/resend.c | 5 +++-- 4 files changed, 30 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/irq.h b/include/linux/irq.h index 3ed5a055b5f4..9315fbb87db3 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -211,6 +211,8 @@ struct irq_data { * IRQD_CAN_RESERVE - Can use reservation mode * IRQD_MSI_NOMASK_QUIRK - Non-maskable MSI quirk for affinity change * required + * IRQD_HANDLE_ENFORCE_IRQCTX - Enforce that handle_irq_*() is only invoked + * from actual interrupt context. */ enum { IRQD_TRIGGER_MASK = 0xf, @@ -234,6 +236,7 @@ enum { IRQD_DEFAULT_TRIGGER_SET = (1 << 25), IRQD_CAN_RESERVE = (1 << 26), IRQD_MSI_NOMASK_QUIRK = (1 << 27), + IRQD_HANDLE_ENFORCE_IRQCTX = (1 << 28), }; #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors) @@ -303,6 +306,16 @@ static inline bool irqd_is_single_target(struct irq_data *d) return __irqd_to_state(d) & IRQD_SINGLE_TARGET; } +static inline void irqd_set_handle_enforce_irqctx(struct irq_data *d) +{ + __irqd_to_state(d) |= IRQD_HANDLE_ENFORCE_IRQCTX; +} + +static inline bool irqd_is_handle_enforce_irqctx(struct irq_data *d) +{ + return __irqd_to_state(d) & IRQD_HANDLE_ENFORCE_IRQCTX; +} + static inline bool irqd_is_wakeup_set(struct irq_data *d) { return __irqd_to_state(d) & IRQD_WAKEUP_STATE; diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index c9d8eb7f5c02..5be382fc9415 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -425,6 +425,10 @@ static inline struct cpumask *irq_desc_get_pending_mask(struct irq_desc *desc) { return desc->pending_mask; } +static inline bool handle_enforce_irqctx(struct irq_data *data) +{ + return irqd_is_handle_enforce_irqctx(data); +} bool irq_fixup_move_pending(struct irq_desc *desc, bool force_clear); #else /* CONFIG_GENERIC_PENDING_IRQ */ static inline bool irq_can_move_pcntxt(struct irq_data *data) @@ -451,6 +455,10 @@ static inline bool irq_fixup_move_pending(struct irq_desc *desc, bool fclear) { return false; } +static inline bool handle_enforce_irqctx(struct irq_data *data) +{ + return false; +} #endif /* !CONFIG_GENERIC_PENDING_IRQ */ #if !defined(CONFIG_IRQ_DOMAIN) || !defined(CONFIG_IRQ_DOMAIN_HIERARCHY) diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 98a5f10d1900..1a7723604399 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -638,9 +638,15 @@ void irq_init_desc(unsigned int irq) int generic_handle_irq(unsigned int irq) { struct irq_desc *desc = irq_to_desc(irq); + struct irq_data *data; if (!desc) return -EINVAL; + + data = irq_desc_get_irq_data(desc); + if (WARN_ON_ONCE(!in_irq() && handle_enforce_irqctx(data))) + return -EPERM; + generic_handle_irq_desc(desc); return 0; } diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index 98c04ca5fa43..5064b13b80d6 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -72,8 +72,9 @@ void check_irq_resend(struct irq_desc *desc) desc->istate &= ~IRQS_PENDING; desc->istate |= IRQS_REPLAY; - if (!desc->irq_data.chip->irq_retrigger || - !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) { + if ((!desc->irq_data.chip->irq_retrigger || + !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) && + !handle_enforce_irqctx(&desc->irq_data)) { #ifdef CONFIG_HARDIRQS_SW_RESEND unsigned int irq = irq_desc_get_irq(desc); -- cgit v1.2.3 From acd26bcf362708594ea081ef55140e37d0854ed2 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Fri, 6 Mar 2020 14:03:47 +0100 Subject: genirq: Provide interrupt injection mechanism Error injection mechanisms need a half ways safe way to inject interrupts as invoking generic_handle_irq() or the actual device interrupt handler directly from e.g. a debugfs write is not guaranteed to be safe. On x86 generic_handle_irq() is unsafe due to the hardware trainwreck which is the base of x86 interrupt delivery and affinity management. Move the irq debugfs injection code into a separate function which can be used by error injection code as well. The implementation prevents at least that state is corrupted, but it cannot close a very tiny race window on x86 which might result in a stale and not serviced device interrupt under very unlikely circumstances. This is explicitly for debugging and testing and not for production use or abuse in random driver code. Signed-off-by: Thomas Gleixner Tested-by: Kuppuswamy Sathyanarayanan Reviewed-by: Kuppuswamy Sathyanarayanan Acked-by: Marc Zyngier Link: https://lkml.kernel.org/r/20200306130623.990928309@linutronix.de --- include/linux/interrupt.h | 2 ++ kernel/irq/Kconfig | 5 +++++ kernel/irq/chip.c | 2 +- kernel/irq/debugfs.c | 34 +----------------------------- kernel/irq/internals.h | 2 +- kernel/irq/resend.c | 53 +++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 61 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index c5fe60ec6b84..80f637c3a6f3 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -248,6 +248,8 @@ extern void enable_percpu_nmi(unsigned int irq, unsigned int type); extern int prepare_percpu_nmi(unsigned int irq); extern void teardown_percpu_nmi(unsigned int irq); +extern int irq_inject_interrupt(unsigned int irq); + /* The following three functions are for the core kernel use only. */ extern void suspend_device_irqs(void); extern void resume_device_irqs(void); diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig index f92d9a687372..20d501af4f2e 100644 --- a/kernel/irq/Kconfig +++ b/kernel/irq/Kconfig @@ -43,6 +43,10 @@ config GENERIC_IRQ_MIGRATION config AUTO_IRQ_AFFINITY bool +# Interrupt injection mechanism +config GENERIC_IRQ_INJECTION + bool + # Tasklet based software resend for pending interrupts on enable_irq() config HARDIRQS_SW_RESEND bool @@ -127,6 +131,7 @@ config SPARSE_IRQ config GENERIC_IRQ_DEBUGFS bool "Expose irq internals in debugfs" depends on DEBUG_FS + select GENERIC_IRQ_INJECTION default n ---help--- diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index b3fa2d87d2f3..41e7e37a0928 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -278,7 +278,7 @@ int irq_startup(struct irq_desc *desc, bool resend, bool force) } } if (resend) - check_irq_resend(desc); + check_irq_resend(desc, false); return ret; } diff --git a/kernel/irq/debugfs.c b/kernel/irq/debugfs.c index 0c607798f519..4f9f844074db 100644 --- a/kernel/irq/debugfs.c +++ b/kernel/irq/debugfs.c @@ -190,39 +190,7 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf, return -EFAULT; if (!strncmp(buf, "trigger", size)) { - unsigned long flags; - int err; - - /* Try the HW interface first */ - err = irq_set_irqchip_state(irq_desc_get_irq(desc), - IRQCHIP_STATE_PENDING, true); - if (!err) - return count; - - /* - * Otherwise, try to inject via the resend interface, - * which may or may not succeed. - */ - chip_bus_lock(desc); - raw_spin_lock_irqsave(&desc->lock, flags); - - /* - * Don't allow injection when the interrupt is: - * - Level or NMI type - * - not activated - * - replaying already - */ - if (irq_settings_is_level(desc) || - !irqd_is_activated(&desc->irq_data) || - (desc->istate & (IRQS_NMI | IRQS_REPLAY))) { - err = -EINVAL; - } else { - desc->istate |= IRQS_PENDING; - err = check_irq_resend(desc); - } - - raw_spin_unlock_irqrestore(&desc->lock, flags); - chip_bus_sync_unlock(desc); + int err = irq_inject_interrupt(irq_desc_get_irq(desc)); return err ? err : count; } diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 8980859bdf1e..7db284b10ac9 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -108,7 +108,7 @@ irqreturn_t handle_irq_event_percpu(struct irq_desc *desc); irqreturn_t handle_irq_event(struct irq_desc *desc); /* Resending of interrupts :*/ -int check_irq_resend(struct irq_desc *desc); +int check_irq_resend(struct irq_desc *desc, bool inject); bool irq_wait_for_poll(struct irq_desc *desc); void __irq_wake_thread(struct irq_desc *desc, struct irqaction *action); diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index bef72dcfb79b..27634f4022d0 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -91,7 +91,7 @@ static int irq_sw_resend(struct irq_desc *desc) * * Is called with interrupts disabled and desc->lock held. */ -int check_irq_resend(struct irq_desc *desc) +int check_irq_resend(struct irq_desc *desc, bool inject) { int err = 0; @@ -108,7 +108,7 @@ int check_irq_resend(struct irq_desc *desc) if (desc->istate & IRQS_REPLAY) return -EBUSY; - if (!(desc->istate & IRQS_PENDING)) + if (!(desc->istate & IRQS_PENDING) && !inject) return 0; desc->istate &= ~IRQS_PENDING; @@ -122,3 +122,52 @@ int check_irq_resend(struct irq_desc *desc) desc->istate |= IRQS_REPLAY; return err; } + +#ifdef CONFIG_GENERIC_IRQ_INJECTION +/** + * irq_inject_interrupt - Inject an interrupt for testing/error injection + * @irq: The interrupt number + * + * This function must only be used for debug and testing purposes! + * + * Especially on x86 this can cause a premature completion of an interrupt + * affinity change causing the interrupt line to become stale. Very + * unlikely, but possible. + * + * The injection can fail for various reasons: + * - Interrupt is not activated + * - Interrupt is NMI type or currently replaying + * - Interrupt is level type + * - Interrupt does not support hardware retrigger and software resend is + * either not enabled or not possible for the interrupt. + */ +int irq_inject_interrupt(unsigned int irq) +{ + struct irq_desc *desc; + unsigned long flags; + int err; + + /* Try the state injection hardware interface first */ + if (!irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true)) + return 0; + + /* That failed, try via the resend mechanism */ + desc = irq_get_desc_buslock(irq, &flags, 0); + if (!desc) + return -EINVAL; + + /* + * Only try to inject when the interrupt is: + * - not NMI type + * - activated + */ + if ((desc->istate & IRQS_NMI) || !irqd_is_activated(&desc->irq_data)) + err = -EINVAL; + else + err = check_irq_resend(desc, true); + + irq_put_desc_busunlock(desc, flags); + return err; +} +EXPORT_SYMBOL_GPL(irq_inject_interrupt); +#endif -- cgit v1.2.3 From 3f17ada8f38c57b6c81d41db6d911932b280d5b5 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:26:01 +0200 Subject: iio: imu: adis: add unlocked __adis_initial_startup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change splits the __adis_initial_startup() away from adis_initial_startup(). The unlocked version can be used in certain calls during probe, where races won't happen since the ADIS driver may not be registered yet with IIO. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 14 ++++---------- include/linux/iio/imu/adis.h | 13 ++++++++++++- 2 files changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 022bb54fb748..e4897dad34ab 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -365,7 +365,7 @@ static int adis_self_test(struct adis *adis) } /** - * adis_inital_startup() - Performs device self-test + * __adis_initial_startup() - Device initial setup * @adis: The adis device * * Returns 0 if the device is operational, a negative error code otherwise. @@ -373,28 +373,22 @@ static int adis_self_test(struct adis *adis) * This function should be called early on in the device initialization sequence * to ensure that the device is in a sane and known state and that it is usable. */ -int adis_initial_startup(struct adis *adis) +int __adis_initial_startup(struct adis *adis) { int ret; - mutex_lock(&adis->state_lock); - ret = adis_self_test(adis); if (ret) { dev_err(&adis->spi->dev, "Self-test failed, trying reset.\n"); __adis_reset(adis); ret = adis_self_test(adis); - if (ret) { + if (ret) dev_err(&adis->spi->dev, "Second self-test failed, giving up.\n"); - goto out_unlock; - } } -out_unlock: - mutex_unlock(&adis->state_lock); return ret; } -EXPORT_SYMBOL_GPL(adis_initial_startup); +EXPORT_SYMBOL_GPL(__adis_initial_startup); /** * adis_single_conversion() - Performs a single sample conversion diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index d2fcf45b4cef..15e75670f923 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -297,6 +297,7 @@ static inline int adis_read_reg_32(struct adis *adis, unsigned int reg, int adis_enable_irq(struct adis *adis, bool enable); int __adis_check_status(struct adis *adis); +int __adis_initial_startup(struct adis *adis); static inline int adis_check_status(struct adis *adis) { @@ -309,7 +310,17 @@ static inline int adis_check_status(struct adis *adis) return ret; } -int adis_initial_startup(struct adis *adis); +/* locked version of __adis_initial_startup() */ +static inline int adis_initial_startup(struct adis *adis) +{ + int ret; + + mutex_lock(&adis->state_lock); + ret = __adis_initial_startup(adis); + mutex_unlock(&adis->state_lock); + + return ret; +} int adis_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int error_mask, -- cgit v1.2.3 From fdcf6bbb4ed388844e74810e117ac87db1347f3a Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Mon, 10 Feb 2020 15:26:02 +0200 Subject: iio: imu: adis: Add self_test_reg variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a dedicated self_test_reg variable. This is also a step to let new drivers make use of `adis_initial_startup()`. Some devices use MSG_CTRL reg to request a self_test command while others use the GLOB_CMD register. Signed-off-by: Nuno Sá Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/accel/adis16201.c | 1 + drivers/iio/accel/adis16209.c | 1 + drivers/iio/gyro/adis16136.c | 1 + drivers/iio/gyro/adis16260.c | 1 + drivers/iio/imu/adis.c | 6 +++--- drivers/iio/imu/adis16400.c | 1 + drivers/iio/imu/adis16460.c | 2 ++ drivers/iio/imu/adis16480.c | 1 + drivers/staging/iio/accel/adis16203.c | 1 + drivers/staging/iio/accel/adis16240.c | 1 + include/linux/iio/imu/adis.h | 2 ++ 11 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 0f0f27a8184e..4154e7396bbe 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -246,6 +246,7 @@ static const struct adis_data adis16201_data = { .diag_stat_reg = ADIS16201_DIAG_STAT_REG, .self_test_mask = ADIS16201_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16201_MSC_CTRL_REG, .self_test_no_autoclear = true, .timeouts = &adis16201_timeouts, diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index c6dbd2424e10..31d45e7c5485 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -256,6 +256,7 @@ static const struct adis_data adis16209_data = { .diag_stat_reg = ADIS16209_STAT_REG, .self_test_mask = ADIS16209_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16209_MSC_CTRL_REG, .self_test_no_autoclear = true, .timeouts = &adis16209_timeouts, diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 1db1131e5c67..a4c967a5fc5c 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -471,6 +471,7 @@ static const char * const adis16136_status_error_msgs[] = { .diag_stat_reg = ADIS16136_REG_DIAG_STAT, \ .glob_cmd_reg = ADIS16136_REG_GLOB_CMD, \ .msc_ctrl_reg = ADIS16136_REG_MSC_CTRL, \ + .self_test_reg = ADIS16136_REG_MSC_CTRL, \ .self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST, \ .read_delay = 10, \ .write_delay = 10, \ diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index be09b3e5910c..9823573e811a 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -346,6 +346,7 @@ static const struct adis_data adis16260_data = { .diag_stat_reg = ADIS16260_DIAG_STAT, .self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST, + .self_test_reg = ADIS16260_MSC_CTRL, .timeouts = &adis16260_timeouts, .status_error_msgs = adis1620_status_error_msgs, diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index e4897dad34ab..f7845a90f376 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -346,8 +346,8 @@ static int adis_self_test(struct adis *adis) int ret; const struct adis_timeout *timeouts = adis->data->timeouts; - ret = __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, - adis->data->self_test_mask); + ret = __adis_write_reg_16(adis, adis->data->self_test_reg, + adis->data->self_test_mask); if (ret) { dev_err(&adis->spi->dev, "Failed to initiate self test: %d\n", ret); @@ -359,7 +359,7 @@ static int adis_self_test(struct adis *adis) ret = __adis_check_status(adis); if (adis->data->self_test_no_autoclear) - __adis_write_reg_16(adis, adis->data->msc_ctrl_reg, 0x00); + __adis_write_reg_16(adis, adis->data->self_test_reg, 0x00); return ret; } diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 1c0770e03ec9..05e70c1c4835 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -956,6 +956,7 @@ static const char * const adis16400_status_error_msgs[] = { .read_delay = 50, \ .write_delay = 50, \ .self_test_mask = ADIS16400_MSC_CTRL_MEM_TEST, \ + .self_test_reg = ADIS16400_MSC_CTRL, \ .status_error_msgs = adis16400_status_error_msgs, \ .status_error_mask = BIT(ADIS16400_DIAG_STAT_ZACCL_FAIL) | \ BIT(ADIS16400_DIAG_STAT_YACCL_FAIL) | \ diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 9539cfe4a259..42fa473c6d81 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -392,6 +392,8 @@ static const struct adis_timeout adis16460_timeouts = { static const struct adis_data adis16460_data = { .diag_stat_reg = ADIS16460_REG_DIAG_STAT, .glob_cmd_reg = ADIS16460_REG_GLOB_CMD, + .self_test_mask = BIT(2), + .self_test_reg = ADIS16460_REG_GLOB_CMD, .has_paging = false, .read_delay = 5, .write_delay = 5, diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 4c4de1b62769..acbe1701fc2d 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -830,6 +830,7 @@ static int adis16480_enable_irq(struct adis *adis, bool enable); .read_delay = 5, \ .write_delay = 5, \ .self_test_mask = BIT(1), \ + .self_test_reg = ADIS16480_REG_GLOB_CMD, \ .status_error_msgs = adis16480_status_error_msgs, \ .status_error_mask = BIT(ADIS16480_DIAG_STAT_XGYRO_FAIL) | \ BIT(ADIS16480_DIAG_STAT_YGYRO_FAIL) | \ diff --git a/drivers/staging/iio/accel/adis16203.c b/drivers/staging/iio/accel/adis16203.c index 39dfe3f7f254..fef52d9b5346 100644 --- a/drivers/staging/iio/accel/adis16203.c +++ b/drivers/staging/iio/accel/adis16203.c @@ -250,6 +250,7 @@ static const struct adis_data adis16203_data = { .diag_stat_reg = ADIS16203_DIAG_STAT, .self_test_mask = ADIS16203_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16203_MSC_CTRL, .self_test_no_autoclear = true, .timeouts = &adis16203_timeouts, diff --git a/drivers/staging/iio/accel/adis16240.c b/drivers/staging/iio/accel/adis16240.c index 39eb8364aa95..8bd35c6c56a1 100644 --- a/drivers/staging/iio/accel/adis16240.c +++ b/drivers/staging/iio/accel/adis16240.c @@ -373,6 +373,7 @@ static const struct adis_data adis16240_data = { .diag_stat_reg = ADIS16240_DIAG_STAT, .self_test_mask = ADIS16240_MSC_CTRL_SELF_TEST_EN, + .self_test_reg = ADIS16240_MSC_CTRL, .self_test_no_autoclear = true, .timeouts = &adis16240_timeouts, diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 15e75670f923..b7feca4e5f26 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -41,6 +41,7 @@ struct adis_timeout { * @glob_cmd_reg: Register address of the GLOB_CMD register * @msc_ctrl_reg: Register address of the MSC_CTRL register * @diag_stat_reg: Register address of the DIAG_STAT register + * @self_test_reg: Register address to request self test command * @status_error_msgs: Array of error messgaes * @status_error_mask: * @timeouts: Chip specific delays @@ -55,6 +56,7 @@ struct adis_data { unsigned int diag_stat_reg; unsigned int self_test_mask; + unsigned int self_test_reg; bool self_test_no_autoclear; const struct adis_timeout *timeouts; -- cgit v1.2.3 From 1fd4567026926d01a94a74d0543e324fc794aa73 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 10 Feb 2020 15:26:04 +0200 Subject: iio: imu: adis: add support product ID check in adis_initial_startup Each driver/chip that wants to validate it's product id, can now specify a 'prod_id_reg' and an expected 'prod_id' value. The 'prod_id' value is intentionally left 0 (uninitialized). There aren't (yet) any product IDs with value 0; this enforces that both 'prod_id_reg' and 'prod_id' are specified. At the very least, this routine validates that the SPI connection to the ADIS chip[s] works well. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/iio/imu/adis.c | 23 ++++++++++++++++++++++- include/linux/iio/imu/adis.h | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 0bd6e32cf577..a8afd01de4f3 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -373,6 +373,10 @@ static int adis_self_test(struct adis *adis) * via GPIOLIB. If no pin is configured a SW reset will be performed. * The RST pin for the ADIS devices should be configured as ACTIVE_LOW. * + * After the self-test operation is performed, the function will also check + * that the product ID is as expected. This assumes that drivers providing + * 'prod_id_reg' will also provide the 'prod_id'. + * * Returns 0 if the device is operational, a negative error code otherwise. * * This function should be called early on in the device initialization sequence @@ -382,6 +386,7 @@ int __adis_initial_startup(struct adis *adis) { const struct adis_timeout *timeouts = adis->data->timeouts; struct gpio_desc *gpio; + uint16_t prod_id; int ret; /* check if the device has rst pin low */ @@ -401,7 +406,23 @@ int __adis_initial_startup(struct adis *adis) return ret; } - return adis_self_test(adis); + ret = adis_self_test(adis); + if (ret) + return ret; + + if (!adis->data->prod_id_reg) + return 0; + + ret = adis_read_reg_16(adis, adis->data->prod_id_reg, &prod_id); + if (ret) + return ret; + + if (prod_id != adis->data->prod_id) + dev_warn(&adis->spi->dev, + "Device ID(%u) and product ID(%u) do not match.", + adis->data->prod_id, prod_id); + + return 0; } EXPORT_SYMBOL_GPL(__adis_initial_startup); diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index b7feca4e5f26..ac7cfd073804 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -41,6 +41,8 @@ struct adis_timeout { * @glob_cmd_reg: Register address of the GLOB_CMD register * @msc_ctrl_reg: Register address of the MSC_CTRL register * @diag_stat_reg: Register address of the DIAG_STAT register + * @prod_id_reg: Register address of the PROD_ID register + * @prod_id: Product ID code that should be expected when reading @prod_id_reg * @self_test_reg: Register address to request self test command * @status_error_msgs: Array of error messgaes * @status_error_mask: @@ -54,6 +56,9 @@ struct adis_data { unsigned int glob_cmd_reg; unsigned int msc_ctrl_reg; unsigned int diag_stat_reg; + unsigned int prod_id_reg; + + unsigned int prod_id; unsigned int self_test_mask; unsigned int self_test_reg; -- cgit v1.2.3 From 3543b1998dd396c66c7a9bce1a7962c10648de6e Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Feb 2020 12:33:17 +0200 Subject: iio: imu: adis: add doc-string for 'adis' struct This change adds a doc-string for the 'adis' struct. It details the fields and their roles. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index ac7cfd073804..63e456aa5c8a 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -73,6 +73,20 @@ struct adis_data { bool has_paging; }; +/** + * struct adis - ADIS device instance data + * @spi: Reference to SPI device which owns this ADIS IIO device + * @trig: IIO trigger object data + * @data: ADIS chip variant specific data + * @burst: ADIS burst transfer information + * @state_lock: Lock used by the device to protect state + * @msg: SPI message object + * @xfer: SPI transfer objects to be used for a @msg + * @current_page: Some ADIS devices have registers, this selects current page + * @buffer: Data buffer for information read from the device + * @tx: DMA safe TX buffer for SPI transfers + * @rx: DMA safe RX buffer for SPI transfers + */ struct adis { struct spi_device *spi; struct iio_trigger *trig; -- cgit v1.2.3 From 2dd86ba821330840661e7102e3304309299aa635 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Feb 2020 12:33:18 +0200 Subject: iio: imu: adis: update 'adis_data' struct doc-string The doc-string has been neglected over time. This change updates it with all the missing info. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 63e456aa5c8a..20339025c75f 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -43,10 +43,14 @@ struct adis_timeout { * @diag_stat_reg: Register address of the DIAG_STAT register * @prod_id_reg: Register address of the PROD_ID register * @prod_id: Product ID code that should be expected when reading @prod_id_reg + * @self_test_mask: Bitmask of supported self-test operations * @self_test_reg: Register address to request self test command + * @self_test_no_autoclear: True if device's self-test needs clear of ctrl reg * @status_error_msgs: Array of error messgaes - * @status_error_mask: + * @status_error_mask: Bitmask of errors supported by the device * @timeouts: Chip specific delays + * @enable_irq: Hook for ADIS devices that have a special IRQ enable/disable + * @has_paging: True if ADIS device has paged registers */ struct adis_data { unsigned int read_delay; -- cgit v1.2.3 From 2303248b291f537f0414a644a53483c71023136f Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Tue, 25 Feb 2020 12:33:19 +0200 Subject: iio: imu: adis: add a note better explaining state_lock The 'state_lock' mutex was renamed from 'txrx_lock' in a previous patch and is intended to be used by ADIS drivers to protect the state of devices during consecutive R/W ops. The initial patch that introduced this change did not do a good [well, any] job at explaining this. This patch adds a comment to the 'state_lock' better explaining it's use. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'include') diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index 20339025c75f..dd8219138c2e 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -98,6 +98,17 @@ struct adis { const struct adis_data *data; struct adis_burst *burst; + /** + * The state_lock is meant to be used during operations that require + * a sequence of SPI R/W in order to protect the SPI transfer + * information (fields 'xfer', 'msg' & 'current_page') between + * potential concurrent accesses. + * This lock is used by all "adis_{functions}" that have to read/write + * registers. These functions also have unlocked variants + * (see "__adis_{functions}"), which don't hold this lock. + * This allows users of the ADIS library to group SPI R/W into + * the drivers, but they also must manage this lock themselves. + */ struct mutex state_lock; struct spi_message msg; struct spi_transfer *xfer; -- cgit v1.2.3 From 2ddc982a6c172c141183ac954d44f01bc4f2fe72 Mon Sep 17 00:00:00 2001 From: Alexandru Tachici Date: Fri, 21 Feb 2020 14:06:55 +0200 Subject: iio: industrialio-core: Fix debugfs read Currently iio_debugfs_read_reg calls debugfs_reg_access every time it is ran. Reading the same hardware register multiple times during the same reading of a debugfs file can cause unintended effects. For example for each: cat iio:device0/direct_reg_access the file_operations.read function will be called at least twice. First will return the full length of the string in bytes and the second will return 0. This patch makes iio_debugfs_read_reg to call debugfs_reg_access only when the user's buffer position (*ppos) is 0. (meaning it is the beginning of a new reading of the debugfs file). Fixes: e553f182d55b ("staging: iio: core: Introduce debugfs support, add support for direct register access") Signed-off-by: Alexandru Ardelean Signed-off-by: Alexandru Tachici Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 15 +++++++++++---- include/linux/iio/iio.h | 2 ++ 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0b14666dff09..eac63c1bb8da 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -301,11 +301,14 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct iio_dev *indio_dev = file->private_data; - char buf[20]; unsigned val = 0; - ssize_t len; int ret; + if (*ppos > 0) + return simple_read_from_buffer(userbuf, count, ppos, + indio_dev->read_buf, + indio_dev->read_buf_len); + ret = indio_dev->info->debugfs_reg_access(indio_dev, indio_dev->cached_reg_addr, 0, &val); @@ -314,9 +317,13 @@ static ssize_t iio_debugfs_read_reg(struct file *file, char __user *userbuf, return ret; } - len = snprintf(buf, sizeof(buf), "0x%X\n", val); + indio_dev->read_buf_len = snprintf(indio_dev->read_buf, + sizeof(indio_dev->read_buf), + "0x%X\n", val); - return simple_read_from_buffer(userbuf, count, ppos, buf, len); + return simple_read_from_buffer(userbuf, count, ppos, + indio_dev->read_buf, + indio_dev->read_buf_len); } static ssize_t iio_debugfs_write_reg(struct file *file, diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 862ce0019eba..eed58ed2f368 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -568,6 +568,8 @@ struct iio_dev { #if defined(CONFIG_DEBUG_FS) struct dentry *debugfs_dentry; unsigned cached_reg_addr; + char read_buf[20]; + unsigned int read_buf_len; #endif }; -- cgit v1.2.3 From 2514921e72472806b059316122f232c0bbdaf1a1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Mar 2020 12:40:11 +0100 Subject: flow_offload: Introduce offload of HW stats type Initially, pass "ANY" (struct is zeroed) to the drivers as that is the current implicit value coming down to flow_offload. Add a bool indicating that entries have mixed HW stats type. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_offload.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index cd3510ac66b0..93d17f37e980 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -154,6 +154,8 @@ enum flow_action_mangle_base { FLOW_ACT_MANGLE_HDR_TYPE_UDP, }; +#define FLOW_ACTION_HW_STATS_TYPE_ANY 0 + typedef void (*action_destr)(void *priv); struct flow_action_cookie { @@ -168,6 +170,7 @@ void flow_action_cookie_destroy(struct flow_action_cookie *cookie); struct flow_action_entry { enum flow_action_id id; + u8 hw_stats_type; action_destr destructor; void *destructor_priv; union { -- cgit v1.2.3 From 319a1d19471ec49b8a91a7f6a3fe2c4535e5c279 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Mar 2020 12:40:13 +0100 Subject: flow_offload: check for basic action hw stats type Introduce flow_action_basic_hw_stats_types_check() helper and use it in drivers. That sanitizes the drivers which do not have support for action HW stats types. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c | 9 +++- .../net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c | 8 ++- .../net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h | 3 +- .../net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c | 3 +- drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c | 6 +++ drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 9 ++++ drivers/net/ethernet/mscc/ocelot_flower.c | 4 ++ drivers/net/ethernet/netronome/nfp/flower/action.c | 4 ++ drivers/net/ethernet/qlogic/qede/qede_filter.c | 10 ++-- drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c | 9 +++- include/net/flow_offload.h | 61 ++++++++++++++++++++++ net/dsa/slave.c | 4 ++ 12 files changed, 119 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c index 9bec256b0934..523bf4be43cc 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -279,7 +279,8 @@ bnxt_tc_parse_pedit(struct bnxt *bp, struct bnxt_tc_actions *actions, static int bnxt_tc_parse_actions(struct bnxt *bp, struct bnxt_tc_actions *actions, - struct flow_action *flow_action) + struct flow_action *flow_action, + struct netlink_ext_ack *extack) { /* Used to store the L2 rewrite mask for dmac (6 bytes) followed by * smac (6 bytes) if rewrite of both is specified, otherwise either @@ -299,6 +300,9 @@ static int bnxt_tc_parse_actions(struct bnxt *bp, return -EINVAL; } + if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, act, flow_action) { switch (act->id) { case FLOW_ACTION_DROP: @@ -491,7 +495,8 @@ static int bnxt_tc_parse_flow(struct bnxt *bp, flow->tun_mask.tp_src = match.mask->src; } - return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action); + return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action, + tc_flow_cmd->common.extack); } static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c index bb5513bdd293..cc46277e98de 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.c @@ -544,7 +544,8 @@ static bool valid_pedit_action(struct net_device *dev, } int cxgb4_validate_flow_actions(struct net_device *dev, - struct flow_action *actions) + struct flow_action *actions, + struct netlink_ext_ack *extack) { struct flow_action_entry *act; bool act_redir = false; @@ -552,6 +553,9 @@ int cxgb4_validate_flow_actions(struct net_device *dev, bool act_vlan = false; int i; + if (!flow_action_basic_hw_stats_types_check(actions, extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, act, actions) { switch (act->id) { case FLOW_ACTION_ACCEPT: @@ -642,7 +646,7 @@ int cxgb4_tc_flower_replace(struct net_device *dev, struct filter_ctx ctx; int fidx, ret; - if (cxgb4_validate_flow_actions(dev, &rule->action)) + if (cxgb4_validate_flow_actions(dev, &rule->action, extack)) return -EOPNOTSUPP; if (cxgb4_validate_flow_match(dev, cls)) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h index e132516e9868..0a30c96b81ff 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_flower.h @@ -112,7 +112,8 @@ void cxgb4_process_flow_actions(struct net_device *in, struct flow_action *actions, struct ch_filter_specification *fs); int cxgb4_validate_flow_actions(struct net_device *dev, - struct flow_action *actions); + struct flow_action *actions, + struct netlink_ext_ack *extack); int cxgb4_tc_flower_replace(struct net_device *dev, struct flow_cls_offload *cls); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c index 1b7681a4eb32..d80dee4d316d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c @@ -286,7 +286,8 @@ int cxgb4_tc_matchall_replace(struct net_device *dev, } ret = cxgb4_validate_flow_actions(dev, - &cls_matchall->rule->action); + &cls_matchall->rule->action, + extack); if (ret) return ret; diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c index 35478cba2aa5..0a0c6ec2336c 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_cls.c @@ -1082,6 +1082,9 @@ static int mvpp2_port_c2_tcam_rule_add(struct mvpp2_port *port, u8 qh, ql, pmap; int index, ctx; + if (!flow_action_basic_hw_stats_types_check(&rule->flow->action, NULL)) + return -EOPNOTSUPP; + memset(&c2, 0, sizeof(c2)); index = mvpp2_cls_c2_port_flow_index(port, rule->loc); @@ -1305,6 +1308,9 @@ static int mvpp2_cls_rfs_parse_rule(struct mvpp2_rfs_rule *rule) struct flow_rule *flow = rule->flow; struct flow_action_entry *act; + if (!flow_action_basic_hw_stats_types_check(&rule->flow->action, NULL)) + return -EOPNOTSUPP; + act = &flow->action.entries[0]; if (act->id != FLOW_ACTION_QUEUE && act->id != FLOW_ACTION_DROP) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 4eb2f2392d2d..cfe393cb4026 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2878,6 +2878,9 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, if (!flow_action_has_entries(flow_action)) return -EINVAL; + if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + return -EOPNOTSUPP; + attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; flow_action_for_each(i, act, flow_action) { @@ -3330,6 +3333,9 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, if (!flow_action_has_entries(flow_action)) return -EINVAL; + if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, act, flow_action) { switch (act->id) { case FLOW_ACTION_DROP: @@ -4148,6 +4154,9 @@ static int scan_tc_matchall_fdb_actions(struct mlx5e_priv *priv, return -EOPNOTSUPP; } + if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, act, flow_action) { switch (act->id) { case FLOW_ACTION_POLICE: diff --git a/drivers/net/ethernet/mscc/ocelot_flower.c b/drivers/net/ethernet/mscc/ocelot_flower.c index 8986f209e981..6d84173373c7 100644 --- a/drivers/net/ethernet/mscc/ocelot_flower.c +++ b/drivers/net/ethernet/mscc/ocelot_flower.c @@ -17,6 +17,10 @@ static int ocelot_flower_parse_action(struct flow_cls_offload *f, if (!flow_offload_has_one_action(&f->rule->action)) return -EOPNOTSUPP; + if (!flow_action_basic_hw_stats_types_check(&f->rule->action, + f->common.extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, a, &f->rule->action) { switch (a->id) { case FLOW_ACTION_DROP: diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c index c06600fb47ff..4aa7346cb040 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/action.c +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c @@ -1207,6 +1207,10 @@ int nfp_flower_compile_action(struct nfp_app *app, bool pkt_host = false; u32 csum_updated = 0; + if (!flow_action_basic_hw_stats_types_check(&flow->rule->action, + extack)) + return -EOPNOTSUPP; + memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ); nfp_flow->meta.act_len = 0; tun_type = NFP_FL_TUNNEL_NONE; diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index d1ce4531d01a..6505f7e2d1db 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -1746,7 +1746,8 @@ unlock: } static int qede_parse_actions(struct qede_dev *edev, - struct flow_action *flow_action) + struct flow_action *flow_action, + struct netlink_ext_ack *extack) { const struct flow_action_entry *act; int i; @@ -1756,6 +1757,9 @@ static int qede_parse_actions(struct qede_dev *edev, return -EINVAL; } + if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, act, flow_action) { switch (act->id) { case FLOW_ACTION_DROP: @@ -1970,7 +1974,7 @@ int qede_add_tc_flower_fltr(struct qede_dev *edev, __be16 proto, } /* parse tc actions and get the vf_id */ - if (qede_parse_actions(edev, &f->rule->action)) + if (qede_parse_actions(edev, &f->rule->action, f->common.extack)) goto unlock; if (qede_flow_find_fltr(edev, &t)) { @@ -2038,7 +2042,7 @@ static int qede_flow_spec_validate(struct qede_dev *edev, return -EINVAL; } - if (qede_parse_actions(edev, flow_action)) + if (qede_parse_actions(edev, flow_action, NULL)) return -EINVAL; return 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c index 7a01dee2f9a8..a0e6118444b0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_tc.c @@ -367,7 +367,8 @@ static int tc_setup_cbs(struct stmmac_priv *priv, static int tc_parse_flow_actions(struct stmmac_priv *priv, struct flow_action *action, - struct stmmac_flow_entry *entry) + struct stmmac_flow_entry *entry, + struct netlink_ext_ack *extack) { struct flow_action_entry *act; int i; @@ -375,6 +376,9 @@ static int tc_parse_flow_actions(struct stmmac_priv *priv, if (!flow_action_has_entries(action)) return -EINVAL; + if (!flow_action_basic_hw_stats_types_check(action, extack)) + return -EOPNOTSUPP; + flow_action_for_each(i, act, action) { switch (act->id) { case FLOW_ACTION_DROP: @@ -530,7 +534,8 @@ static int tc_add_flow(struct stmmac_priv *priv, return -ENOENT; } - ret = tc_parse_flow_actions(priv, &rule->action, entry); + ret = tc_parse_flow_actions(priv, &rule->action, entry, + cls->common.extack); if (ret) return ret; diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 93d17f37e980..8b40f612a565 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -251,6 +252,66 @@ static inline bool flow_offload_has_one_action(const struct flow_action *action) return action->num_entries == 1; } +static inline bool +flow_action_mixed_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack) +{ + const struct flow_action_entry *action_entry; + u8 uninitialized_var(last_hw_stats_type); + int i; + + if (flow_offload_has_one_action(action)) + return true; + + for (i = 0; i < action->num_entries; i++) { + action_entry = &action->entries[i]; + if (i && action_entry->hw_stats_type != last_hw_stats_type) { + NL_SET_ERR_MSG_MOD(extack, "Mixing HW stats types for actions is not supported"); + return false; + } + last_hw_stats_type = action_entry->hw_stats_type; + } + return true; +} + +static inline const struct flow_action_entry * +flow_action_first_entry_get(const struct flow_action *action) +{ + WARN_ON(!flow_action_has_entries(action)); + return &action->entries[0]; +} + +static inline bool +flow_action_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack, + u8 allowed_hw_stats_type) +{ + const struct flow_action_entry *action_entry; + + if (!flow_action_has_entries(action)) + return true; + if (!flow_action_mixed_hw_stats_types_check(action, extack)) + return false; + action_entry = flow_action_first_entry_get(action); + if (allowed_hw_stats_type == 0 && + action_entry->hw_stats_type != FLOW_ACTION_HW_STATS_TYPE_ANY) { + NL_SET_ERR_MSG_MOD(extack, "Driver supports only default HW stats type \"any\""); + return false; + } else if (allowed_hw_stats_type != 0 && + action_entry->hw_stats_type != allowed_hw_stats_type) { + NL_SET_ERR_MSG_MOD(extack, "Driver does not support selected HW stats type"); + return false; + } + return true; +} + +static inline bool +flow_action_basic_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack) +{ + return flow_action_hw_stats_types_check(action, extack, 0); +} + #define flow_action_for_each(__i, __act, __actions) \ for (__i = 0, __act = &(__actions)->entries[0]; __i < (__actions)->num_entries; __act = &(__actions)->entries[++__i]) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 79d9b4384d7b..fca9bfa8437e 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -865,6 +865,10 @@ static int dsa_slave_add_cls_matchall(struct net_device *dev, if (!flow_offload_has_one_action(&cls->rule->action)) return err; + if (!flow_action_basic_hw_stats_types_check(&cls->rule->action, + cls->common.extack)) + return err; + act = &cls->rule->action.entries[0]; if (act->id == FLOW_ACTION_MIRRED && protocol == htons(ETH_P_ALL)) { -- cgit v1.2.3 From d60d7ed4c86074cc7bbea8b68f1ca3e811ad46fd Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Mar 2020 12:40:16 +0100 Subject: flow_offload: introduce "immediate" HW stats type and allow it in mlxsw Introduce new type for immediate HW stats and allow the value in mlxsw offload. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 3 ++- include/net/flow_offload.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 588d374531cc..4bf3ac1cb20d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -30,7 +30,8 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, return -EOPNOTSUPP; act = flow_action_first_entry_get(flow_action); - if (act->hw_stats_type == FLOW_ACTION_HW_STATS_TYPE_ANY) { + if (act->hw_stats_type == FLOW_ACTION_HW_STATS_TYPE_ANY || + act->hw_stats_type == FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE) { /* Count action is inserted first */ err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack); if (err) diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 8b40f612a565..6580c58b368f 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -155,7 +155,8 @@ enum flow_action_mangle_base { FLOW_ACT_MANGLE_HDR_TYPE_UDP, }; -#define FLOW_ACTION_HW_STATS_TYPE_ANY 0 +#define FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE BIT(0) +#define FLOW_ACTION_HW_STATS_TYPE_ANY FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE typedef void (*action_destr)(void *priv); -- cgit v1.2.3 From 48855479510be1f8769cb13174f29dd8a44b4256 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Mar 2020 12:40:17 +0100 Subject: flow_offload: introduce "delayed" HW stats type and allow it in mlx5 Introduce new type for delayed HW stats and allow the value in mlx5 offload. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 6 ++++-- include/net/flow_offload.h | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index cfe393cb4026..cdc63dd59867 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2878,7 +2878,8 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, if (!flow_action_has_entries(flow_action)) return -EINVAL; - if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + if (!flow_action_hw_stats_types_check(flow_action, extack, + FLOW_ACTION_HW_STATS_TYPE_DELAYED)) return -EOPNOTSUPP; attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; @@ -3333,7 +3334,8 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, if (!flow_action_has_entries(flow_action)) return -EINVAL; - if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) + if (!flow_action_hw_stats_types_check(flow_action, extack, + FLOW_ACTION_HW_STATS_TYPE_DELAYED)) return -EOPNOTSUPP; flow_action_for_each(i, act, flow_action) { diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 6580c58b368f..1b6500f0fbca 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -156,7 +156,9 @@ enum flow_action_mangle_base { }; #define FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE BIT(0) -#define FLOW_ACTION_HW_STATS_TYPE_ANY FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE +#define FLOW_ACTION_HW_STATS_TYPE_DELAYED BIT(1) +#define FLOW_ACTION_HW_STATS_TYPE_ANY (FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE | \ + FLOW_ACTION_HW_STATS_TYPE_DELAYED) typedef void (*action_destr)(void *priv); -- cgit v1.2.3 From d7cb1e3ba12e004e92e9ad2e8d272220db0a541c Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Mar 2020 12:40:19 +0100 Subject: flow_offload: introduce "disabled" HW stats type and allow it in mlxsw Introduce new type for disabled HW stats and allow the value in mlxsw offload. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- include/net/flow_offload.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 4bf3ac1cb20d..88aa554415df 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -36,7 +36,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack); if (err) return err; - } else { + } else if (act->hw_stats_type != FLOW_ACTION_HW_STATS_TYPE_DISABLED) { NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type"); return -EOPNOTSUPP; } diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 1b6500f0fbca..64807aa03cee 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -159,6 +159,7 @@ enum flow_action_mangle_base { #define FLOW_ACTION_HW_STATS_TYPE_DELAYED BIT(1) #define FLOW_ACTION_HW_STATS_TYPE_ANY (FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE | \ FLOW_ACTION_HW_STATS_TYPE_DELAYED) +#define FLOW_ACTION_HW_STATS_TYPE_DISABLED 0 typedef void (*action_destr)(void *priv); -- cgit v1.2.3 From 44f8658017419dccbeefe64f30122fa191d0e173 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Sat, 7 Mar 2020 12:40:20 +0100 Subject: sched: act: allow user to specify type of HW stats for a filter Currently, user who is adding an action expects HW to report stats, however it does not have exact expectations about the stats types. That is aligned with TCA_ACT_HW_STATS_TYPE_ANY. Allow user to specify the type of HW stats for an action and require it. Pass the information down to flow_offload layer. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/act_api.h | 4 ++++ include/uapi/linux/pkt_cls.h | 22 ++++++++++++++++++++++ net/sched/act_api.c | 36 ++++++++++++++++++++++++++++++++++++ net/sched/cls_api.c | 7 +++++++ 4 files changed, 69 insertions(+) (limited to 'include') diff --git a/include/net/act_api.h b/include/net/act_api.h index 71347a90a9d1..41337c7fc728 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -41,6 +41,7 @@ struct tc_action { struct tc_cookie __rcu *act_cookie; struct tcf_chain __rcu *goto_chain; u32 tcfa_flags; + u8 hw_stats_type; }; #define tcf_index common.tcfa_index #define tcf_refcnt common.tcfa_refcnt @@ -52,6 +53,9 @@ struct tc_action { #define tcf_rate_est common.tcfa_rate_est #define tcf_lock common.tcfa_lock +#define TCA_ACT_HW_STATS_TYPE_ANY (TCA_ACT_HW_STATS_TYPE_IMMEDIATE | \ + TCA_ACT_HW_STATS_TYPE_DELAYED) + /* Update lastuse only if needed, to avoid dirtying a cache line. * We use a temp variable to avoid fetching jiffies twice. */ diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 449a63971451..81cc1a869588 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -17,6 +17,7 @@ enum { TCA_ACT_PAD, TCA_ACT_COOKIE, TCA_ACT_FLAGS, + TCA_ACT_HW_STATS_TYPE, __TCA_ACT_MAX }; @@ -24,6 +25,27 @@ enum { * actions stats. */ +/* tca HW stats type + * When user does not pass the attribute, he does not care. + * It is the same as if he would pass the attribute with + * all supported bits set. + * In case no bits are set, user is not interested in getting any HW statistics. + */ +#define TCA_ACT_HW_STATS_TYPE_IMMEDIATE (1 << 0) /* Means that in dump, user + * gets the current HW stats + * state from the device + * queried at the dump time. + */ +#define TCA_ACT_HW_STATS_TYPE_DELAYED (1 << 1) /* Means that in dump, user gets + * HW stats that might be out + * of date for some time, maybe + * couple of seconds. This is + * the case when driver polls + * stats updates periodically + * or when it gets async stats update + * from the device. + */ + #define TCA_ACT_MAX __TCA_ACT_MAX #define TCA_OLD_COMPAT (TCA_ACT_MAX+1) #define TCA_ACT_MAX_PRIO 32 diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 8c466a712cda..aa7b737fed2e 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -185,6 +185,7 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act) return nla_total_size(0) /* action number nested */ + nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */ + cookie_len /* TCA_ACT_COOKIE */ + + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS_TYPE */ + nla_total_size(0) /* TCA_ACT_STATS nested */ + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */ /* TCA_STATS_BASIC */ @@ -788,6 +789,17 @@ tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref) } rcu_read_unlock(); + if (a->hw_stats_type != TCA_ACT_HW_STATS_TYPE_ANY) { + struct nla_bitfield32 hw_stats_type = { + a->hw_stats_type, + TCA_ACT_HW_STATS_TYPE_ANY, + }; + + if (nla_put(skb, TCA_ACT_HW_STATS_TYPE, sizeof(hw_stats_type), + &hw_stats_type)) + goto nla_put_failure; + } + if (a->tcfa_flags) { struct nla_bitfield32 flags = { a->tcfa_flags, a->tcfa_flags, }; @@ -854,7 +866,23 @@ static struct tc_cookie *nla_memdup_cookie(struct nlattr **tb) return c; } +static u8 tcf_action_hw_stats_type_get(struct nlattr *hw_stats_type_attr) +{ + struct nla_bitfield32 hw_stats_type_bf; + + /* If the user did not pass the attr, that means he does + * not care about the type. Return "any" in that case + * which is setting on all supported types. + */ + if (!hw_stats_type_attr) + return TCA_ACT_HW_STATS_TYPE_ANY; + hw_stats_type_bf = nla_get_bitfield32(hw_stats_type_attr); + return hw_stats_type_bf.value; +} + static const u32 tca_act_flags_allowed = TCA_ACT_FLAGS_NO_PERCPU_STATS; +static const u32 tca_act_hw_stats_type_allowed = TCA_ACT_HW_STATS_TYPE_ANY; + static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = { [TCA_ACT_KIND] = { .type = NLA_STRING }, [TCA_ACT_INDEX] = { .type = NLA_U32 }, @@ -863,6 +891,8 @@ static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = { [TCA_ACT_OPTIONS] = { .type = NLA_NESTED }, [TCA_ACT_FLAGS] = { .type = NLA_BITFIELD32, .validation_data = &tca_act_flags_allowed }, + [TCA_ACT_HW_STATS_TYPE] = { .type = NLA_BITFIELD32, + .validation_data = &tca_act_hw_stats_type_allowed }, }; struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, @@ -871,6 +901,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, bool rtnl_held, struct netlink_ext_ack *extack) { + u8 hw_stats_type = TCA_ACT_HW_STATS_TYPE_ANY; struct nla_bitfield32 flags = { 0, 0 }; struct tc_action *a; struct tc_action_ops *a_o; @@ -903,6 +934,8 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, goto err_out; } } + hw_stats_type = + tcf_action_hw_stats_type_get(tb[TCA_ACT_HW_STATS_TYPE]); if (tb[TCA_ACT_FLAGS]) flags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]); } else { @@ -953,6 +986,9 @@ struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp, if (!name && tb[TCA_ACT_COOKIE]) tcf_set_action_cookie(&a->act_cookie, cookie); + if (!name) + a->hw_stats_type = hw_stats_type; + /* module count goes up only when brand new policy is created * if it exists and is only bound to in a_o->init() then * ACT_P_CREATED is not returned (a zero is). diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 4e766c5ab77a..e91448640a4f 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3464,6 +3464,10 @@ int tc_setup_flow_action(struct flow_action *flow_action, struct tc_action *act; int i, j, k, err = 0; + BUILD_BUG_ON(TCA_ACT_HW_STATS_TYPE_ANY != FLOW_ACTION_HW_STATS_TYPE_ANY); + BUILD_BUG_ON(TCA_ACT_HW_STATS_TYPE_IMMEDIATE != FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE); + BUILD_BUG_ON(TCA_ACT_HW_STATS_TYPE_DELAYED != FLOW_ACTION_HW_STATS_TYPE_DELAYED); + if (!exts) return 0; @@ -3476,6 +3480,9 @@ int tc_setup_flow_action(struct flow_action *flow_action, err = tcf_act_get_cookie(entry, act); if (err) goto err_out_locked; + + entry->hw_stats_type = act->hw_stats_type; + if (is_tcf_gact_ok(act)) { entry->id = FLOW_ACTION_ACCEPT; } else if (is_tcf_gact_shot(act)) { -- cgit v1.2.3 From 83f73c5bb7b9a9135173f0ba2b1aa00c06664ff9 Mon Sep 17 00:00:00 2001 From: Dmitry Yakunin Date: Thu, 5 Mar 2020 15:33:12 +0300 Subject: inet_diag: return classid for all socket types In commit 1ec17dbd90f8 ("inet_diag: fix reporting cgroup classid and fallback to priority") croup classid reporting was fixed. But this works only for TCP sockets because for other socket types icsk parameter can be NULL and classid code path is skipped. This change moves classid handling to inet_diag_msg_attrs_fill() function. Also inet_diag_msg_attrs_size() helper was added and addends in nlmsg_new() were reordered to save order from inet_sk_diag_fill(). Fixes: 1ec17dbd90f8 ("inet_diag: fix reporting cgroup classid and fallback to priority") Signed-off-by: Dmitry Yakunin Reviewed-by: Konstantin Khlebnikov Signed-off-by: David S. Miller --- include/linux/inet_diag.h | 18 ++++++++++++------ net/ipv4/inet_diag.c | 44 ++++++++++++++++++++------------------------ net/ipv4/raw_diag.c | 5 +++-- net/ipv4/udp_diag.c | 5 +++-- net/sctp/diag.c | 8 ++------ 5 files changed, 40 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index 39faaaf843e1..c91cf2dee12a 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -2,15 +2,10 @@ #ifndef _INET_DIAG_H_ #define _INET_DIAG_H_ 1 +#include #include -struct net; -struct sock; struct inet_hashinfo; -struct nlattr; -struct nlmsghdr; -struct sk_buff; -struct netlink_callback; struct inet_diag_handler { void (*dump)(struct sk_buff *skb, @@ -62,6 +57,17 @@ int inet_diag_bc_sk(const struct nlattr *_bc, struct sock *sk); void inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk); +static inline size_t inet_diag_msg_attrs_size(void) +{ + return nla_total_size(1) /* INET_DIAG_SHUTDOWN */ + + nla_total_size(1) /* INET_DIAG_TOS */ +#if IS_ENABLED(CONFIG_IPV6) + + nla_total_size(1) /* INET_DIAG_TCLASS */ + + nla_total_size(1) /* INET_DIAG_SKV6ONLY */ +#endif + + nla_total_size(4) /* INET_DIAG_MARK */ + + nla_total_size(4); /* INET_DIAG_CLASS_ID */ +} int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, struct inet_diag_msg *r, int ext, struct user_namespace *user_ns, bool net_admin); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index f11e997e517b..8c8377568a78 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -100,13 +100,9 @@ static size_t inet_sk_attr_size(struct sock *sk, aux = handler->idiag_get_aux_size(sk, net_admin); return nla_total_size(sizeof(struct tcp_info)) - + nla_total_size(1) /* INET_DIAG_SHUTDOWN */ - + nla_total_size(1) /* INET_DIAG_TOS */ - + nla_total_size(1) /* INET_DIAG_TCLASS */ - + nla_total_size(4) /* INET_DIAG_MARK */ - + nla_total_size(4) /* INET_DIAG_CLASS_ID */ - + nla_total_size(sizeof(struct inet_diag_meminfo)) + nla_total_size(sizeof(struct inet_diag_msg)) + + inet_diag_msg_attrs_size() + + nla_total_size(sizeof(struct inet_diag_meminfo)) + nla_total_size(SK_MEMINFO_VARS * sizeof(u32)) + nla_total_size(TCP_CA_NAME_MAX) + nla_total_size(sizeof(struct tcpvegas_info)) @@ -147,6 +143,24 @@ int inet_diag_msg_attrs_fill(struct sock *sk, struct sk_buff *skb, if (net_admin && nla_put_u32(skb, INET_DIAG_MARK, sk->sk_mark)) goto errout; + if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) || + ext & (1 << (INET_DIAG_TCLASS - 1))) { + u32 classid = 0; + +#ifdef CONFIG_SOCK_CGROUP_DATA + classid = sock_cgroup_classid(&sk->sk_cgrp_data); +#endif + /* Fallback to socket priority if class id isn't set. + * Classful qdiscs use it as direct reference to class. + * For cgroup2 classid is always zero. + */ + if (!classid) + classid = sk->sk_priority; + + if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) + goto errout; + } + r->idiag_uid = from_kuid_munged(user_ns, sock_i_uid(sk)); r->idiag_inode = sock_i_ino(sk); @@ -284,24 +298,6 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, goto errout; } - if (ext & (1 << (INET_DIAG_CLASS_ID - 1)) || - ext & (1 << (INET_DIAG_TCLASS - 1))) { - u32 classid = 0; - -#ifdef CONFIG_SOCK_CGROUP_DATA - classid = sock_cgroup_classid(&sk->sk_cgrp_data); -#endif - /* Fallback to socket priority if class id isn't set. - * Classful qdiscs use it as direct reference to class. - * For cgroup2 classid is always zero. - */ - if (!classid) - classid = sk->sk_priority; - - if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) - goto errout; - } - out: nlmsg_end(skb, nlh); return 0; diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index e35736b99300..a93e7d1e1251 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -100,8 +100,9 @@ static int raw_diag_dump_one(struct sk_buff *in_skb, if (IS_ERR(sk)) return PTR_ERR(sk); - rep = nlmsg_new(sizeof(struct inet_diag_msg) + - sizeof(struct inet_diag_meminfo) + 64, + rep = nlmsg_new(nla_total_size(sizeof(struct inet_diag_msg)) + + inet_diag_msg_attrs_size() + + nla_total_size(sizeof(struct inet_diag_meminfo)) + 64, GFP_KERNEL); if (!rep) { sock_put(sk); diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 910555a4d9fe..dccd2286bc28 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -64,8 +64,9 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, goto out; err = -ENOMEM; - rep = nlmsg_new(sizeof(struct inet_diag_msg) + - sizeof(struct inet_diag_meminfo) + 64, + rep = nlmsg_new(nla_total_size(sizeof(struct inet_diag_msg)) + + inet_diag_msg_attrs_size() + + nla_total_size(sizeof(struct inet_diag_meminfo)) + 64, GFP_KERNEL); if (!rep) goto out; diff --git a/net/sctp/diag.c b/net/sctp/diag.c index 8a15146faaeb..1069d7af3672 100644 --- a/net/sctp/diag.c +++ b/net/sctp/diag.c @@ -237,15 +237,11 @@ static size_t inet_assoc_attr_size(struct sctp_association *asoc) addrcnt++; return nla_total_size(sizeof(struct sctp_info)) - + nla_total_size(1) /* INET_DIAG_SHUTDOWN */ - + nla_total_size(1) /* INET_DIAG_TOS */ - + nla_total_size(1) /* INET_DIAG_TCLASS */ - + nla_total_size(4) /* INET_DIAG_MARK */ - + nla_total_size(4) /* INET_DIAG_CLASS_ID */ + nla_total_size(addrlen * asoc->peer.transport_count) + nla_total_size(addrlen * addrcnt) - + nla_total_size(sizeof(struct inet_diag_meminfo)) + nla_total_size(sizeof(struct inet_diag_msg)) + + inet_diag_msg_attrs_size() + + nla_total_size(sizeof(struct inet_diag_meminfo)) + 64; } -- cgit v1.2.3 From d7f5f3c89c1a2344e88842ba0de327cc0098583e Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 5 Mar 2020 22:28:15 -0600 Subject: remoteproc: add IPA notification to q6v5 driver Set up a subdev in the q6v5 modem remoteproc driver that generates event notifications for the IPA driver to use for initialization and recovery following a modem shutdown or crash. A pair of new functions provides a way for the IPA driver to register and deregister a notification callback function that will be called whenever modem events (about to boot, running, about to shut down, etc.) occur. A void pointer value (provided by the IPA driver at registration time) and an event type are supplied to the callback function. One event, MODEM_REMOVING, is signaled whenever the q6v5 driver is about to remove the notification subdevice. It requires the IPA driver de-register its callback. This sub-device is only used by the modem subsystem (MSS) driver, so the code that adds the new subdev and allows registration and deregistration of the notifier is found in "qcom_q6v6_mss.c". Signed-off-by: Alex Elder Signed-off-by: David S. Miller --- drivers/remoteproc/Kconfig | 6 ++ drivers/remoteproc/Makefile | 1 + drivers/remoteproc/qcom_q6v5_ipa_notify.c | 85 +++++++++++++++++++++++++ drivers/remoteproc/qcom_q6v5_mss.c | 38 +++++++++++ include/linux/remoteproc/qcom_q6v5_ipa_notify.h | 82 ++++++++++++++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 drivers/remoteproc/qcom_q6v5_ipa_notify.c create mode 100644 include/linux/remoteproc/qcom_q6v5_ipa_notify.h (limited to 'include') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index de3862c15fcc..56084635dd63 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -167,6 +167,12 @@ config QCOM_Q6V5_WCSS Say y here to support the Qualcomm Peripheral Image Loader for the Hexagon V5 based WCSS remote processors. +config QCOM_Q6V5_IPA_NOTIFY + tristate + depends on QCOM_IPA + depends on QCOM_Q6V5_MSS + default QCOM_IPA + config QCOM_SYSMON tristate "Qualcomm sysmon driver" depends on RPMSG diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index e30a1b15fbac..0effd3825035 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_QCOM_Q6V5_ADSP) += qcom_q6v5_adsp.o obj-$(CONFIG_QCOM_Q6V5_MSS) += qcom_q6v5_mss.o obj-$(CONFIG_QCOM_Q6V5_PAS) += qcom_q6v5_pas.o obj-$(CONFIG_QCOM_Q6V5_WCSS) += qcom_q6v5_wcss.o +obj-$(CONFIG_QCOM_Q6V5_IPA_NOTIFY) += qcom_q6v5_ipa_notify.o obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o qcom_wcnss_pil-y += qcom_wcnss.o diff --git a/drivers/remoteproc/qcom_q6v5_ipa_notify.c b/drivers/remoteproc/qcom_q6v5_ipa_notify.c new file mode 100644 index 000000000000..e1c10a128bfd --- /dev/null +++ b/drivers/remoteproc/qcom_q6v5_ipa_notify.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Qualcomm IPA notification subdev support + * + * Copyright (C) 2019 Linaro Ltd. + */ + +#include +#include +#include +#include + +static void +ipa_notify_common(struct rproc_subdev *subdev, enum qcom_rproc_event event) +{ + struct qcom_rproc_ipa_notify *ipa_notify; + qcom_ipa_notify_t notify; + + ipa_notify = container_of(subdev, struct qcom_rproc_ipa_notify, subdev); + notify = ipa_notify->notify; + if (notify) + notify(ipa_notify->data, event); +} + +static int ipa_notify_prepare(struct rproc_subdev *subdev) +{ + ipa_notify_common(subdev, MODEM_STARTING); + + return 0; +} + +static int ipa_notify_start(struct rproc_subdev *subdev) +{ + ipa_notify_common(subdev, MODEM_RUNNING); + + return 0; +} + +static void ipa_notify_stop(struct rproc_subdev *subdev, bool crashed) + +{ + ipa_notify_common(subdev, crashed ? MODEM_CRASHED : MODEM_STOPPING); +} + +static void ipa_notify_unprepare(struct rproc_subdev *subdev) +{ + ipa_notify_common(subdev, MODEM_OFFLINE); +} + +static void ipa_notify_removing(struct rproc_subdev *subdev) +{ + ipa_notify_common(subdev, MODEM_REMOVING); +} + +/* Register the IPA notification subdevice with the Q6V5 MSS remoteproc */ +void qcom_add_ipa_notify_subdev(struct rproc *rproc, + struct qcom_rproc_ipa_notify *ipa_notify) +{ + ipa_notify->notify = NULL; + ipa_notify->data = NULL; + ipa_notify->subdev.prepare = ipa_notify_prepare; + ipa_notify->subdev.start = ipa_notify_start; + ipa_notify->subdev.stop = ipa_notify_stop; + ipa_notify->subdev.unprepare = ipa_notify_unprepare; + + rproc_add_subdev(rproc, &ipa_notify->subdev); +} +EXPORT_SYMBOL_GPL(qcom_add_ipa_notify_subdev); + +/* Remove the IPA notification subdevice */ +void qcom_remove_ipa_notify_subdev(struct rproc *rproc, + struct qcom_rproc_ipa_notify *ipa_notify) +{ + struct rproc_subdev *subdev = &ipa_notify->subdev; + + ipa_notify_removing(subdev); + + rproc_remove_subdev(rproc, subdev); + ipa_notify->notify = NULL; /* Make it obvious */ +} +EXPORT_SYMBOL_GPL(qcom_remove_ipa_notify_subdev); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm IPA notification remoteproc subdev"); diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index a1cc9cbe038f..f9ccce76e44b 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -22,6 +22,7 @@ #include #include #include +#include "linux/remoteproc/qcom_q6v5_ipa_notify.h" #include #include #include @@ -201,6 +202,7 @@ struct q6v5 { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; + struct qcom_rproc_ipa_notify ipa_notify_subdev; struct qcom_sysmon *sysmon; bool need_mem_protection; bool has_alt_reset; @@ -1540,6 +1542,39 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) return 0; } +#if IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) + +/* Register IPA notification function */ +int qcom_register_ipa_notify(struct rproc *rproc, qcom_ipa_notify_t notify, + void *data) +{ + struct qcom_rproc_ipa_notify *ipa_notify; + struct q6v5 *qproc = rproc->priv; + + if (!notify) + return -EINVAL; + + ipa_notify = &qproc->ipa_notify_subdev; + if (ipa_notify->notify) + return -EBUSY; + + ipa_notify->notify = notify; + ipa_notify->data = data; + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_register_ipa_notify); + +/* Deregister IPA notification function */ +void qcom_deregister_ipa_notify(struct rproc *rproc) +{ + struct q6v5 *qproc = rproc->priv; + + qproc->ipa_notify_subdev.notify = NULL; +} +EXPORT_SYMBOL_GPL(qcom_deregister_ipa_notify); +#endif /* !IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) */ + static int q6v5_probe(struct platform_device *pdev) { const struct rproc_hexagon_res *desc; @@ -1664,6 +1699,7 @@ static int q6v5_probe(struct platform_device *pdev) qcom_add_glink_subdev(rproc, &qproc->glink_subdev); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); + qcom_add_ipa_notify_subdev(rproc, &qproc->ipa_notify_subdev); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); if (IS_ERR(qproc->sysmon)) { ret = PTR_ERR(qproc->sysmon); @@ -1677,6 +1713,7 @@ static int q6v5_probe(struct platform_device *pdev) return 0; detach_proxy_pds: + qcom_remove_ipa_notify_subdev(qproc->rproc, &qproc->ipa_notify_subdev); q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); detach_active_pds: q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); @@ -1693,6 +1730,7 @@ static int q6v5_remove(struct platform_device *pdev) rproc_del(qproc->rproc); qcom_remove_sysmon_subdev(qproc->sysmon); + qcom_remove_ipa_notify_subdev(qproc->rproc, &qproc->ipa_notify_subdev); qcom_remove_glink_subdev(qproc->rproc, &qproc->glink_subdev); qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); diff --git a/include/linux/remoteproc/qcom_q6v5_ipa_notify.h b/include/linux/remoteproc/qcom_q6v5_ipa_notify.h new file mode 100644 index 000000000000..0820edc0ab7d --- /dev/null +++ b/include/linux/remoteproc/qcom_q6v5_ipa_notify.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* Copyright (C) 2019 Linaro Ltd. */ + +#ifndef __QCOM_Q6V5_IPA_NOTIFY_H__ +#define __QCOM_Q6V5_IPA_NOTIFY_H__ + +#if IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) + +#include + +enum qcom_rproc_event { + MODEM_STARTING = 0, /* Modem is about to be started */ + MODEM_RUNNING = 1, /* Startup complete; modem is operational */ + MODEM_STOPPING = 2, /* Modem is about to shut down */ + MODEM_CRASHED = 3, /* Modem has crashed (implies stopping) */ + MODEM_OFFLINE = 4, /* Modem is now offline */ + MODEM_REMOVING = 5, /* Modem is about to be removed */ +}; + +typedef void (*qcom_ipa_notify_t)(void *data, enum qcom_rproc_event event); + +struct qcom_rproc_ipa_notify { + struct rproc_subdev subdev; + + qcom_ipa_notify_t notify; + void *data; +}; + +/** + * qcom_add_ipa_notify_subdev() - Register IPA notification subdevice + * @rproc: rproc handle + * @ipa_notify: IPA notification subdevice handle + * + * Register the @ipa_notify subdevice with the @rproc so modem events + * can be sent to IPA when they occur. + * + * This is defined in "qcom_q6v5_ipa_notify.c". + */ +void qcom_add_ipa_notify_subdev(struct rproc *rproc, + struct qcom_rproc_ipa_notify *ipa_notify); + +/** + * qcom_remove_ipa_notify_subdev() - Remove IPA SSR subdevice + * @rproc: rproc handle + * @ipa_notify: IPA notification subdevice handle + * + * This is defined in "qcom_q6v5_ipa_notify.c". + */ +void qcom_remove_ipa_notify_subdev(struct rproc *rproc, + struct qcom_rproc_ipa_notify *ipa_notify); + +/** + * qcom_register_ipa_notify() - Register IPA notification function + * @rproc: Remote processor handle + * @notify: Non-null IPA notification callback function pointer + * @data: Data supplied to IPA notification callback function + * + * @Return: 0 if successful, or a negative error code otherwise + * + * This is defined in "qcom_q6v5_mss.c". + */ +int qcom_register_ipa_notify(struct rproc *rproc, qcom_ipa_notify_t notify, + void *data); +/** + * qcom_deregister_ipa_notify() - Deregister IPA notification function + * @rproc: Remote processor handle + * + * This is defined in "qcom_q6v5_mss.c". + */ +void qcom_deregister_ipa_notify(struct rproc *rproc); + +#else /* !IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) */ + +struct qcom_rproc_ipa_notify { /* empty */ }; + +#define qcom_add_ipa_notify_subdev(rproc, ipa_notify) /* no-op */ +#define qcom_remove_ipa_notify_subdev(rproc, ipa_notify) /* no-op */ + +#endif /* !IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) */ + +#endif /* !__QCOM_Q6V5_IPA_NOTIFY_H__ */ -- cgit v1.2.3 From 0cfb07c5f221be5a1219f590f576c48de4efedf3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 12 Jan 2020 17:49:45 +0100 Subject: zorro: Make zorro_match_device() static Unlike its PCI counterpart, zorro_match_device() was never used outside the Zorro bus code. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200112164949.20196-2-geert@linux-m68k.org --- drivers/zorro/zorro-driver.c | 3 +-- include/linux/zorro.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/zorro/zorro-driver.c b/drivers/zorro/zorro-driver.c index fa23b7366b98..84ac94aecb79 100644 --- a/drivers/zorro/zorro-driver.c +++ b/drivers/zorro/zorro-driver.c @@ -28,7 +28,7 @@ * zorro_device_id structure or %NULL if there is no match. */ -const struct zorro_device_id * +static const struct zorro_device_id * zorro_match_device(const struct zorro_device_id *ids, const struct zorro_dev *z) { @@ -39,7 +39,6 @@ zorro_match_device(const struct zorro_device_id *ids, } return NULL; } -EXPORT_SYMBOL(zorro_match_device); static int zorro_device_probe(struct device *dev) diff --git a/include/linux/zorro.h b/include/linux/zorro.h index 63fbba0740c2..cb72515b0ac1 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -70,7 +70,6 @@ struct zorro_driver { /* New-style probing */ extern int zorro_register_driver(struct zorro_driver *); extern void zorro_unregister_driver(struct zorro_driver *); -extern const struct zorro_device_id *zorro_match_device(const struct zorro_device_id *ids, const struct zorro_dev *z); static inline struct zorro_driver *zorro_dev_driver(const struct zorro_dev *z) { return z->driver; -- cgit v1.2.3 From 1c5b915a13068313a0809404964737bf5aedace7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 12 Jan 2020 17:49:48 +0100 Subject: zorro: Remove unused zorro_dev_driver() This function was never used. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200112164949.20196-5-geert@linux-m68k.org --- include/linux/zorro.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/zorro.h b/include/linux/zorro.h index cb72515b0ac1..22f3f80fbcb5 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -70,10 +70,6 @@ struct zorro_driver { /* New-style probing */ extern int zorro_register_driver(struct zorro_driver *); extern void zorro_unregister_driver(struct zorro_driver *); -static inline struct zorro_driver *zorro_dev_driver(const struct zorro_dev *z) -{ - return z->driver; -} extern unsigned int zorro_num_autocon; /* # of autoconfig devices found */ -- cgit v1.2.3 From 7332bc4dc89c87a64a19dab96a41525d5630e9cf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 12 Jan 2020 17:49:49 +0100 Subject: zorro: Move zorro_bus_type to bus-private header file zorro_bus_type was never used outside the Zorro bus code. Hence move it from the public to the bus-private header file. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200112164949.20196-6-geert@linux-m68k.org --- drivers/zorro/zorro.h | 7 +++++++ include/linux/zorro.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/zorro/zorro.h b/drivers/zorro/zorro.h index ac0bab3412d9..f84df9fb4c20 100644 --- a/drivers/zorro/zorro.h +++ b/drivers/zorro/zorro.h @@ -1,5 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ + /* + * Zorro bus + */ + +extern struct bus_type zorro_bus_type; + + #ifdef CONFIG_ZORRO_NAMES extern void zorro_name_device(struct zorro_dev *z); #else diff --git a/include/linux/zorro.h b/include/linux/zorro.h index 22f3f80fbcb5..e2e4de188d84 100644 --- a/include/linux/zorro.h +++ b/include/linux/zorro.h @@ -40,13 +40,6 @@ struct zorro_dev { #define to_zorro_dev(n) container_of(n, struct zorro_dev, dev) - /* - * Zorro bus - */ - -extern struct bus_type zorro_bus_type; - - /* * Zorro device drivers */ -- cgit v1.2.3 From b3875759332e19d3b88da1e7258cdd37ac474d32 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 12 Jan 2020 17:56:11 +0100 Subject: dio: Make dio_match_device() static Unlike its PCI counterpart, dio_match_device() was never used outside the DIO bus code. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200112165613.20960-2-geert@linux-m68k.org --- drivers/dio/dio-driver.c | 3 +-- include/linux/dio.h | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/dio/dio-driver.c b/drivers/dio/dio-driver.c index a7b174ef4c85..daf87e214a77 100644 --- a/drivers/dio/dio-driver.c +++ b/drivers/dio/dio-driver.c @@ -25,7 +25,7 @@ * dio_device_id structure or %NULL if there is no match. */ -const struct dio_device_id * +static const struct dio_device_id * dio_match_device(const struct dio_device_id *ids, const struct dio_dev *d) { @@ -137,7 +137,6 @@ static int __init dio_driver_init(void) postcore_initcall(dio_driver_init); -EXPORT_SYMBOL(dio_match_device); EXPORT_SYMBOL(dio_register_driver); EXPORT_SYMBOL(dio_unregister_driver); EXPORT_SYMBOL(dio_bus_type); diff --git a/include/linux/dio.h b/include/linux/dio.h index 1470d1d943b4..ca07243690ed 100644 --- a/include/linux/dio.h +++ b/include/linux/dio.h @@ -247,7 +247,6 @@ extern int dio_create_sysfs_dev_files(struct dio_dev *); /* New-style probing */ extern int dio_register_driver(struct dio_driver *); extern void dio_unregister_driver(struct dio_driver *); -extern const struct dio_device_id *dio_match_device(const struct dio_device_id *ids, const struct dio_dev *z); static inline struct dio_driver *dio_dev_driver(const struct dio_dev *d) { return d->driver; -- cgit v1.2.3 From 435cb3eaf34e821072134afc2a61329ca2fd7549 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 12 Jan 2020 17:56:13 +0100 Subject: dio: Remove unused dio_dev_driver() This function was never used. Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20200112165613.20960-4-geert@linux-m68k.org --- include/linux/dio.h | 4 ---- 1 file changed, 4 deletions(-) (limited to 'include') diff --git a/include/linux/dio.h b/include/linux/dio.h index ca07243690ed..5abd07361eb5 100644 --- a/include/linux/dio.h +++ b/include/linux/dio.h @@ -247,10 +247,6 @@ extern int dio_create_sysfs_dev_files(struct dio_dev *); /* New-style probing */ extern int dio_register_driver(struct dio_driver *); extern void dio_unregister_driver(struct dio_driver *); -static inline struct dio_driver *dio_dev_driver(const struct dio_dev *d) -{ - return d->driver; -} #define dio_resource_start(d) ((d)->resource.start) #define dio_resource_end(d) ((d)->resource.end) -- cgit v1.2.3 From f1541773af49ecd1edae29c8ac0775253a0b0760 Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Sat, 8 Feb 2020 15:43:50 +0800 Subject: mtd: spinand: rework detect procedure for different READ_ID operation Currently there are 3 different variants of read_id implementation: 1. opcode only. Found in GD5FxGQ4xF. 2. opcode + 1 addr byte. Found in GD5GxGQ4xA/E 3. opcode + 1 dummy byte. Found in other currently supported chips. Original implementation was for variant 1 and let detect function of chips with variant 2 and 3 to ignore the first byte. This isn't robust: 1. For chips of variant 2, if SPI master doesn't keep MOSI low during read, chip will get a random id offset, and the entire id buffer will shift by that offset, causing detect failure. 2. For chips of variant 1, if it happens to get a devid that equals to manufacture id of variant 2 or 3 chips, it'll get incorrectly detected. This patch reworks detect procedure to address problems above. New logic do detection for all variants separatedly, in 1-2-3 order. Since all current detect methods do exactly the same id matching procedure, unify them into core.c and remove detect method from manufacture_ops. Tested on GD5F1GQ4UAYIG and W25N01GVZEIG. Signed-off-by: Chuanhong Guo Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200208074439.146296-1-gch981213@gmail.com --- drivers/mtd/nand/spi/core.c | 86 ++++++++++++++++++++++++++++----------- drivers/mtd/nand/spi/gigadevice.c | 45 ++++++-------------- drivers/mtd/nand/spi/macronix.c | 30 +++----------- drivers/mtd/nand/spi/micron.c | 26 ++---------- drivers/mtd/nand/spi/paragon.c | 28 +++---------- drivers/mtd/nand/spi/toshiba.c | 45 ++++++++------------ drivers/mtd/nand/spi/winbond.c | 34 +++------------- include/linux/mtd/spinand.h | 66 ++++++++++++++++++++---------- 8 files changed, 157 insertions(+), 203 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 89f6beefb01c..a9e9cbad942f 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -370,10 +371,11 @@ out: return status & STATUS_BUSY ? -ETIMEDOUT : 0; } -static int spinand_read_id_op(struct spinand_device *spinand, u8 *buf) +static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, + u8 ndummy, u8 *buf) { - struct spi_mem_op op = SPINAND_READID_OP(0, spinand->scratchbuf, - SPINAND_MAX_ID_LEN); + struct spi_mem_op op = SPINAND_READID_OP( + naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -762,24 +764,62 @@ static const struct spinand_manufacturer *spinand_manufacturers[] = { &winbond_spinand_manufacturer, }; -static int spinand_manufacturer_detect(struct spinand_device *spinand) +static int spinand_manufacturer_match(struct spinand_device *spinand, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; unsigned int i; int ret; for (i = 0; i < ARRAY_SIZE(spinand_manufacturers); i++) { - ret = spinand_manufacturers[i]->ops->detect(spinand); - if (ret > 0) { - spinand->manufacturer = spinand_manufacturers[i]; - return 0; - } else if (ret < 0) { - return ret; - } - } + const struct spinand_manufacturer *manufacturer = + spinand_manufacturers[i]; + + if (id[0] != manufacturer->id) + continue; + ret = spinand_match_and_init(spinand, + manufacturer->chips, + manufacturer->nchips, + rdid_method); + if (ret < 0) + continue; + + spinand->manufacturer = manufacturer; + return 0; + } return -ENOTSUPP; } +static int spinand_id_detect(struct spinand_device *spinand) +{ + u8 *id = spinand->id.data; + int ret; + + ret = spinand_read_id_op(spinand, 0, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, SPINAND_READID_METHOD_OPCODE); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 1, 0, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_ADDR); + if (!ret) + return 0; + + ret = spinand_read_id_op(spinand, 0, 1, id); + if (ret) + return ret; + ret = spinand_manufacturer_match(spinand, + SPINAND_READID_METHOD_OPCODE_DUMMY); + + return ret; +} + static int spinand_manufacturer_init(struct spinand_device *spinand) { if (spinand->manufacturer->ops->init) @@ -835,9 +875,9 @@ spinand_select_op_variant(struct spinand_device *spinand, * @spinand: SPI NAND object * @table: SPI NAND device description table * @table_size: size of the device description table + * @rdid_method: read id method to match * - * Should be used by SPI NAND manufacturer drivers when they want to find a - * match between a device ID retrieved through the READ_ID command and an + * Match between a device ID retrieved through the READ_ID command and an * entry in the SPI NAND description table. If a match is found, the spinand * object will be initialized with information provided by the matching * spinand_info entry. @@ -846,8 +886,10 @@ spinand_select_op_variant(struct spinand_device *spinand, */ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u16 devid) + unsigned int table_size, + enum spinand_readid_method rdid_method) { + u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; @@ -855,13 +897,17 @@ int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *info = &table[i]; const struct spi_mem_op *op; - if (devid != info->devid) + if (rdid_method != info->devid.method) + continue; + + if (memcmp(id + 1, info->devid.id, info->devid.len)) continue; nand->memorg = table[i].memorg; nand->eccreq = table[i].eccreq; spinand->eccinfo = table[i].eccinfo; spinand->flags = table[i].flags; + spinand->id.len = 1 + table[i].devid.len; spinand->select_target = table[i].select_target; op = spinand_select_op_variant(spinand, @@ -898,13 +944,7 @@ static int spinand_detect(struct spinand_device *spinand) if (ret) return ret; - ret = spinand_read_id_op(spinand, spinand->id.data); - if (ret) - return ret; - - spinand->id.len = SPINAND_MAX_ID_LEN; - - ret = spinand_manufacturer_detect(spinand); + ret = spinand_id_detect(spinand); if (ret) { dev_err(dev, "unknown raw ID %*phN\n", SPINAND_MAX_ID_LEN, spinand->id.data); diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index e99d425aa93f..d219c970042a 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -195,7 +195,8 @@ static int gd5fxgq4ufxxg_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info gigadevice_spinand_table[] = { - SPINAND_INFO("GD5F1GQ4xA", 0xF1, + SPINAND_INFO("GD5F1GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf1), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -204,7 +205,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F2GQ4xA", 0xF2, + SPINAND_INFO("GD5F2GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf2), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -213,7 +215,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F4GQ4xA", 0xF4, + SPINAND_INFO("GD5F4GQ4xA", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xf4), NAND_MEMORG(1, 2048, 64, 64, 4096, 80, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -222,7 +225,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout, gd5fxgq4xa_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UExxG", 0xd1, + SPINAND_INFO("GD5F1GQ4UExxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd1), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -231,7 +235,8 @@ static const struct spinand_info gigadevice_spinand_table[] = { 0, SPINAND_ECCINFO(&gd5fxgq4_variant2_ooblayout, gd5fxgq4uexxg_ecc_get_status)), - SPINAND_INFO("GD5F1GQ4UFxxG", 0xb148, + SPINAND_INFO("GD5F1GQ4UFxxG", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants_f, @@ -242,39 +247,13 @@ static const struct spinand_info gigadevice_spinand_table[] = { gd5fxgq4ufxxg_ecc_get_status)), }; -static int gigadevice_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - u16 did; - int ret; - - /* - * Earlier GDF5-series devices (A,E) return [0][MID][DID] - * Later (F) devices return [MID][DID1][DID2] - */ - - if (id[0] == SPINAND_MFR_GIGADEVICE) - did = (id[1] << 8) + id[2]; - else if (id[0] == 0 && id[1] == SPINAND_MFR_GIGADEVICE) - did = id[2]; - else - return 0; - - ret = spinand_match_and_init(spinand, gigadevice_spinand_table, - ARRAY_SIZE(gigadevice_spinand_table), - did); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = { - .detect = gigadevice_spinand_detect, }; const struct spinand_manufacturer gigadevice_spinand_manufacturer = { .id = SPINAND_MFR_GIGADEVICE, .name = "GigaDevice", + .chips = gigadevice_spinand_table, + .nchips = ARRAY_SIZE(gigadevice_spinand_table), .ops = &gigadevice_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index 21def3f8fb36..0f900f3aa21a 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -99,7 +99,8 @@ static int mx35lf1ge4ab_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info macronix_spinand_table[] = { - SPINAND_INFO("MX35LF1GE4AB", 0x12, + SPINAND_INFO("MX35LF1GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x12), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -108,7 +109,8 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_HAS_QE_BIT, SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, mx35lf1ge4ab_ecc_get_status)), - SPINAND_INFO("MX35LF2GE4AB", 0x22, + SPINAND_INFO("MX35LF2GE4AB", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x22), NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(4, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -118,33 +120,13 @@ static const struct spinand_info macronix_spinand_table[] = { SPINAND_ECCINFO(&mx35lfxge4ab_ooblayout, NULL)), }; -static int macronix_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Macronix SPI NAND read ID needs a dummy byte, so the first byte in - * raw_id is garbage. - */ - if (id[1] != SPINAND_MFR_MACRONIX) - return 0; - - ret = spinand_match_and_init(spinand, macronix_spinand_table, - ARRAY_SIZE(macronix_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops macronix_spinand_manuf_ops = { - .detect = macronix_spinand_detect, }; const struct spinand_manufacturer macronix_spinand_manufacturer = { .id = SPINAND_MFR_MACRONIX, .name = "Macronix", + .chips = macronix_spinand_table, + .nchips = ARRAY_SIZE(macronix_spinand_table), .ops = ¯onix_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 7d7b1f7fcf71..f56f81325e10 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -91,7 +91,8 @@ static int mt29f2g01abagd_ecc_get_status(struct spinand_device *spinand, } static const struct spinand_info micron_spinand_table[] = { - SPINAND_INFO("MT29F2G01ABAGD", 0x24, + SPINAND_INFO("MT29F2G01ABAGD", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x24), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 2, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -102,32 +103,13 @@ static const struct spinand_info micron_spinand_table[] = { mt29f2g01abagd_ecc_get_status)), }; -static int micron_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Micron SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_MICRON) - return 0; - - ret = spinand_match_and_init(spinand, micron_spinand_table, - ARRAY_SIZE(micron_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { - .detect = micron_spinand_detect, }; const struct spinand_manufacturer micron_spinand_manufacturer = { .id = SPINAND_MFR_MICRON, .name = "Micron", + .chips = micron_spinand_table, + .nchips = ARRAY_SIZE(micron_spinand_table), .ops = µn_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/paragon.c b/drivers/mtd/nand/spi/paragon.c index 52307681cbd0..519ade513c1f 100644 --- a/drivers/mtd/nand/spi/paragon.c +++ b/drivers/mtd/nand/spi/paragon.c @@ -97,7 +97,8 @@ static const struct mtd_ooblayout_ops pn26g0xa_ooblayout = { static const struct spinand_info paragon_spinand_table[] = { - SPINAND_INFO("PN26G01A", 0xe1, + SPINAND_INFO("PN26G01A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe1), NAND_MEMORG(1, 2048, 128, 64, 1024, 21, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -106,7 +107,8 @@ static const struct spinand_info paragon_spinand_table[] = { 0, SPINAND_ECCINFO(&pn26g0xa_ooblayout, pn26g0xa_ecc_get_status)), - SPINAND_INFO("PN26G02A", 0xe2, + SPINAND_INFO("PN26G02A", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xe2), NAND_MEMORG(1, 2048, 128, 64, 2048, 41, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -117,31 +119,13 @@ static const struct spinand_info paragon_spinand_table[] = { pn26g0xa_ecc_get_status)), }; -static int paragon_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* Read ID returns [0][MID][DID] */ - - if (id[1] != SPINAND_MFR_PARAGON) - return 0; - - ret = spinand_match_and_init(spinand, paragon_spinand_table, - ARRAY_SIZE(paragon_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops paragon_spinand_manuf_ops = { - .detect = paragon_spinand_detect, }; const struct spinand_manufacturer paragon_spinand_manufacturer = { .id = SPINAND_MFR_PARAGON, .name = "Paragon", + .chips = paragon_spinand_table, + .nchips = ARRAY_SIZE(paragon_spinand_table), .ops = ¶gon_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 833e8f64e0a0..d34773191700 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -96,7 +96,8 @@ static int tc58cxgxsx_ecc_get_status(struct spinand_device *spinand, static const struct spinand_info toshiba_spinand_table[] = { /* 3.3V 1Gb */ - SPINAND_INFO("TC58CVG0S3", 0xC2, + SPINAND_INFO("TC58CVG0S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xC2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -106,7 +107,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 2Gb */ - SPINAND_INFO("TC58CVG1S3", 0xCB, + SPINAND_INFO("TC58CVG1S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -116,7 +118,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", 0xCD, + SPINAND_INFO("TC58CVG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xCD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -126,7 +129,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 3.3V 4Gb */ - SPINAND_INFO("TC58CVG2S0", 0xED, + SPINAND_INFO("TC58CVG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xED), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -136,7 +140,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 1Gb */ - SPINAND_INFO("TC58CYG0S3", 0xB2, + SPINAND_INFO("TC58CYG0S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xB2), NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -146,7 +151,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 2Gb */ - SPINAND_INFO("TC58CYG1S3", 0xBB, + SPINAND_INFO("TC58CYG1S3", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBB), NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -156,7 +162,8 @@ static const struct spinand_info toshiba_spinand_table[] = { SPINAND_ECCINFO(&tc58cxgxsx_ooblayout, tc58cxgxsx_ecc_get_status)), /* 1.8V 4Gb */ - SPINAND_INFO("TC58CYG2S0", 0xBD, + SPINAND_INFO("TC58CYG2S0", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xBD), NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1), NAND_ECCREQ(8, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -167,33 +174,13 @@ static const struct spinand_info toshiba_spinand_table[] = { tc58cxgxsx_ecc_get_status)), }; -static int toshiba_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Toshiba SPI NAND read ID needs a dummy byte, - * so the first byte in id is garbage. - */ - if (id[1] != SPINAND_MFR_TOSHIBA) - return 0; - - ret = spinand_match_and_init(spinand, toshiba_spinand_table, - ARRAY_SIZE(toshiba_spinand_table), - id[2]); - if (ret) - return ret; - - return 1; -} - static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = { - .detect = toshiba_spinand_detect, }; const struct spinand_manufacturer toshiba_spinand_manufacturer = { .id = SPINAND_MFR_TOSHIBA, .name = "Toshiba", + .chips = toshiba_spinand_table, + .nchips = ARRAY_SIZE(toshiba_spinand_table), .ops = &toshiba_spinand_manuf_ops, }; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index a6c17e0cace8..76684428354e 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -75,7 +75,8 @@ static int w25m02gv_select_target(struct spinand_device *spinand, } static const struct spinand_info winbond_spinand_table[] = { - SPINAND_INFO("W25M02GV", 0xAB, + SPINAND_INFO("W25M02GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -84,7 +85,8 @@ static const struct spinand_info winbond_spinand_table[] = { 0, SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), SPINAND_SELECT_TARGET(w25m02gv_select_target)), - SPINAND_INFO("W25N01GV", 0xAA, + SPINAND_INFO("W25N01GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), NAND_ECCREQ(1, 512), SPINAND_INFO_OP_VARIANTS(&read_cache_variants, @@ -94,31 +96,6 @@ static const struct spinand_info winbond_spinand_table[] = { SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), }; -/** - * winbond_spinand_detect - initialize device related part in spinand_device - * struct if it is a Winbond device. - * @spinand: SPI NAND device structure - */ -static int winbond_spinand_detect(struct spinand_device *spinand) -{ - u8 *id = spinand->id.data; - int ret; - - /* - * Winbond SPI NAND read ID need a dummy byte, - * so the first byte in raw_id is dummy. - */ - if (id[1] != SPINAND_MFR_WINBOND) - return 0; - - ret = spinand_match_and_init(spinand, winbond_spinand_table, - ARRAY_SIZE(winbond_spinand_table), id[2]); - if (ret) - return ret; - - return 1; -} - static int winbond_spinand_init(struct spinand_device *spinand) { struct nand_device *nand = spinand_to_nand(spinand); @@ -138,12 +115,13 @@ static int winbond_spinand_init(struct spinand_device *spinand) } static const struct spinand_manufacturer_ops winbond_spinand_manuf_ops = { - .detect = winbond_spinand_detect, .init = winbond_spinand_init, }; const struct spinand_manufacturer winbond_spinand_manufacturer = { .id = SPINAND_MFR_WINBOND, .name = "Winbond", + .chips = winbond_spinand_table, + .nchips = ARRAY_SIZE(winbond_spinand_table), .ops = &winbond_spinand_manuf_ops, }; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 4ea558bd3c46..f4c4ae87181b 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -32,9 +32,9 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPINAND_READID_OP(ndummy, buf, len) \ +#define SPINAND_READID_OP(naddr, ndummy, buf, len) \ SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ - SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_ADDR(naddr, 0, 1), \ SPI_MEM_OP_DUMMY(ndummy, 1), \ SPI_MEM_OP_DATA_IN(len, buf, 1)) @@ -176,37 +176,46 @@ struct spinand_device; * @data: buffer containing the id bytes. Currently 4 bytes large, but can * be extended if required * @len: ID length - * - * struct_spinand_id->data contains all bytes returned after a READ_ID command, - * including dummy bytes if the chip does not emit ID bytes right after the - * READ_ID command. The responsibility to extract real ID bytes is left to - * struct_manufacurer_ops->detect(). */ struct spinand_id { u8 data[SPINAND_MAX_ID_LEN]; int len; }; +enum spinand_readid_method { + SPINAND_READID_METHOD_OPCODE, + SPINAND_READID_METHOD_OPCODE_ADDR, + SPINAND_READID_METHOD_OPCODE_DUMMY, +}; + +/** + * struct spinand_devid - SPI NAND device id structure + * @id: device id of current chip + * @len: number of bytes in device id + * @method: method to read chip id + * There are 3 possible variants: + * SPINAND_READID_METHOD_OPCODE: chip id is returned immediately + * after read_id opcode. + * SPINAND_READID_METHOD_OPCODE_ADDR: chip id is returned after + * read_id opcode + 1-byte address. + * SPINAND_READID_METHOD_OPCODE_DUMMY: chip id is returned after + * read_id opcode + 1 dummy byte. + */ +struct spinand_devid { + const u8 *id; + const u8 len; + const enum spinand_readid_method method; +}; + /** * struct manufacurer_ops - SPI NAND manufacturer specific operations - * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed - * the core calls the struct_manufacurer_ops->detect() hook of each - * registered manufacturer until one of them return 1. Note that - * the first thing to check in this hook is that the manufacturer ID - * in struct_spinand_device->id matches the manufacturer whose - * ->detect() hook has been called. Should return 1 if there's a - * match, 0 if the manufacturer ID does not match and a negative - * error code otherwise. When true is returned, the core assumes - * that properties of the NAND chip (spinand->base.memorg and - * spinand->base.eccreq) have been filled * @init: initialize a SPI NAND device * @cleanup: cleanup a SPI NAND device * * Each SPI NAND manufacturer driver should implement this interface so that - * NAND chips coming from this vendor can be detected and initialized properly. + * NAND chips coming from this vendor can be initialized properly. */ struct spinand_manufacturer_ops { - int (*detect)(struct spinand_device *spinand); int (*init)(struct spinand_device *spinand); void (*cleanup)(struct spinand_device *spinand); }; @@ -215,11 +224,16 @@ struct spinand_manufacturer_ops { * struct spinand_manufacturer - SPI NAND manufacturer instance * @id: manufacturer ID * @name: manufacturer name + * @devid_len: number of bytes in device ID + * @chips: supported SPI NANDs under current manufacturer + * @nchips: number of SPI NANDs available in chips array * @ops: manufacturer operations */ struct spinand_manufacturer { u8 id; char *name; + const struct spinand_info *chips; + const size_t nchips; const struct spinand_manufacturer_ops *ops; }; @@ -291,7 +305,7 @@ struct spinand_ecc_info { */ struct spinand_info { const char *model; - u16 devid; + struct spinand_devid devid; u32 flags; struct nand_memory_organization memorg; struct nand_ecc_req eccreq; @@ -305,6 +319,13 @@ struct spinand_info { unsigned int target); }; +#define SPINAND_ID(__method, ...) \ + { \ + .id = (const u8[]){ __VA_ARGS__ }, \ + .len = sizeof((u8[]){ __VA_ARGS__ }), \ + .method = __method, \ + } + #define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ { \ .read_cache = __read, \ @@ -451,9 +472,10 @@ static inline void spinand_set_of_node(struct spinand_device *spinand, nanddev_set_of_node(&spinand->base, np); } -int spinand_match_and_init(struct spinand_device *dev, +int spinand_match_and_init(struct spinand_device *spinand, const struct spinand_info *table, - unsigned int table_size, u16 devid); + unsigned int table_size, + enum spinand_readid_method rdid_method); int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); int spinand_select_target(struct spinand_device *spinand, unsigned int target); -- cgit v1.2.3 From dceeb0f0e61071b1d990459dbd6a53f590cdaf77 Mon Sep 17 00:00:00 2001 From: Tejas Patel Date: Thu, 9 Jan 2020 11:06:03 -0800 Subject: include: linux: firmware: Correct config dependency of zynqmp_eemi_ops zynqmp_eemi_ops will be compiled only when CONFIG_ZYNQMP_FIRMWARE is enabled. So check for CONFIG_ZYNQMP_FIRMWARE instead of checking for CONFIG_ARCH_ZYNQMP. Signed-off-by: Tejas Patel Signed-off-by: Jolly Shah Signed-off-by: Michal Simek --- include/linux/firmware/xlnx-zynqmp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 2cd12ebd6826..ed1aace0cbbc 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -320,7 +320,7 @@ struct zynqmp_eemi_ops { int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 *ret_payload); -#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP) +#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE) const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void); #else static inline struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) -- cgit v1.2.3 From 7b70973d7edb2f005511102d5a2e0116464a46a1 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 9 Mar 2020 11:12:32 +0000 Subject: bpf: sockmap: Only check ULP for TCP sockets The sock map code checks that a socket does not have an active upper layer protocol before inserting it into the map. This requires casting via inet_csk, which isn't valid for UDP sockets. Guard checks for ULP by checking inet_sk(sk)->is_icsk first. Signed-off-by: Lorenz Bauer Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309111243.6982-2-lmb@cloudflare.com --- include/linux/skmsg.h | 8 +++++++- include/net/inet_connection_sock.h | 6 ++++++ net/core/sock_map.c | 6 ++---- net/ipv4/tcp_ulp.c | 7 ------- 4 files changed, 15 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 112765bd146d..4d3d75d63066 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -360,7 +360,13 @@ static inline void sk_psock_restore_proto(struct sock *sk, struct sk_psock *psock) { sk->sk_prot->unhash = psock->saved_unhash; - tcp_update_ulp(sk, psock->sk_proto, psock->saved_write_space); + if (inet_csk_has_ulp(sk)) { + tcp_update_ulp(sk, psock->sk_proto, psock->saved_write_space); + } else { + sk->sk_write_space = psock->saved_write_space; + /* Pairs with lockless read in sk_clone_lock() */ + WRITE_ONCE(sk->sk_prot, psock->sk_proto); + } } static inline void sk_psock_set_state(struct sk_psock *psock, diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 895546058a20..a3f076befa4f 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -335,4 +335,10 @@ static inline void inet_csk_inc_pingpong_cnt(struct sock *sk) if (icsk->icsk_ack.pingpong < U8_MAX) icsk->icsk_ack.pingpong++; } + +static inline bool inet_csk_has_ulp(struct sock *sk) +{ + return inet_sk(sk)->is_icsk && !!inet_csk(sk)->icsk_ulp_ops; +} + #endif /* _INET_CONNECTION_SOCK_H */ diff --git a/net/core/sock_map.c b/net/core/sock_map.c index 2e0f465295c3..cb8f740f7949 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -384,7 +384,6 @@ static int sock_map_update_common(struct bpf_map *map, u32 idx, struct sock *sk, u64 flags) { struct bpf_stab *stab = container_of(map, struct bpf_stab, map); - struct inet_connection_sock *icsk = inet_csk(sk); struct sk_psock_link *link; struct sk_psock *psock; struct sock *osk; @@ -395,7 +394,7 @@ static int sock_map_update_common(struct bpf_map *map, u32 idx, return -EINVAL; if (unlikely(idx >= map->max_entries)) return -E2BIG; - if (unlikely(rcu_access_pointer(icsk->icsk_ulp_data))) + if (inet_csk_has_ulp(sk)) return -EINVAL; link = sk_psock_init_link(); @@ -738,7 +737,6 @@ static int sock_hash_update_common(struct bpf_map *map, void *key, struct sock *sk, u64 flags) { struct bpf_htab *htab = container_of(map, struct bpf_htab, map); - struct inet_connection_sock *icsk = inet_csk(sk); u32 key_size = map->key_size, hash; struct bpf_htab_elem *elem, *elem_new; struct bpf_htab_bucket *bucket; @@ -749,7 +747,7 @@ static int sock_hash_update_common(struct bpf_map *map, void *key, WARN_ON_ONCE(!rcu_read_lock_held()); if (unlikely(flags > BPF_EXIST)) return -EINVAL; - if (unlikely(icsk->icsk_ulp_data)) + if (inet_csk_has_ulp(sk)) return -EINVAL; link = sk_psock_init_link(); diff --git a/net/ipv4/tcp_ulp.c b/net/ipv4/tcp_ulp.c index 2703f24c5d1a..7c27aa629af1 100644 --- a/net/ipv4/tcp_ulp.c +++ b/net/ipv4/tcp_ulp.c @@ -105,13 +105,6 @@ void tcp_update_ulp(struct sock *sk, struct proto *proto, { struct inet_connection_sock *icsk = inet_csk(sk); - if (!icsk->icsk_ulp_ops) { - sk->sk_write_space = write_space; - /* Pairs with lockless read in sk_clone_lock() */ - WRITE_ONCE(sk->sk_prot, proto); - return; - } - if (icsk->icsk_ulp_ops->update) icsk->icsk_ulp_ops->update(sk, proto, write_space); } -- cgit v1.2.3 From 1a2e20132db7bb76dd4f97b8364bd167227dd15f Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 9 Mar 2020 11:12:33 +0000 Subject: skmsg: Update saved hooks only once Only update psock->saved_* if psock->sk_proto has not been initialized yet. This allows us to get rid of tcp_bpf_reinit_sk_prot. Signed-off-by: Lorenz Bauer Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309111243.6982-3-lmb@cloudflare.com --- include/linux/skmsg.h | 20 ++++++++++++++++---- net/ipv4/tcp_bpf.c | 16 +--------------- 2 files changed, 17 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 4d3d75d63066..2be51b7a5800 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -347,11 +347,23 @@ static inline void sk_psock_update_proto(struct sock *sk, struct sk_psock *psock, struct proto *ops) { - psock->saved_unhash = sk->sk_prot->unhash; - psock->saved_close = sk->sk_prot->close; - psock->saved_write_space = sk->sk_write_space; + /* Initialize saved callbacks and original proto only once, since this + * function may be called multiple times for a psock, e.g. when + * psock->progs.msg_parser is updated. + * + * Since we've not installed the new proto, psock is not yet in use and + * we can initialize it without synchronization. + */ + if (!psock->sk_proto) { + struct proto *orig = READ_ONCE(sk->sk_prot); + + psock->saved_unhash = orig->unhash; + psock->saved_close = orig->close; + psock->saved_write_space = sk->sk_write_space; + + psock->sk_proto = orig; + } - psock->sk_proto = sk->sk_prot; /* Pairs with lockless read in sk_clone_lock() */ WRITE_ONCE(sk->sk_prot, ops); } diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 7d6e1b75d4d4..3327afa05c3d 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -637,20 +637,6 @@ static void tcp_bpf_update_sk_prot(struct sock *sk, struct sk_psock *psock) sk_psock_update_proto(sk, psock, &tcp_bpf_prots[family][config]); } -static void tcp_bpf_reinit_sk_prot(struct sock *sk, struct sk_psock *psock) -{ - int family = sk->sk_family == AF_INET6 ? TCP_BPF_IPV6 : TCP_BPF_IPV4; - int config = psock->progs.msg_parser ? TCP_BPF_TX : TCP_BPF_BASE; - - /* Reinit occurs when program types change e.g. TCP_BPF_TX is removed - * or added requiring sk_prot hook updates. We keep original saved - * hooks in this case. - * - * Pairs with lockless read in sk_clone_lock(). - */ - WRITE_ONCE(sk->sk_prot, &tcp_bpf_prots[family][config]); -} - static int tcp_bpf_assert_proto_ops(struct proto *ops) { /* In order to avoid retpoline, we make assumptions when we call @@ -670,7 +656,7 @@ void tcp_bpf_reinit(struct sock *sk) rcu_read_lock(); psock = sk_psock(sk); - tcp_bpf_reinit_sk_prot(sk, psock); + tcp_bpf_update_sk_prot(sk, psock); rcu_read_unlock(); } -- cgit v1.2.3 From d19da360ee0f3e6c1375391db1a724b66fd43312 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 9 Mar 2020 11:12:34 +0000 Subject: bpf: tcp: Move assertions into tcp_bpf_get_proto We need to ensure that sk->sk_prot uses certain callbacks, so that code that directly calls e.g. tcp_sendmsg in certain corner cases works. To avoid spurious asserts, we must to do this only if sk_psock_update_proto has not yet been called. The same invariants apply for tcp_bpf_check_v6_needs_rebuild, so move the call as well. Doing so allows us to merge tcp_bpf_init and tcp_bpf_reinit. Signed-off-by: Lorenz Bauer Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309111243.6982-4-lmb@cloudflare.com --- include/net/tcp.h | 1 - net/core/sock_map.c | 25 +++++++++---------------- net/ipv4/tcp_bpf.c | 42 ++++++++++++++++++++++-------------------- 3 files changed, 31 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 07f947cc80e6..ccf39d80b695 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2196,7 +2196,6 @@ struct sk_msg; struct sk_psock; int tcp_bpf_init(struct sock *sk); -void tcp_bpf_reinit(struct sock *sk); int tcp_bpf_sendmsg_redir(struct sock *sk, struct sk_msg *msg, u32 bytes, int flags); int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, diff --git a/net/core/sock_map.c b/net/core/sock_map.c index cb8f740f7949..fafcbd22ecba 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -145,8 +145,8 @@ static int sock_map_link(struct bpf_map *map, struct sk_psock_progs *progs, struct sock *sk) { struct bpf_prog *msg_parser, *skb_parser, *skb_verdict; - bool skb_progs, sk_psock_is_new = false; struct sk_psock *psock; + bool skb_progs; int ret; skb_verdict = READ_ONCE(progs->skb_verdict); @@ -191,18 +191,14 @@ static int sock_map_link(struct bpf_map *map, struct sk_psock_progs *progs, ret = -ENOMEM; goto out_progs; } - sk_psock_is_new = true; } if (msg_parser) psock_set_prog(&psock->progs.msg_parser, msg_parser); - if (sk_psock_is_new) { - ret = tcp_bpf_init(sk); - if (ret < 0) - goto out_drop; - } else { - tcp_bpf_reinit(sk); - } + + ret = tcp_bpf_init(sk); + if (ret < 0) + goto out_drop; write_lock_bh(&sk->sk_callback_lock); if (skb_progs && !psock->parser.enabled) { @@ -239,15 +235,12 @@ static int sock_map_link_no_progs(struct bpf_map *map, struct sock *sk) if (IS_ERR(psock)) return PTR_ERR(psock); - if (psock) { - tcp_bpf_reinit(sk); - return 0; + if (!psock) { + psock = sk_psock_init(sk, map->numa_node); + if (!psock) + return -ENOMEM; } - psock = sk_psock_init(sk, map->numa_node); - if (!psock) - return -ENOMEM; - ret = tcp_bpf_init(sk); if (ret < 0) sk_psock_put(sk, psock); diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index 3327afa05c3d..ed8a8f3c9afe 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -629,14 +629,6 @@ static int __init tcp_bpf_v4_build_proto(void) } core_initcall(tcp_bpf_v4_build_proto); -static void tcp_bpf_update_sk_prot(struct sock *sk, struct sk_psock *psock) -{ - int family = sk->sk_family == AF_INET6 ? TCP_BPF_IPV6 : TCP_BPF_IPV4; - int config = psock->progs.msg_parser ? TCP_BPF_TX : TCP_BPF_BASE; - - sk_psock_update_proto(sk, psock, &tcp_bpf_prots[family][config]); -} - static int tcp_bpf_assert_proto_ops(struct proto *ops) { /* In order to avoid retpoline, we make assumptions when we call @@ -648,34 +640,44 @@ static int tcp_bpf_assert_proto_ops(struct proto *ops) ops->sendpage == tcp_sendpage ? 0 : -ENOTSUPP; } -void tcp_bpf_reinit(struct sock *sk) +static struct proto *tcp_bpf_get_proto(struct sock *sk, struct sk_psock *psock) { - struct sk_psock *psock; + int family = sk->sk_family == AF_INET6 ? TCP_BPF_IPV6 : TCP_BPF_IPV4; + int config = psock->progs.msg_parser ? TCP_BPF_TX : TCP_BPF_BASE; - sock_owned_by_me(sk); + if (!psock->sk_proto) { + struct proto *ops = READ_ONCE(sk->sk_prot); - rcu_read_lock(); - psock = sk_psock(sk); - tcp_bpf_update_sk_prot(sk, psock); - rcu_read_unlock(); + if (tcp_bpf_assert_proto_ops(ops)) + return ERR_PTR(-EINVAL); + + tcp_bpf_check_v6_needs_rebuild(sk, ops); + } + + return &tcp_bpf_prots[family][config]; } int tcp_bpf_init(struct sock *sk) { - struct proto *ops = READ_ONCE(sk->sk_prot); struct sk_psock *psock; + struct proto *prot; sock_owned_by_me(sk); rcu_read_lock(); psock = sk_psock(sk); - if (unlikely(!psock || psock->sk_proto || - tcp_bpf_assert_proto_ops(ops))) { + if (unlikely(!psock)) { rcu_read_unlock(); return -EINVAL; } - tcp_bpf_check_v6_needs_rebuild(sk, ops); - tcp_bpf_update_sk_prot(sk, psock); + + prot = tcp_bpf_get_proto(sk, psock); + if (IS_ERR(prot)) { + rcu_read_unlock(); + return PTR_ERR(prot); + } + + sk_psock_update_proto(sk, psock, prot); rcu_read_unlock(); return 0; } -- cgit v1.2.3 From 5da0040442312a2b696748f8240243ce543a4970 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 9 Mar 2020 11:12:35 +0000 Subject: bpf: tcp: Guard declarations with CONFIG_NET_SOCK_MSG tcp_bpf.c is only included in the build if CONFIG_NET_SOCK_MSG is selected. The declaration should therefore be guarded as such. Signed-off-by: Lorenz Bauer Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309111243.6982-5-lmb@cloudflare.com --- include/net/tcp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index ccf39d80b695..ad3abeaa703e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2195,6 +2195,7 @@ void tcp_update_ulp(struct sock *sk, struct proto *p, struct sk_msg; struct sk_psock; +#ifdef CONFIG_NET_SOCK_MSG int tcp_bpf_init(struct sock *sk); int tcp_bpf_sendmsg_redir(struct sock *sk, struct sk_msg *msg, u32 bytes, int flags); @@ -2202,13 +2203,12 @@ int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); int __tcp_bpf_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg, int len, int flags); -#ifdef CONFIG_NET_SOCK_MSG void tcp_bpf_clone(const struct sock *sk, struct sock *newsk); #else static inline void tcp_bpf_clone(const struct sock *sk, struct sock *newsk) { } -#endif +#endif /* CONFIG_NET_SOCK_MSG */ /* Call BPF_SOCK_OPS program that returns an int. If the return value * is < 0, then the BPF op failed (for example if the loaded BPF -- cgit v1.2.3 From f747632b608f90217a4e9ebb1deba8a37612aa32 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 9 Mar 2020 11:12:36 +0000 Subject: bpf: sockmap: Move generic sockmap hooks from BPF TCP The init, close and unhash handlers from TCP sockmap are generic, and can be reused by UDP sockmap. Move the helpers into the sockmap code base and expose them. This requires tcp_bpf_get_proto and tcp_bpf_clone to be conditional on BPF_STREAM_PARSER. The moved functions are unmodified, except that sk_psock_unlink is renamed to sock_map_unlink to better match its behaviour. Signed-off-by: Lorenz Bauer Signed-off-by: Daniel Borkmann Reviewed-by: Jakub Sitnicki Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309111243.6982-6-lmb@cloudflare.com --- include/linux/bpf.h | 4 +- include/linux/skmsg.h | 28 ------------- include/net/tcp.h | 15 ++++--- net/core/sock_map.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++--- net/ipv4/tcp_bpf.c | 84 +++------------------------------------ 5 files changed, 118 insertions(+), 119 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 40c53924571d..94a329b9da81 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1419,6 +1419,8 @@ static inline void bpf_map_offload_map_free(struct bpf_map *map) #if defined(CONFIG_BPF_STREAM_PARSER) int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, u32 which); int sock_map_get_from_fd(const union bpf_attr *attr, struct bpf_prog *prog); +void sock_map_unhash(struct sock *sk); +void sock_map_close(struct sock *sk, long timeout); #else static inline int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, u32 which) @@ -1431,7 +1433,7 @@ static inline int sock_map_get_from_fd(const union bpf_attr *attr, { return -EINVAL; } -#endif +#endif /* CONFIG_BPF_STREAM_PARSER */ #if defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL) void bpf_sk_reuseport_detach(struct sock *sk); diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h index 2be51b7a5800..8a709f63c5e5 100644 --- a/include/linux/skmsg.h +++ b/include/linux/skmsg.h @@ -323,14 +323,6 @@ static inline void sk_psock_free_link(struct sk_psock_link *link) } struct sk_psock_link *sk_psock_link_pop(struct sk_psock *psock); -#if defined(CONFIG_BPF_STREAM_PARSER) -void sk_psock_unlink(struct sock *sk, struct sk_psock_link *link); -#else -static inline void sk_psock_unlink(struct sock *sk, - struct sk_psock_link *link) -{ -} -#endif void __sk_psock_purge_ingress_msg(struct sk_psock *psock); @@ -399,26 +391,6 @@ static inline bool sk_psock_test_state(const struct sk_psock *psock, return test_bit(bit, &psock->state); } -static inline struct sk_psock *sk_psock_get_checked(struct sock *sk) -{ - struct sk_psock *psock; - - rcu_read_lock(); - psock = sk_psock(sk); - if (psock) { - if (sk->sk_prot->recvmsg != tcp_bpf_recvmsg) { - psock = ERR_PTR(-EBUSY); - goto out; - } - - if (!refcount_inc_not_zero(&psock->refcnt)) - psock = ERR_PTR(-EBUSY); - } -out: - rcu_read_unlock(); - return psock; -} - static inline struct sk_psock *sk_psock_get(struct sock *sk) { struct sk_psock *psock; diff --git a/include/net/tcp.h b/include/net/tcp.h index ad3abeaa703e..43fa07a36fa6 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -2195,19 +2195,22 @@ void tcp_update_ulp(struct sock *sk, struct proto *p, struct sk_msg; struct sk_psock; +#ifdef CONFIG_BPF_STREAM_PARSER +struct proto *tcp_bpf_get_proto(struct sock *sk, struct sk_psock *psock); +void tcp_bpf_clone(const struct sock *sk, struct sock *newsk); +#else +static inline void tcp_bpf_clone(const struct sock *sk, struct sock *newsk) +{ +} +#endif /* CONFIG_BPF_STREAM_PARSER */ + #ifdef CONFIG_NET_SOCK_MSG -int tcp_bpf_init(struct sock *sk); int tcp_bpf_sendmsg_redir(struct sock *sk, struct sk_msg *msg, u32 bytes, int flags); int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int flags, int *addr_len); int __tcp_bpf_recvmsg(struct sock *sk, struct sk_psock *psock, struct msghdr *msg, int len, int flags); -void tcp_bpf_clone(const struct sock *sk, struct sock *newsk); -#else -static inline void tcp_bpf_clone(const struct sock *sk, struct sock *newsk) -{ -} #endif /* CONFIG_NET_SOCK_MSG */ /* Call BPF_SOCK_OPS program that returns an int. If the return value diff --git a/net/core/sock_map.c b/net/core/sock_map.c index fafcbd22ecba..cb240d87e068 100644 --- a/net/core/sock_map.c +++ b/net/core/sock_map.c @@ -141,6 +141,51 @@ static void sock_map_unref(struct sock *sk, void *link_raw) } } +static int sock_map_init_proto(struct sock *sk) +{ + struct sk_psock *psock; + struct proto *prot; + + sock_owned_by_me(sk); + + rcu_read_lock(); + psock = sk_psock(sk); + if (unlikely(!psock)) { + rcu_read_unlock(); + return -EINVAL; + } + + prot = tcp_bpf_get_proto(sk, psock); + if (IS_ERR(prot)) { + rcu_read_unlock(); + return PTR_ERR(prot); + } + + sk_psock_update_proto(sk, psock, prot); + rcu_read_unlock(); + return 0; +} + +static struct sk_psock *sock_map_psock_get_checked(struct sock *sk) +{ + struct sk_psock *psock; + + rcu_read_lock(); + psock = sk_psock(sk); + if (psock) { + if (sk->sk_prot->recvmsg != tcp_bpf_recvmsg) { + psock = ERR_PTR(-EBUSY); + goto out; + } + + if (!refcount_inc_not_zero(&psock->refcnt)) + psock = ERR_PTR(-EBUSY); + } +out: + rcu_read_unlock(); + return psock; +} + static int sock_map_link(struct bpf_map *map, struct sk_psock_progs *progs, struct sock *sk) { @@ -172,7 +217,7 @@ static int sock_map_link(struct bpf_map *map, struct sk_psock_progs *progs, } } - psock = sk_psock_get_checked(sk); + psock = sock_map_psock_get_checked(sk); if (IS_ERR(psock)) { ret = PTR_ERR(psock); goto out_progs; @@ -196,7 +241,7 @@ static int sock_map_link(struct bpf_map *map, struct sk_psock_progs *progs, if (msg_parser) psock_set_prog(&psock->progs.msg_parser, msg_parser); - ret = tcp_bpf_init(sk); + ret = sock_map_init_proto(sk); if (ret < 0) goto out_drop; @@ -231,7 +276,7 @@ static int sock_map_link_no_progs(struct bpf_map *map, struct sock *sk) struct sk_psock *psock; int ret; - psock = sk_psock_get_checked(sk); + psock = sock_map_psock_get_checked(sk); if (IS_ERR(psock)) return PTR_ERR(psock); @@ -241,7 +286,7 @@ static int sock_map_link_no_progs(struct bpf_map *map, struct sock *sk) return -ENOMEM; } - ret = tcp_bpf_init(sk); + ret = sock_map_init_proto(sk); if (ret < 0) sk_psock_put(sk, psock); return ret; @@ -1120,7 +1165,7 @@ int sock_map_prog_update(struct bpf_map *map, struct bpf_prog *prog, return 0; } -void sk_psock_unlink(struct sock *sk, struct sk_psock_link *link) +static void sock_map_unlink(struct sock *sk, struct sk_psock_link *link) { switch (link->map->map_type) { case BPF_MAP_TYPE_SOCKMAP: @@ -1133,3 +1178,54 @@ void sk_psock_unlink(struct sock *sk, struct sk_psock_link *link) break; } } + +static void sock_map_remove_links(struct sock *sk, struct sk_psock *psock) +{ + struct sk_psock_link *link; + + while ((link = sk_psock_link_pop(psock))) { + sock_map_unlink(sk, link); + sk_psock_free_link(link); + } +} + +void sock_map_unhash(struct sock *sk) +{ + void (*saved_unhash)(struct sock *sk); + struct sk_psock *psock; + + rcu_read_lock(); + psock = sk_psock(sk); + if (unlikely(!psock)) { + rcu_read_unlock(); + if (sk->sk_prot->unhash) + sk->sk_prot->unhash(sk); + return; + } + + saved_unhash = psock->saved_unhash; + sock_map_remove_links(sk, psock); + rcu_read_unlock(); + saved_unhash(sk); +} + +void sock_map_close(struct sock *sk, long timeout) +{ + void (*saved_close)(struct sock *sk, long timeout); + struct sk_psock *psock; + + lock_sock(sk); + rcu_read_lock(); + psock = sk_psock(sk); + if (unlikely(!psock)) { + rcu_read_unlock(); + release_sock(sk); + return sk->sk_prot->close(sk, timeout); + } + + saved_close = psock->saved_close; + sock_map_remove_links(sk, psock); + rcu_read_unlock(); + release_sock(sk); + saved_close(sk, timeout); +} diff --git a/net/ipv4/tcp_bpf.c b/net/ipv4/tcp_bpf.c index ed8a8f3c9afe..fe7b4fbc31c1 100644 --- a/net/ipv4/tcp_bpf.c +++ b/net/ipv4/tcp_bpf.c @@ -528,57 +528,7 @@ out_err: return copied ? copied : err; } -static void tcp_bpf_remove(struct sock *sk, struct sk_psock *psock) -{ - struct sk_psock_link *link; - - while ((link = sk_psock_link_pop(psock))) { - sk_psock_unlink(sk, link); - sk_psock_free_link(link); - } -} - -static void tcp_bpf_unhash(struct sock *sk) -{ - void (*saved_unhash)(struct sock *sk); - struct sk_psock *psock; - - rcu_read_lock(); - psock = sk_psock(sk); - if (unlikely(!psock)) { - rcu_read_unlock(); - if (sk->sk_prot->unhash) - sk->sk_prot->unhash(sk); - return; - } - - saved_unhash = psock->saved_unhash; - tcp_bpf_remove(sk, psock); - rcu_read_unlock(); - saved_unhash(sk); -} - -static void tcp_bpf_close(struct sock *sk, long timeout) -{ - void (*saved_close)(struct sock *sk, long timeout); - struct sk_psock *psock; - - lock_sock(sk); - rcu_read_lock(); - psock = sk_psock(sk); - if (unlikely(!psock)) { - rcu_read_unlock(); - release_sock(sk); - return sk->sk_prot->close(sk, timeout); - } - - saved_close = psock->saved_close; - tcp_bpf_remove(sk, psock); - rcu_read_unlock(); - release_sock(sk); - saved_close(sk, timeout); -} - +#ifdef CONFIG_BPF_STREAM_PARSER enum { TCP_BPF_IPV4, TCP_BPF_IPV6, @@ -599,8 +549,8 @@ static void tcp_bpf_rebuild_protos(struct proto prot[TCP_BPF_NUM_CFGS], struct proto *base) { prot[TCP_BPF_BASE] = *base; - prot[TCP_BPF_BASE].unhash = tcp_bpf_unhash; - prot[TCP_BPF_BASE].close = tcp_bpf_close; + prot[TCP_BPF_BASE].unhash = sock_map_unhash; + prot[TCP_BPF_BASE].close = sock_map_close; prot[TCP_BPF_BASE].recvmsg = tcp_bpf_recvmsg; prot[TCP_BPF_BASE].stream_memory_read = tcp_bpf_stream_read; @@ -640,7 +590,7 @@ static int tcp_bpf_assert_proto_ops(struct proto *ops) ops->sendpage == tcp_sendpage ? 0 : -ENOTSUPP; } -static struct proto *tcp_bpf_get_proto(struct sock *sk, struct sk_psock *psock) +struct proto *tcp_bpf_get_proto(struct sock *sk, struct sk_psock *psock) { int family = sk->sk_family == AF_INET6 ? TCP_BPF_IPV6 : TCP_BPF_IPV4; int config = psock->progs.msg_parser ? TCP_BPF_TX : TCP_BPF_BASE; @@ -657,31 +607,6 @@ static struct proto *tcp_bpf_get_proto(struct sock *sk, struct sk_psock *psock) return &tcp_bpf_prots[family][config]; } -int tcp_bpf_init(struct sock *sk) -{ - struct sk_psock *psock; - struct proto *prot; - - sock_owned_by_me(sk); - - rcu_read_lock(); - psock = sk_psock(sk); - if (unlikely(!psock)) { - rcu_read_unlock(); - return -EINVAL; - } - - prot = tcp_bpf_get_proto(sk, psock); - if (IS_ERR(prot)) { - rcu_read_unlock(); - return PTR_ERR(prot); - } - - sk_psock_update_proto(sk, psock, prot); - rcu_read_unlock(); - return 0; -} - /* If a child got cloned from a listening socket that had tcp_bpf * protocol callbacks installed, we need to restore the callbacks to * the default ones because the child does not inherit the psock state @@ -695,3 +620,4 @@ void tcp_bpf_clone(const struct sock *sk, struct sock *newsk) if (prot == &tcp_bpf_prots[family][TCP_BPF_BASE]) newsk->sk_prot = sk->sk_prot_creator; } +#endif /* CONFIG_BPF_STREAM_PARSER */ -- cgit v1.2.3 From edc6741cc66059532ba621928e3f1b02a53a2f39 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 9 Mar 2020 11:12:38 +0000 Subject: bpf: Add sockmap hooks for UDP sockets Add basic psock hooks for UDP sockets. This allows adding and removing sockets, as well as automatic removal on unhash and close. Signed-off-by: Lorenz Bauer Signed-off-by: Jakub Sitnicki Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309111243.6982-8-lmb@cloudflare.com --- MAINTAINERS | 1 + include/net/udp.h | 5 +++++ net/ipv4/Makefile | 1 + net/ipv4/udp_bpf.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 net/ipv4/udp_bpf.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index c23884e084be..14554bde1c06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9370,6 +9370,7 @@ F: include/linux/skmsg.h F: net/core/skmsg.c F: net/core/sock_map.c F: net/ipv4/tcp_bpf.c +F: net/ipv4/udp_bpf.c LANTIQ / INTEL Ethernet drivers M: Hauke Mehrtens diff --git a/include/net/udp.h b/include/net/udp.h index e55d5f765807..a8fa6c0c6ded 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -503,4 +503,9 @@ static inline struct sk_buff *udp_rcv_segment(struct sock *sk, return segs; } +#ifdef CONFIG_BPF_STREAM_PARSER +struct sk_psock; +struct proto *udp_bpf_get_proto(struct sock *sk, struct sk_psock *psock); +#endif /* BPF_STREAM_PARSER */ + #endif /* _UDP_H */ diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile index 9d97bace13c8..9e1a186a3671 100644 --- a/net/ipv4/Makefile +++ b/net/ipv4/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_TCP_CONG_LP) += tcp_lp.o obj-$(CONFIG_TCP_CONG_YEAH) += tcp_yeah.o obj-$(CONFIG_TCP_CONG_ILLINOIS) += tcp_illinois.o obj-$(CONFIG_NET_SOCK_MSG) += tcp_bpf.o +obj-$(CONFIG_BPF_STREAM_PARSER) += udp_bpf.o obj-$(CONFIG_NETLABEL) += cipso_ipv4.o obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \ diff --git a/net/ipv4/udp_bpf.c b/net/ipv4/udp_bpf.c new file mode 100644 index 000000000000..eddd973e6575 --- /dev/null +++ b/net/ipv4/udp_bpf.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2020 Cloudflare Ltd https://cloudflare.com */ + +#include +#include +#include + +enum { + UDP_BPF_IPV4, + UDP_BPF_IPV6, + UDP_BPF_NUM_PROTS, +}; + +static struct proto *udpv6_prot_saved __read_mostly; +static DEFINE_SPINLOCK(udpv6_prot_lock); +static struct proto udp_bpf_prots[UDP_BPF_NUM_PROTS]; + +static void udp_bpf_rebuild_protos(struct proto *prot, const struct proto *base) +{ + *prot = *base; + prot->unhash = sock_map_unhash; + prot->close = sock_map_close; +} + +static void udp_bpf_check_v6_needs_rebuild(struct sock *sk, struct proto *ops) +{ + if (sk->sk_family == AF_INET6 && + unlikely(ops != smp_load_acquire(&udpv6_prot_saved))) { + spin_lock_bh(&udpv6_prot_lock); + if (likely(ops != udpv6_prot_saved)) { + udp_bpf_rebuild_protos(&udp_bpf_prots[UDP_BPF_IPV6], ops); + smp_store_release(&udpv6_prot_saved, ops); + } + spin_unlock_bh(&udpv6_prot_lock); + } +} + +static int __init udp_bpf_v4_build_proto(void) +{ + udp_bpf_rebuild_protos(&udp_bpf_prots[UDP_BPF_IPV4], &udp_prot); + return 0; +} +core_initcall(udp_bpf_v4_build_proto); + +struct proto *udp_bpf_get_proto(struct sock *sk, struct sk_psock *psock) +{ + int family = sk->sk_family == AF_INET ? UDP_BPF_IPV4 : UDP_BPF_IPV6; + + if (!psock->sk_proto) + udp_bpf_check_v6_needs_rebuild(sk, READ_ONCE(sk->sk_prot)); + + return &udp_bpf_prots[family]; +} -- cgit v1.2.3 From 98394efb48f51560fda86ed12f5d4e391629755f Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Mon, 24 Feb 2020 10:20:02 +0530 Subject: dt-bindings: clock: Add SM8250 GCC clock bindings Add device tree bindings for global clock controller on SM8250 SoCs. Signed-off-by: Taniya Das Signed-off-by: Venkata Narendra Kumar Gutta Signed-off-by: Vinod Koul Link: https://lkml.kernel.org/r/20200224045003.3783838-5-vkoul@kernel.org Reviewed-by: Rob Herring Reviewed-by: Bryan O'Donoghue Tested-by: Bryan O'Donoghue Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,gcc-sm8250.yaml | 72 ++++++ include/dt-bindings/clock/qcom,gcc-sm8250.h | 271 +++++++++++++++++++++ 2 files changed, 343 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml create mode 100644 include/dt-bindings/clock/qcom,gcc-sm8250.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml new file mode 100644 index 000000000000..2c40a8aa9815 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sm8250.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/clock/qcom,gcc-sm8250.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Global Clock & Reset Controller Binding for SM8250 + +maintainers: + - Stephen Boyd + - Taniya Das + +description: | + Qualcomm global clock control module which supports the clocks, resets and + power domains on SM8250. + + See also: + - dt-bindings/clock/qcom,gcc-sm8250.h + +properties: + compatible: + const: qcom,gcc-sm8250 + + clocks: + items: + - description: Board XO source + - description: Sleep clock source + + clock-names: + items: + - const: bi_tcxo + - const: sleep_clk + + '#clock-cells': + const: 1 + + '#reset-cells': + const: 1 + + '#power-domain-cells': + const: 1 + + reg: + maxItems: 1 + + protected-clocks: + description: + Protected clock specifier list as per common clock binding. + +required: + - compatible + - clocks + - clock-names + - reg + - '#clock-cells' + - '#reset-cells' + - '#power-domain-cells' + +examples: + - | + #include + clock-controller@100000 { + compatible = "qcom,gcc-sm8250"; + reg = <0 0x00100000 0 0x1f0000>; + clocks = <&rpmhcc RPMH_CXO_CLK>, + <&sleep_clk>; + clock-names = "bi_tcxo", "sleep_clk"; + #clock-cells = <1>; + #reset-cells = <1>; + #power-domain-cells = <1>; + }; +... diff --git a/include/dt-bindings/clock/qcom,gcc-sm8250.h b/include/dt-bindings/clock/qcom,gcc-sm8250.h new file mode 100644 index 000000000000..7b7abe327e37 --- /dev/null +++ b/include/dt-bindings/clock/qcom,gcc-sm8250.h @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _DT_BINDINGS_CLK_QCOM_GCC_SM8250_H +#define _DT_BINDINGS_CLK_QCOM_GCC_SM8250_H + +/* GCC clocks */ +#define GPLL0 0 +#define GPLL0_OUT_EVEN 1 +#define GPLL4 2 +#define GPLL9 3 +#define GCC_AGGRE_NOC_PCIE_TBU_CLK 4 +#define GCC_AGGRE_UFS_CARD_AXI_CLK 5 +#define GCC_AGGRE_UFS_PHY_AXI_CLK 6 +#define GCC_AGGRE_USB3_PRIM_AXI_CLK 7 +#define GCC_AGGRE_USB3_SEC_AXI_CLK 8 +#define GCC_BOOT_ROM_AHB_CLK 9 +#define GCC_CAMERA_AHB_CLK 10 +#define GCC_CAMERA_HF_AXI_CLK 11 +#define GCC_CAMERA_SF_AXI_CLK 12 +#define GCC_CAMERA_XO_CLK 13 +#define GCC_CFG_NOC_USB3_PRIM_AXI_CLK 14 +#define GCC_CFG_NOC_USB3_SEC_AXI_CLK 15 +#define GCC_CPUSS_AHB_CLK 16 +#define GCC_CPUSS_AHB_CLK_SRC 17 +#define GCC_CPUSS_AHB_POSTDIV_CLK_SRC 18 +#define GCC_CPUSS_DVM_BUS_CLK 19 +#define GCC_CPUSS_RBCPR_CLK 20 +#define GCC_DDRSS_GPU_AXI_CLK 21 +#define GCC_DDRSS_PCIE_SF_TBU_CLK 22 +#define GCC_DISP_AHB_CLK 23 +#define GCC_DISP_HF_AXI_CLK 24 +#define GCC_DISP_SF_AXI_CLK 25 +#define GCC_DISP_XO_CLK 26 +#define GCC_GP1_CLK 27 +#define GCC_GP1_CLK_SRC 28 +#define GCC_GP2_CLK 29 +#define GCC_GP2_CLK_SRC 30 +#define GCC_GP3_CLK 31 +#define GCC_GP3_CLK_SRC 32 +#define GCC_GPU_CFG_AHB_CLK 33 +#define GCC_GPU_GPLL0_CLK_SRC 34 +#define GCC_GPU_GPLL0_DIV_CLK_SRC 35 +#define GCC_GPU_IREF_EN 36 +#define GCC_GPU_MEMNOC_GFX_CLK 37 +#define GCC_GPU_SNOC_DVM_GFX_CLK 38 +#define GCC_NPU_AXI_CLK 39 +#define GCC_NPU_BWMON_AXI_CLK 40 +#define GCC_NPU_BWMON_CFG_AHB_CLK 41 +#define GCC_NPU_CFG_AHB_CLK 42 +#define GCC_NPU_DMA_CLK 43 +#define GCC_NPU_GPLL0_CLK_SRC 44 +#define GCC_NPU_GPLL0_DIV_CLK_SRC 45 +#define GCC_PCIE0_PHY_REFGEN_CLK 46 +#define GCC_PCIE1_PHY_REFGEN_CLK 47 +#define GCC_PCIE2_PHY_REFGEN_CLK 48 +#define GCC_PCIE_0_AUX_CLK 49 +#define GCC_PCIE_0_AUX_CLK_SRC 50 +#define GCC_PCIE_0_CFG_AHB_CLK 51 +#define GCC_PCIE_0_MSTR_AXI_CLK 52 +#define GCC_PCIE_0_PIPE_CLK 53 +#define GCC_PCIE_0_SLV_AXI_CLK 54 +#define GCC_PCIE_0_SLV_Q2A_AXI_CLK 55 +#define GCC_PCIE_1_AUX_CLK 56 +#define GCC_PCIE_1_AUX_CLK_SRC 57 +#define GCC_PCIE_1_CFG_AHB_CLK 58 +#define GCC_PCIE_1_MSTR_AXI_CLK 59 +#define GCC_PCIE_1_PIPE_CLK 60 +#define GCC_PCIE_1_SLV_AXI_CLK 61 +#define GCC_PCIE_1_SLV_Q2A_AXI_CLK 62 +#define GCC_PCIE_2_AUX_CLK 63 +#define GCC_PCIE_2_AUX_CLK_SRC 64 +#define GCC_PCIE_2_CFG_AHB_CLK 65 +#define GCC_PCIE_2_MSTR_AXI_CLK 66 +#define GCC_PCIE_2_PIPE_CLK 67 +#define GCC_PCIE_2_SLV_AXI_CLK 68 +#define GCC_PCIE_2_SLV_Q2A_AXI_CLK 69 +#define GCC_PCIE_MDM_CLKREF_EN 70 +#define GCC_PCIE_PHY_AUX_CLK 71 +#define GCC_PCIE_PHY_REFGEN_CLK_SRC 72 +#define GCC_PCIE_WIFI_CLKREF_EN 73 +#define GCC_PCIE_WIGIG_CLKREF_EN 74 +#define GCC_PDM2_CLK 75 +#define GCC_PDM2_CLK_SRC 76 +#define GCC_PDM_AHB_CLK 77 +#define GCC_PDM_XO4_CLK 78 +#define GCC_PRNG_AHB_CLK 79 +#define GCC_QMIP_CAMERA_NRT_AHB_CLK 80 +#define GCC_QMIP_CAMERA_RT_AHB_CLK 81 +#define GCC_QMIP_DISP_AHB_CLK 82 +#define GCC_QMIP_VIDEO_CVP_AHB_CLK 83 +#define GCC_QMIP_VIDEO_VCODEC_AHB_CLK 84 +#define GCC_QUPV3_WRAP0_CORE_2X_CLK 85 +#define GCC_QUPV3_WRAP0_CORE_CLK 86 +#define GCC_QUPV3_WRAP0_S0_CLK 87 +#define GCC_QUPV3_WRAP0_S0_CLK_SRC 88 +#define GCC_QUPV3_WRAP0_S1_CLK 89 +#define GCC_QUPV3_WRAP0_S1_CLK_SRC 90 +#define GCC_QUPV3_WRAP0_S2_CLK 91 +#define GCC_QUPV3_WRAP0_S2_CLK_SRC 92 +#define GCC_QUPV3_WRAP0_S3_CLK 93 +#define GCC_QUPV3_WRAP0_S3_CLK_SRC 94 +#define GCC_QUPV3_WRAP0_S4_CLK 95 +#define GCC_QUPV3_WRAP0_S4_CLK_SRC 96 +#define GCC_QUPV3_WRAP0_S5_CLK 97 +#define GCC_QUPV3_WRAP0_S5_CLK_SRC 98 +#define GCC_QUPV3_WRAP0_S6_CLK 99 +#define GCC_QUPV3_WRAP0_S6_CLK_SRC 100 +#define GCC_QUPV3_WRAP0_S7_CLK 101 +#define GCC_QUPV3_WRAP0_S7_CLK_SRC 102 +#define GCC_QUPV3_WRAP1_CORE_2X_CLK 103 +#define GCC_QUPV3_WRAP1_CORE_CLK 104 +#define GCC_QUPV3_WRAP1_S0_CLK 105 +#define GCC_QUPV3_WRAP1_S0_CLK_SRC 106 +#define GCC_QUPV3_WRAP1_S1_CLK 107 +#define GCC_QUPV3_WRAP1_S1_CLK_SRC 108 +#define GCC_QUPV3_WRAP1_S2_CLK 109 +#define GCC_QUPV3_WRAP1_S2_CLK_SRC 110 +#define GCC_QUPV3_WRAP1_S3_CLK 111 +#define GCC_QUPV3_WRAP1_S3_CLK_SRC 112 +#define GCC_QUPV3_WRAP1_S4_CLK 113 +#define GCC_QUPV3_WRAP1_S4_CLK_SRC 114 +#define GCC_QUPV3_WRAP1_S5_CLK 115 +#define GCC_QUPV3_WRAP1_S5_CLK_SRC 116 +#define GCC_QUPV3_WRAP2_CORE_2X_CLK 117 +#define GCC_QUPV3_WRAP2_CORE_CLK 118 +#define GCC_QUPV3_WRAP2_S0_CLK 119 +#define GCC_QUPV3_WRAP2_S0_CLK_SRC 120 +#define GCC_QUPV3_WRAP2_S1_CLK 121 +#define GCC_QUPV3_WRAP2_S1_CLK_SRC 122 +#define GCC_QUPV3_WRAP2_S2_CLK 123 +#define GCC_QUPV3_WRAP2_S2_CLK_SRC 124 +#define GCC_QUPV3_WRAP2_S3_CLK 125 +#define GCC_QUPV3_WRAP2_S3_CLK_SRC 126 +#define GCC_QUPV3_WRAP2_S4_CLK 127 +#define GCC_QUPV3_WRAP2_S4_CLK_SRC 128 +#define GCC_QUPV3_WRAP2_S5_CLK 129 +#define GCC_QUPV3_WRAP2_S5_CLK_SRC 130 +#define GCC_QUPV3_WRAP_0_M_AHB_CLK 131 +#define GCC_QUPV3_WRAP_0_S_AHB_CLK 132 +#define GCC_QUPV3_WRAP_1_M_AHB_CLK 133 +#define GCC_QUPV3_WRAP_1_S_AHB_CLK 134 +#define GCC_QUPV3_WRAP_2_M_AHB_CLK 135 +#define GCC_QUPV3_WRAP_2_S_AHB_CLK 136 +#define GCC_SDCC2_AHB_CLK 137 +#define GCC_SDCC2_APPS_CLK 138 +#define GCC_SDCC2_APPS_CLK_SRC 139 +#define GCC_SDCC4_AHB_CLK 140 +#define GCC_SDCC4_APPS_CLK 141 +#define GCC_SDCC4_APPS_CLK_SRC 142 +#define GCC_SYS_NOC_CPUSS_AHB_CLK 143 +#define GCC_TSIF_AHB_CLK 144 +#define GCC_TSIF_INACTIVITY_TIMERS_CLK 145 +#define GCC_TSIF_REF_CLK 146 +#define GCC_TSIF_REF_CLK_SRC 147 +#define GCC_UFS_1X_CLKREF_EN 148 +#define GCC_UFS_CARD_AHB_CLK 149 +#define GCC_UFS_CARD_AXI_CLK 150 +#define GCC_UFS_CARD_AXI_CLK_SRC 151 +#define GCC_UFS_CARD_ICE_CORE_CLK 152 +#define GCC_UFS_CARD_ICE_CORE_CLK_SRC 153 +#define GCC_UFS_CARD_PHY_AUX_CLK 154 +#define GCC_UFS_CARD_PHY_AUX_CLK_SRC 155 +#define GCC_UFS_CARD_RX_SYMBOL_0_CLK 156 +#define GCC_UFS_CARD_RX_SYMBOL_1_CLK 157 +#define GCC_UFS_CARD_TX_SYMBOL_0_CLK 158 +#define GCC_UFS_CARD_UNIPRO_CORE_CLK 159 +#define GCC_UFS_CARD_UNIPRO_CORE_CLK_SRC 160 +#define GCC_UFS_PHY_AHB_CLK 161 +#define GCC_UFS_PHY_AXI_CLK 162 +#define GCC_UFS_PHY_AXI_CLK_SRC 163 +#define GCC_UFS_PHY_ICE_CORE_CLK 164 +#define GCC_UFS_PHY_ICE_CORE_CLK_SRC 165 +#define GCC_UFS_PHY_PHY_AUX_CLK 166 +#define GCC_UFS_PHY_PHY_AUX_CLK_SRC 167 +#define GCC_UFS_PHY_RX_SYMBOL_0_CLK 168 +#define GCC_UFS_PHY_RX_SYMBOL_1_CLK 169 +#define GCC_UFS_PHY_TX_SYMBOL_0_CLK 170 +#define GCC_UFS_PHY_UNIPRO_CORE_CLK 171 +#define GCC_UFS_PHY_UNIPRO_CORE_CLK_SRC 172 +#define GCC_USB30_PRIM_MASTER_CLK 173 +#define GCC_USB30_PRIM_MASTER_CLK_SRC 174 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK 175 +#define GCC_USB30_PRIM_MOCK_UTMI_CLK_SRC 176 +#define GCC_USB30_PRIM_MOCK_UTMI_POSTDIV_CLK_SRC 177 +#define GCC_USB30_PRIM_SLEEP_CLK 178 +#define GCC_USB30_SEC_MASTER_CLK 179 +#define GCC_USB30_SEC_MASTER_CLK_SRC 180 +#define GCC_USB30_SEC_MOCK_UTMI_CLK 181 +#define GCC_USB30_SEC_MOCK_UTMI_CLK_SRC 182 +#define GCC_USB30_SEC_MOCK_UTMI_POSTDIV_CLK_SRC 183 +#define GCC_USB30_SEC_SLEEP_CLK 184 +#define GCC_USB3_PRIM_PHY_AUX_CLK 185 +#define GCC_USB3_PRIM_PHY_AUX_CLK_SRC 186 +#define GCC_USB3_PRIM_PHY_COM_AUX_CLK 187 +#define GCC_USB3_PRIM_PHY_PIPE_CLK 188 +#define GCC_USB3_PRIM_PHY_PIPE_CLK_SRC 189 +#define GCC_USB3_SEC_CLKREF_EN 190 +#define GCC_USB3_SEC_PHY_AUX_CLK 191 +#define GCC_USB3_SEC_PHY_AUX_CLK_SRC 192 +#define GCC_USB3_SEC_PHY_COM_AUX_CLK 193 +#define GCC_USB3_SEC_PHY_PIPE_CLK 194 +#define GCC_USB3_SEC_PHY_PIPE_CLK_SRC 195 +#define GCC_VIDEO_AHB_CLK 196 +#define GCC_VIDEO_AXI0_CLK 197 +#define GCC_VIDEO_AXI1_CLK 198 +#define GCC_VIDEO_XO_CLK 199 + +/* GCC resets */ +#define GCC_GPU_BCR 0 +#define GCC_MMSS_BCR 1 +#define GCC_NPU_BWMON_BCR 2 +#define GCC_NPU_BCR 3 +#define GCC_PCIE_0_BCR 4 +#define GCC_PCIE_0_LINK_DOWN_BCR 5 +#define GCC_PCIE_0_NOCSR_COM_PHY_BCR 6 +#define GCC_PCIE_0_PHY_BCR 7 +#define GCC_PCIE_0_PHY_NOCSR_COM_PHY_BCR 8 +#define GCC_PCIE_1_BCR 9 +#define GCC_PCIE_1_LINK_DOWN_BCR 10 +#define GCC_PCIE_1_NOCSR_COM_PHY_BCR 11 +#define GCC_PCIE_1_PHY_BCR 12 +#define GCC_PCIE_1_PHY_NOCSR_COM_PHY_BCR 13 +#define GCC_PCIE_2_BCR 14 +#define GCC_PCIE_2_LINK_DOWN_BCR 15 +#define GCC_PCIE_2_NOCSR_COM_PHY_BCR 16 +#define GCC_PCIE_2_PHY_BCR 17 +#define GCC_PCIE_2_PHY_NOCSR_COM_PHY_BCR 18 +#define GCC_PCIE_PHY_BCR 19 +#define GCC_PCIE_PHY_CFG_AHB_BCR 20 +#define GCC_PCIE_PHY_COM_BCR 21 +#define GCC_PDM_BCR 22 +#define GCC_PRNG_BCR 23 +#define GCC_QUPV3_WRAPPER_0_BCR 24 +#define GCC_QUPV3_WRAPPER_1_BCR 25 +#define GCC_QUPV3_WRAPPER_2_BCR 26 +#define GCC_QUSB2PHY_PRIM_BCR 27 +#define GCC_QUSB2PHY_SEC_BCR 28 +#define GCC_SDCC2_BCR 29 +#define GCC_SDCC4_BCR 30 +#define GCC_TSIF_BCR 31 +#define GCC_UFS_CARD_BCR 32 +#define GCC_UFS_PHY_BCR 33 +#define GCC_USB30_PRIM_BCR 34 +#define GCC_USB30_SEC_BCR 35 +#define GCC_USB3_DP_PHY_PRIM_BCR 36 +#define GCC_USB3_DP_PHY_SEC_BCR 37 +#define GCC_USB3_PHY_PRIM_BCR 38 +#define GCC_USB3_PHY_SEC_BCR 39 +#define GCC_USB3PHY_PHY_PRIM_BCR 40 +#define GCC_USB3PHY_PHY_SEC_BCR 41 +#define GCC_USB_PHY_CFG_AHB2PHY_BCR 42 +#define GCC_VIDEO_AXI0_CLK_ARES 43 +#define GCC_VIDEO_AXI1_CLK_ARES 44 + +/* GCC power domains */ +#define PCIE_0_GDSC 0 +#define PCIE_1_GDSC 1 +#define PCIE_2_GDSC 2 +#define UFS_CARD_GDSC 3 +#define UFS_PHY_GDSC 4 +#define USB30_PRIM_GDSC 5 +#define USB30_SEC_GDSC 6 +#define HLOS1_VOTE_MMNOC_MMU_TBU_HF0_GDSC 7 +#define HLOS1_VOTE_MMNOC_MMU_TBU_HF1_GDSC 8 +#define HLOS1_VOTE_MMNOC_MMU_TBU_SF0_GDSC 9 +#define HLOS1_VOTE_MMNOC_MMU_TBU_SF1_GDSC 10 + +#endif -- cgit v1.2.3 From e08ab0b377a1489760533424437c5f4be7f484a4 Mon Sep 17 00:00:00 2001 From: Yousuk Seung Date: Mon, 9 Mar 2020 13:16:40 -0700 Subject: tcp: add bytes not sent to SCM_TIMESTAMPING_OPT_STATS Add TCP_NLA_BYTES_NOTSENT to SCM_TIMESTAMPING_OPT_STATS that reports bytes in the write queue but not sent. This is the same metric as what is exported with tcp_info.tcpi_notsent_bytes. Signed-off-by: Yousuk Seung Signed-off-by: Eric Dumazet Acked-by: Soheil Hassas Yeganeh Acked-by: Yuchung Cheng Acked-by: Neal Cardwell Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 1a7fc856e237..f2acb2566333 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -312,6 +312,7 @@ enum { TCP_NLA_REORD_SEEN, /* reordering events seen */ TCP_NLA_SRTT, /* smoothed RTT in usecs */ TCP_NLA_TIMEOUT_REHASH, /* Timeout-triggered rehash attempts */ + TCP_NLA_BYTES_NOTSENT, /* Bytes in write queue not yet sent */ }; /* for TCP_MD5SIG socket option */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 48aa457a9516..b7134f76f840 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3344,6 +3344,7 @@ static size_t tcp_opt_stats_get_size(void) nla_total_size(sizeof(u32)) + /* TCP_NLA_REORD_SEEN */ nla_total_size(sizeof(u32)) + /* TCP_NLA_SRTT */ nla_total_size(sizeof(u16)) + /* TCP_NLA_TIMEOUT_REHASH */ + nla_total_size(sizeof(u32)) + /* TCP_NLA_BYTES_NOTSENT */ 0; } @@ -3399,6 +3400,8 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) nla_put_u32(stats, TCP_NLA_REORD_SEEN, tp->reord_seen); nla_put_u32(stats, TCP_NLA_SRTT, tp->srtt_us >> 3); nla_put_u16(stats, TCP_NLA_TIMEOUT_REHASH, tp->timeout_rehash); + nla_put_u32(stats, TCP_NLA_BYTES_NOTSENT, + max_t(int, 0, tp->write_seq - tp->snd_nxt)); return stats; } -- cgit v1.2.3 From a8015ded89ad740d21355470d41879c5bd82aab7 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 10 Mar 2020 03:28:18 +0200 Subject: net: mscc: ocelot: properly account for VLAN header length when setting MRU What the driver writes into MAC_MAXLEN_CFG does not actually represent VLAN_ETH_FRAME_LEN but instead ETH_FRAME_LEN + ETH_FCS_LEN. Yes they are numerically equal, but the difference is important, as the switch treats VLAN-tagged traffic specially and knows to increase the maximum accepted frame size automatically. So it is always wrong to account for VLAN in the MAC_MAXLEN_CFG register. Unconditionally increase the maximum allowed frame size for double-tagged traffic. Accounting for the additional length does not mean that the other VLAN membership checks aren't performed, so there's no harm done. Also, stop abusing the MTU name for configuring the MRU. There is no support for configuring the MRU on an interface at the moment. Fixes: a556c76adc05 ("net: mscc: Add initial Ocelot switch support") Fixes: fa914e9c4d94 ("net: mscc: ocelot: create a helper for changing the port MTU") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/mscc/ocelot.c | 28 +++++++++++++++++----------- include/soc/mscc/ocelot_dev.h | 2 +- 2 files changed, 18 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 86d543ab1ab9..d3b7373c5961 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2176,24 +2176,29 @@ static int ocelot_init_timestamp(struct ocelot *ocelot) return 0; } -static void ocelot_port_set_mtu(struct ocelot *ocelot, int port, size_t mtu) +/* Configure the maximum SDU (L2 payload) on RX to the value specified in @sdu. + * The length of VLAN tags is accounted for automatically via DEV_MAC_TAGS_CFG. + */ +static void ocelot_port_set_maxlen(struct ocelot *ocelot, int port, size_t sdu) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + int maxlen = sdu + ETH_HLEN + ETH_FCS_LEN; int atop_wm; - ocelot_port_writel(ocelot_port, mtu, DEV_MAC_MAXLEN_CFG); + ocelot_port_writel(ocelot_port, maxlen, DEV_MAC_MAXLEN_CFG); /* Set Pause WM hysteresis - * 152 = 6 * mtu / OCELOT_BUFFER_CELL_SZ - * 101 = 4 * mtu / OCELOT_BUFFER_CELL_SZ + * 152 = 6 * maxlen / OCELOT_BUFFER_CELL_SZ + * 101 = 4 * maxlen / OCELOT_BUFFER_CELL_SZ */ ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA | SYS_PAUSE_CFG_PAUSE_STOP(101) | SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, port); /* Tail dropping watermark */ - atop_wm = (ocelot->shared_queue_sz - 9 * mtu) / OCELOT_BUFFER_CELL_SZ; - ocelot_write_rix(ocelot, ocelot_wm_enc(9 * mtu), + atop_wm = (ocelot->shared_queue_sz - 9 * maxlen) / + OCELOT_BUFFER_CELL_SZ; + ocelot_write_rix(ocelot, ocelot_wm_enc(9 * maxlen), SYS_ATOP, port); ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG); } @@ -2222,9 +2227,10 @@ void ocelot_init_port(struct ocelot *ocelot, int port) DEV_MAC_HDX_CFG); /* Set Max Length and maximum tags allowed */ - ocelot_port_set_mtu(ocelot, port, VLAN_ETH_FRAME_LEN); + ocelot_port_set_maxlen(ocelot, port, ETH_DATA_LEN); ocelot_port_writel(ocelot_port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) | DEV_MAC_TAGS_CFG_VLAN_AWR_ENA | + DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA | DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, DEV_MAC_TAGS_CFG); @@ -2310,18 +2316,18 @@ void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, * Only one port can be an NPI at the same time. */ if (cpu < ocelot->num_phys_ports) { - int mtu = VLAN_ETH_FRAME_LEN + OCELOT_TAG_LEN; + int sdu = ETH_DATA_LEN + OCELOT_TAG_LEN; ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | QSYS_EXT_CPU_CFG_EXT_CPU_PORT(cpu), QSYS_EXT_CPU_CFG); if (injection == OCELOT_TAG_PREFIX_SHORT) - mtu += OCELOT_SHORT_PREFIX_LEN; + sdu += OCELOT_SHORT_PREFIX_LEN; else if (injection == OCELOT_TAG_PREFIX_LONG) - mtu += OCELOT_LONG_PREFIX_LEN; + sdu += OCELOT_LONG_PREFIX_LEN; - ocelot_port_set_mtu(ocelot, cpu, mtu); + ocelot_port_set_maxlen(ocelot, cpu, sdu); } /* CPU port Injection/Extraction configuration */ diff --git a/include/soc/mscc/ocelot_dev.h b/include/soc/mscc/ocelot_dev.h index 0a50d53bbd3f..7c08437061fc 100644 --- a/include/soc/mscc/ocelot_dev.h +++ b/include/soc/mscc/ocelot_dev.h @@ -74,7 +74,7 @@ #define DEV_MAC_TAGS_CFG_TAG_ID_M GENMASK(31, 16) #define DEV_MAC_TAGS_CFG_TAG_ID_X(x) (((x) & GENMASK(31, 16)) >> 16) #define DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA BIT(2) -#define DEV_MAC_TAGS_CFG_PB_ENA BIT(1) +#define DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA BIT(1) #define DEV_MAC_TAGS_CFG_VLAN_AWR_ENA BIT(0) #define DEV_MAC_ADV_CHK_CFG 0x2c -- cgit v1.2.3 From fcb26bd2b6cab573f06e5855638368cf88e99c2b Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Mon, 9 Mar 2020 09:36:26 +0100 Subject: net: phy: Add Synopsys DesignWare XPCS MDIO module Synopsys DesignWare XPCS is an MMD that can manage link status, auto-negotiation, link training, ... In this commit we add basic support for XPCS using USXGMII interface and Clause 73 Auto-negotiation. This is highly tied with PHYLINK and can't be used without it. A given ethernet driver can use the provided callbacks to add the support for XPCS. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- MAINTAINERS | 7 + drivers/net/phy/Kconfig | 6 + drivers/net/phy/Makefile | 1 + drivers/net/phy/mdio-xpcs.c | 612 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mdio-xpcs.h | 41 +++ 5 files changed, 667 insertions(+) create mode 100644 drivers/net/phy/mdio-xpcs.c create mode 100644 include/linux/mdio-xpcs.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index e8666f980a21..6918c3f0ff1c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16117,6 +16117,13 @@ L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/synopsys/ +SYNOPSYS DESIGNWARE ETHERNET XPCS DRIVER +M: Jose Abreu +L: netdev@vger.kernel.org +S: Supported +F: drivers/net/phy/mdio-xpcs.c +F: include/linux/mdio-xpcs.h + SYNOPSYS DESIGNWARE I2C DRIVER M: Jarkko Nikula R: Andy Shevchenko diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index d6f197e06134..cc7f1df855da 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -214,6 +214,12 @@ config MDIO_XGENE This module provides a driver for the MDIO busses found in the APM X-Gene SoC's. +config MDIO_XPCS + tristate "Synopsys DesignWare XPCS controller" + help + This module provides helper functions for Synopsys DesignWare XPCS + controllers. + endif endif diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index d9b3c0fec8e3..26f8039f300f 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_THUNDER) += mdio-thunder.o obj-$(CONFIG_MDIO_XGENE) += mdio-xgene.o +obj-$(CONFIG_MDIO_XPCS) += mdio-xpcs.o obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o diff --git a/drivers/net/phy/mdio-xpcs.c b/drivers/net/phy/mdio-xpcs.c new file mode 100644 index 000000000000..973f588146f7 --- /dev/null +++ b/drivers/net/phy/mdio-xpcs.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates. + * Synopsys DesignWare XPCS helpers + * + * Author: Jose Abreu + */ + +#include +#include +#include +#include +#include + +#define SYNOPSYS_XPCS_USXGMII_ID 0x7996ced0 +#define SYNOPSYS_XPCS_10GKR_ID 0x7996ced0 +#define SYNOPSYS_XPCS_MASK 0xffffffff + +/* Vendor regs access */ +#define DW_VENDOR BIT(15) + +/* VR_XS_PCS */ +#define DW_USXGMII_RST BIT(10) +#define DW_USXGMII_EN BIT(9) +#define DW_VR_XS_PCS_DIG_STS 0x0010 +#define DW_RXFIFO_ERR GENMASK(6, 5) + +/* SR_MII */ +#define DW_USXGMII_FULL BIT(8) +#define DW_USXGMII_SS_MASK (BIT(13) | BIT(6) | BIT(5)) +#define DW_USXGMII_10000 (BIT(13) | BIT(6)) +#define DW_USXGMII_5000 (BIT(13) | BIT(5)) +#define DW_USXGMII_2500 (BIT(5)) +#define DW_USXGMII_1000 (BIT(6)) +#define DW_USXGMII_100 (BIT(13)) +#define DW_USXGMII_10 (0) + +/* SR_AN */ +#define DW_SR_AN_ADV1 0x10 +#define DW_SR_AN_ADV2 0x11 +#define DW_SR_AN_ADV3 0x12 +#define DW_SR_AN_LP_ABL1 0x13 +#define DW_SR_AN_LP_ABL2 0x14 +#define DW_SR_AN_LP_ABL3 0x15 + +/* Clause 73 Defines */ +/* AN_LP_ABL1 */ +#define DW_C73_PAUSE BIT(10) +#define DW_C73_ASYM_PAUSE BIT(11) +#define DW_C73_AN_ADV_SF 0x1 +/* AN_LP_ABL2 */ +#define DW_C73_1000KX BIT(5) +#define DW_C73_10000KX4 BIT(6) +#define DW_C73_10000KR BIT(7) +/* AN_LP_ABL3 */ +#define DW_C73_2500KX BIT(0) +#define DW_C73_5000KR BIT(1) + +static const int xpcs_usxgmii_features[] = { + ETHTOOL_LINK_MODE_Pause_BIT, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + ETHTOOL_LINK_MODE_Autoneg_BIT, + ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + ETHTOOL_LINK_MODE_2500baseX_Full_BIT, + __ETHTOOL_LINK_MODE_MASK_NBITS, +}; + +static const int xpcs_10gkr_features[] = { + ETHTOOL_LINK_MODE_Pause_BIT, + ETHTOOL_LINK_MODE_Asym_Pause_BIT, + ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, + __ETHTOOL_LINK_MODE_MASK_NBITS, +}; + +static const phy_interface_t xpcs_usxgmii_interfaces[] = { + PHY_INTERFACE_MODE_USXGMII, + PHY_INTERFACE_MODE_MAX, +}; + +static const phy_interface_t xpcs_10gkr_interfaces[] = { + PHY_INTERFACE_MODE_10GKR, + PHY_INTERFACE_MODE_MAX, +}; + +static struct xpcs_id { + u32 id; + u32 mask; + const int *supported; + const phy_interface_t *interface; +} xpcs_id_list[] = { + { + .id = SYNOPSYS_XPCS_USXGMII_ID, + .mask = SYNOPSYS_XPCS_MASK, + .supported = xpcs_usxgmii_features, + .interface = xpcs_usxgmii_interfaces, + }, { + .id = SYNOPSYS_XPCS_10GKR_ID, + .mask = SYNOPSYS_XPCS_MASK, + .supported = xpcs_10gkr_features, + .interface = xpcs_10gkr_interfaces, + }, +}; + +static int xpcs_read(struct mdio_xpcs_args *xpcs, int dev, u32 reg) +{ + u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; + + return mdiobus_read(xpcs->bus, xpcs->addr, reg_addr); +} + +static int xpcs_write(struct mdio_xpcs_args *xpcs, int dev, u32 reg, u16 val) +{ + u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; + + return mdiobus_write(xpcs->bus, xpcs->addr, reg_addr, val); +} + +static int xpcs_read_vendor(struct mdio_xpcs_args *xpcs, int dev, u32 reg) +{ + return xpcs_read(xpcs, dev, DW_VENDOR | reg); +} + +static int xpcs_write_vendor(struct mdio_xpcs_args *xpcs, int dev, int reg, + u16 val) +{ + return xpcs_write(xpcs, dev, DW_VENDOR | reg, val); +} + +static int xpcs_read_vpcs(struct mdio_xpcs_args *xpcs, int reg) +{ + return xpcs_read_vendor(xpcs, MDIO_MMD_PCS, reg); +} + +static int xpcs_write_vpcs(struct mdio_xpcs_args *xpcs, int reg, u16 val) +{ + return xpcs_write_vendor(xpcs, MDIO_MMD_PCS, reg, val); +} + +static int xpcs_poll_reset(struct mdio_xpcs_args *xpcs, int dev) +{ + /* Poll until the reset bit clears (50ms per retry == 0.6 sec) */ + unsigned int retries = 12; + int ret; + + do { + msleep(50); + ret = xpcs_read(xpcs, dev, MDIO_CTRL1); + if (ret < 0) + return ret; + } while (ret & MDIO_CTRL1_RESET && --retries); + + return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; +} + +static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs, int dev) +{ + int ret; + + ret = xpcs_write(xpcs, dev, MDIO_CTRL1, MDIO_CTRL1_RESET); + if (ret < 0) + return ret; + + return xpcs_poll_reset(xpcs, dev); +} + +#define xpcs_warn(__xpcs, __state, __args...) \ +({ \ + if ((__state)->link) \ + dev_warn(&(__xpcs)->bus->dev, ##__args); \ +}) + +static int xpcs_read_fault(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); + if (ret < 0) + return ret; + + if (ret & MDIO_STAT1_FAULT) { + xpcs_warn(xpcs, state, "Link fault condition detected!\n"); + return -EFAULT; + } + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT2); + if (ret < 0) + return ret; + + if (ret & MDIO_STAT2_RXFAULT) + xpcs_warn(xpcs, state, "Receiver fault detected!\n"); + if (ret & MDIO_STAT2_TXFAULT) + xpcs_warn(xpcs, state, "Transmitter fault detected!\n"); + + ret = xpcs_read_vendor(xpcs, MDIO_MMD_PCS, DW_VR_XS_PCS_DIG_STS); + if (ret < 0) + return ret; + + if (ret & DW_RXFIFO_ERR) { + xpcs_warn(xpcs, state, "FIFO fault condition detected!\n"); + return -EFAULT; + } + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_PCS_10GBRT_STAT1_BLKLK)) + xpcs_warn(xpcs, state, "Link is not locked!\n"); + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_PCS_10GBRT_STAT2); + if (ret < 0) + return ret; + + if (ret & MDIO_PCS_10GBRT_STAT2_ERR) + xpcs_warn(xpcs, state, "Link has errors!\n"); + + return 0; +} + +static int xpcs_read_link(struct mdio_xpcs_args *xpcs, bool an) +{ + bool link = true; + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_STAT1_LSTATUS)) + link = false; + + if (an) { + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_STAT1_LSTATUS)) + link = false; + } + + return link; +} + +static int xpcs_get_max_usxgmii_speed(const unsigned long *supported) +{ + int max = SPEED_UNKNOWN; + + if (phylink_test(supported, 1000baseKX_Full)) + max = SPEED_1000; + if (phylink_test(supported, 2500baseX_Full)) + max = SPEED_2500; + if (phylink_test(supported, 10000baseKX4_Full)) + max = SPEED_10000; + if (phylink_test(supported, 10000baseKR_Full)) + max = SPEED_10000; + + return max; +} + +static int xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed) +{ + int ret, speed_sel; + + switch (speed) { + case SPEED_10: + speed_sel = DW_USXGMII_10; + break; + case SPEED_100: + speed_sel = DW_USXGMII_100; + break; + case SPEED_1000: + speed_sel = DW_USXGMII_1000; + break; + case SPEED_2500: + speed_sel = DW_USXGMII_2500; + break; + case SPEED_5000: + speed_sel = DW_USXGMII_5000; + break; + case SPEED_10000: + speed_sel = DW_USXGMII_10000; + break; + default: + /* Nothing to do here */ + return -EINVAL; + } + + ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN); + if (ret < 0) + return ret; + + ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret &= ~DW_USXGMII_SS_MASK; + ret |= speed_sel | DW_USXGMII_FULL; + + ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); + if (ret < 0) + return ret; + + ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); + if (ret < 0) + return ret; + + return xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); +} + +static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) +{ + int ret, adv; + + /* By default, in USXGMII mode XPCS operates at 10G baud and + * replicates data to achieve lower speeds. Hereby, in this + * default configuration we need to advertise all supported + * modes and not only the ones we want to use. + */ + + /* SR_AN_ADV3 */ + adv = 0; + if (phylink_test(xpcs->supported, 2500baseX_Full)) + adv |= DW_C73_2500KX; + + /* TODO: 5000baseKR */ + + ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV3, adv); + if (ret < 0) + return ret; + + /* SR_AN_ADV2 */ + adv = 0; + if (phylink_test(xpcs->supported, 1000baseKX_Full)) + adv |= DW_C73_1000KX; + if (phylink_test(xpcs->supported, 10000baseKX4_Full)) + adv |= DW_C73_10000KX4; + if (phylink_test(xpcs->supported, 10000baseKR_Full)) + adv |= DW_C73_10000KR; + + ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv); + if (ret < 0) + return ret; + + /* SR_AN_ADV1 */ + adv = DW_C73_AN_ADV_SF; + if (phylink_test(xpcs->supported, Pause)) + adv |= DW_C73_PAUSE; + if (phylink_test(xpcs->supported, Asym_Pause)) + adv |= DW_C73_ASYM_PAUSE; + + return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv); +} + +static int xpcs_config_aneg(struct mdio_xpcs_args *xpcs) +{ + int ret; + + ret = xpcs_config_aneg_c73(xpcs); + if (ret < 0) + return ret; + + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART; + + return xpcs_write(xpcs, MDIO_MMD_AN, MDIO_CTRL1, ret); +} + +static int xpcs_aneg_done(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); + if (ret < 0) + return ret; + + if (ret & MDIO_AN_STAT1_COMPLETE) { + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1); + if (ret < 0) + return ret; + + /* Check if Aneg outcome is valid */ + if (!(ret & DW_C73_AN_ADV_SF)) + return 0; + + return 1; + } + + return 0; +} + +static int xpcs_read_lpa(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + ret = xpcs_read(xpcs, MDIO_MMD_AN, MDIO_STAT1); + if (ret < 0) + return ret; + + if (!(ret & MDIO_AN_STAT1_LPABLE)) { + phylink_clear(state->lp_advertising, Autoneg); + return 0; + } + + phylink_set(state->lp_advertising, Autoneg); + + /* Clause 73 outcome */ + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL3); + if (ret < 0) + return ret; + + if (ret & DW_C73_2500KX) + phylink_set(state->lp_advertising, 2500baseX_Full); + + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL2); + if (ret < 0) + return ret; + + if (ret & DW_C73_1000KX) + phylink_set(state->lp_advertising, 1000baseKX_Full); + if (ret & DW_C73_10000KX4) + phylink_set(state->lp_advertising, 10000baseKX4_Full); + if (ret & DW_C73_10000KR) + phylink_set(state->lp_advertising, 10000baseKR_Full); + + ret = xpcs_read(xpcs, MDIO_MMD_AN, DW_SR_AN_LP_ABL1); + if (ret < 0) + return ret; + + if (ret & DW_C73_PAUSE) + phylink_set(state->lp_advertising, Pause); + if (ret & DW_C73_ASYM_PAUSE) + phylink_set(state->lp_advertising, Asym_Pause); + + linkmode_and(state->lp_advertising, state->lp_advertising, + state->advertising); + return 0; +} + +static void xpcs_resolve_lpa(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int max_speed = xpcs_get_max_usxgmii_speed(state->lp_advertising); + + state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; + state->speed = max_speed; + state->duplex = DUPLEX_FULL; +} + +static void xpcs_resolve_pma(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + state->pause = MLO_PAUSE_TX | MLO_PAUSE_RX; + state->duplex = DUPLEX_FULL; + + switch (state->interface) { + case PHY_INTERFACE_MODE_10GKR: + state->speed = SPEED_10000; + break; + default: + state->speed = SPEED_UNKNOWN; + break; + } +} + +static int xpcs_validate(struct mdio_xpcs_args *xpcs, + unsigned long *supported, + struct phylink_link_state *state) +{ + linkmode_and(supported, supported, xpcs->supported); + linkmode_and(state->advertising, state->advertising, xpcs->supported); + return 0; +} + +static int xpcs_config(struct mdio_xpcs_args *xpcs, + const struct phylink_link_state *state) +{ + int ret; + + if (state->an_enabled) { + ret = xpcs_config_aneg(xpcs); + if (ret) + return ret; + } + + return 0; +} + +static int xpcs_get_state(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state) +{ + int ret; + + /* Link needs to be read first ... */ + state->link = xpcs_read_link(xpcs, state->an_enabled) > 0 ? 1 : 0; + + /* ... and then we check the faults. */ + ret = xpcs_read_fault(xpcs, state); + if (ret) { + ret = xpcs_soft_reset(xpcs, MDIO_MMD_PCS); + if (ret) + return ret; + + state->link = 0; + + return xpcs_config(xpcs, state); + } + + if (state->link && state->an_enabled && xpcs_aneg_done(xpcs, state)) { + state->an_complete = true; + xpcs_read_lpa(xpcs, state); + xpcs_resolve_lpa(xpcs, state); + } else if (state->link) { + xpcs_resolve_pma(xpcs, state); + } + + return 0; +} + +static int xpcs_link_up(struct mdio_xpcs_args *xpcs, int speed, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_USXGMII) + return xpcs_config_usxgmii(xpcs, speed); + + return 0; +} + +static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs) +{ + int ret; + u32 id; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID1); + if (ret < 0) + return 0xffffffff; + + id = ret << 16; + + ret = xpcs_read(xpcs, MDIO_MMD_PCS, MII_PHYSID2); + if (ret < 0) + return 0xffffffff; + + return id | ret; +} + +static bool xpcs_check_features(struct mdio_xpcs_args *xpcs, + struct xpcs_id *match, + phy_interface_t interface) +{ + int i; + + for (i = 0; match->interface[i] != PHY_INTERFACE_MODE_MAX; i++) { + if (match->interface[i] == interface) + break; + } + + if (match->interface[i] == PHY_INTERFACE_MODE_MAX) + return false; + + for (i = 0; match->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) + set_bit(match->supported[i], xpcs->supported); + + return true; +} + +static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) +{ + u32 xpcs_id = xpcs_get_id(xpcs); + struct xpcs_id *match = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { + struct xpcs_id *entry = &xpcs_id_list[i]; + + if ((xpcs_id & entry->mask) == entry->id) { + match = entry; + + if (xpcs_check_features(xpcs, match, interface)) + return 0; + } + } + + return -ENODEV; +} + +static struct mdio_xpcs_ops xpcs_ops = { + .validate = xpcs_validate, + .config = xpcs_config, + .get_state = xpcs_get_state, + .link_up = xpcs_link_up, + .probe = xpcs_probe, +}; + +struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) +{ + return &xpcs_ops; +} +EXPORT_SYMBOL_GPL(mdio_xpcs_get_ops); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mdio-xpcs.h b/include/linux/mdio-xpcs.h new file mode 100644 index 000000000000..9a841aa5982d --- /dev/null +++ b/include/linux/mdio-xpcs.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates. + * Synopsys DesignWare XPCS helpers + */ + +#ifndef __LINUX_MDIO_XPCS_H +#define __LINUX_MDIO_XPCS_H + +#include +#include + +struct mdio_xpcs_args { + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); + struct mii_bus *bus; + int addr; +}; + +struct mdio_xpcs_ops { + int (*validate)(struct mdio_xpcs_args *xpcs, + unsigned long *supported, + struct phylink_link_state *state); + int (*config)(struct mdio_xpcs_args *xpcs, + const struct phylink_link_state *state); + int (*get_state)(struct mdio_xpcs_args *xpcs, + struct phylink_link_state *state); + int (*link_up)(struct mdio_xpcs_args *xpcs, int speed, + phy_interface_t interface); + int (*probe)(struct mdio_xpcs_args *xpcs, phy_interface_t interface); +}; + +#if IS_ENABLED(CONFIG_MDIO_XPCS) +struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); +#else +static inline struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) +{ + return NULL; +} +#endif + +#endif /* __LINUX_MDIO_XPCS_H */ -- cgit v1.2.3 From f213bbe8a9d6ba1d0adf424787c02f361ea78c38 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Mon, 9 Mar 2020 09:36:27 +0100 Subject: net: stmmac: Integrate it with DesignWare XPCS Adds all the necessary logic so that stmmac can be used with Synopsys DesignWare XPCS. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/Kconfig | 1 + drivers/net/ethernet/stmicro/stmmac/common.h | 3 +++ drivers/net/ethernet/stmicro/stmmac/hwif.h | 12 ++++++++++ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 16 ++++++++++++-- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 27 +++++++++++++++++++++++ include/linux/stmmac.h | 1 + 6 files changed, 58 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 338e25a6374e..9ad927f646e8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -3,6 +3,7 @@ config STMMAC_ETH tristate "STMicroelectronics Multi-Gigabit Ethernet driver" depends on HAS_IOMEM && HAS_DMA select MII + select MDIO_XPCS select PAGE_POOL select PHYLINK select CRC32 diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 487099092693..9bdbf589d93f 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -15,6 +15,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_VLAN_8021Q) #define STMMAC_VLAN_TAG_USED @@ -446,6 +447,8 @@ struct mac_device_info { const struct stmmac_hwtimestamp *ptp; const struct stmmac_tc_ops *tc; const struct stmmac_mmc_ops *mmc; + const struct mdio_xpcs_ops *xpcs; + struct mdio_xpcs_args xpcs_args; struct mii_regs mii; /* MII register Addresses */ struct mac_link link; void __iomem *pcsr; /* vpointer to device CSRs */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index df63b0367aff..c71dd99c8abf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -577,6 +577,18 @@ struct stmmac_mmc_ops { #define stmmac_mmc_read(__priv, __args...) \ stmmac_do_void_callback(__priv, mmc, read, __args) +/* XPCS callbacks */ +#define stmmac_xpcs_validate(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, validate, __args) +#define stmmac_xpcs_config(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, config, __args) +#define stmmac_xpcs_get_state(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, get_state, __args) +#define stmmac_xpcs_link_up(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, link_up, __args) +#define stmmac_xpcs_probe(__priv, __args...) \ + stmmac_do_callback(__priv, xpcs, probe, __args) + struct stmmac_regs_off { u32 ptp_off; u32 mmc_off; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3a190cf250e6..f26699d9a050 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -863,18 +863,26 @@ static void stmmac_validate(struct phylink_config *config, linkmode_and(state->advertising, state->advertising, mac_supported); linkmode_andnot(state->advertising, state->advertising, mask); + + /* If PCS is supported, check which modes it supports. */ + stmmac_xpcs_validate(priv, &priv->hw->xpcs_args, supported, state); } static void stmmac_mac_pcs_get_state(struct phylink_config *config, struct phylink_link_state *state) { + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + state->link = 0; + stmmac_xpcs_get_state(priv, &priv->hw->xpcs_args, state); } static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { - /* Nothing for now. */ + struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); + + stmmac_xpcs_config(priv, &priv->hw->xpcs_args, state); } static void stmmac_mac_an_restart(struct phylink_config *config) @@ -902,6 +910,8 @@ static void stmmac_mac_link_up(struct phylink_config *config, struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); u32 ctrl; + stmmac_xpcs_link_up(priv, &priv->hw->xpcs_args, speed, interface); + ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl &= ~priv->hw->link.speed_mask; @@ -1042,6 +1052,7 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) priv->phylink_config.dev = &priv->dev->dev; priv->phylink_config.type = PHYLINK_NETDEV; + priv->phylink_config.pcs_poll = true; if (!fwnode) fwnode = dev_fwnode(priv->device); @@ -2689,7 +2700,8 @@ static int stmmac_open(struct net_device *dev) int ret; if (priv->hw->pcs != STMMAC_PCS_TBI && - priv->hw->pcs != STMMAC_PCS_RTBI) { + priv->hw->pcs != STMMAC_PCS_RTBI && + priv->hw->xpcs == NULL) { ret = stmmac_init_phy(dev); if (ret) { netdev_err(priv->dev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index cfe5d8b73142..b2a707e2ef43 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -382,6 +382,14 @@ int stmmac_mdio_register(struct net_device *ndev) max_addr = PHY_MAX_ADDR; } + if (mdio_bus_data->has_xpcs) { + priv->hw->xpcs = mdio_xpcs_get_ops(); + if (!priv->hw->xpcs) { + err = -ENODEV; + goto bus_register_fail; + } + } + if (mdio_bus_data->needs_reset) new_bus->reset = &stmmac_mdio_reset; @@ -433,6 +441,25 @@ int stmmac_mdio_register(struct net_device *ndev) found = 1; } + /* Try to probe the XPCS by scanning all addresses. */ + if (priv->hw->xpcs) { + struct mdio_xpcs_args *xpcs = &priv->hw->xpcs_args; + int ret, mode = priv->plat->phy_interface; + max_addr = PHY_MAX_ADDR; + + xpcs->bus = new_bus; + + for (addr = 0; addr < max_addr; addr++) { + xpcs->addr = addr; + + ret = stmmac_xpcs_probe(priv, xpcs, mode); + if (!ret) { + found = 1; + break; + } + } + } + if (!found && !mdio_node) { dev_warn(dev, "No PHY found\n"); mdiobus_unregister(new_bus); diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index 19190c609282..fbafb353e9be 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -80,6 +80,7 @@ struct stmmac_mdio_bus_data { unsigned int phy_mask; + unsigned int has_xpcs; int *irqs; int probed_phy_irq; bool needs_reset; -- cgit v1.2.3 From f5152416528c2295f35dd9c9bd4fb27c4032413d Mon Sep 17 00:00:00 2001 From: Qian Cai Date: Thu, 5 Mar 2020 15:15:02 -0500 Subject: iommu/vt-d: Silence RCU-list debugging warnings Similar to the commit 02d715b4a818 ("iommu/vt-d: Fix RCU list debugging warnings"), there are several other places that call list_for_each_entry_rcu() outside of an RCU read side critical section but with dmar_global_lock held. Silence those false positives as well. drivers/iommu/intel-iommu.c:4288 RCU-list traversed in non-reader section!! 1 lock held by swapper/0/1: #0: ffffffff935892c8 (dmar_global_lock){+.+.}, at: intel_iommu_init+0x1ad/0xb97 drivers/iommu/dmar.c:366 RCU-list traversed in non-reader section!! 1 lock held by swapper/0/1: #0: ffffffff935892c8 (dmar_global_lock){+.+.}, at: intel_iommu_init+0x125/0xb97 drivers/iommu/intel-iommu.c:5057 RCU-list traversed in non-reader section!! 1 lock held by swapper/0/1: #0: ffffffffa71892c8 (dmar_global_lock){++++}, at: intel_iommu_init+0x61a/0xb13 Signed-off-by: Qian Cai Acked-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 3 ++- include/linux/dmar.h | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 071bb42bbbc5..7b16c4db40b4 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -363,7 +363,8 @@ dmar_find_dmaru(struct acpi_dmar_hardware_unit *drhd) { struct dmar_drhd_unit *dmaru; - list_for_each_entry_rcu(dmaru, &dmar_drhd_units, list) + list_for_each_entry_rcu(dmaru, &dmar_drhd_units, list, + dmar_rcu_check()) if (dmaru->segment == drhd->segment && dmaru->reg_base_addr == drhd->address) return dmaru; diff --git a/include/linux/dmar.h b/include/linux/dmar.h index 712be8bc6a7c..d7bf029df737 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -74,11 +74,13 @@ extern struct list_head dmar_drhd_units; dmar_rcu_check()) #define for_each_active_drhd_unit(drhd) \ - list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \ + list_for_each_entry_rcu(drhd, &dmar_drhd_units, list, \ + dmar_rcu_check()) \ if (drhd->ignored) {} else #define for_each_active_iommu(i, drhd) \ - list_for_each_entry_rcu(drhd, &dmar_drhd_units, list) \ + list_for_each_entry_rcu(drhd, &dmar_drhd_units, list, \ + dmar_rcu_check()) \ if (i=drhd->iommu, drhd->ignored) {} else #define for_each_iommu(i, drhd) \ -- cgit v1.2.3 From ed680522268da2f6f2a67505dd144e718d726712 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 28 Feb 2020 18:12:20 +0100 Subject: i2c: convert SMBus alert setup function to return an ERRPTR Only few drivers use this call, so drivers and I2C core are converted at once with this patch. By simply using i2c_new_client_device() instead of i2c_new_device(), we easily can return an ERRPTR for this function as well. To make out of tree users aware that something changed, the function is renamed to i2c_new_smbus_alert_device(). Signed-off-by: Wolfram Sang Reviewed-by: Luca Ceresoli Signed-off-by: Wolfram Sang --- Documentation/i2c/smbus-protocol.rst | 2 +- drivers/i2c/busses/i2c-parport.c | 12 ++++++++---- drivers/i2c/busses/i2c-thunderx-pcidrv.c | 11 ++++++++--- drivers/i2c/busses/i2c-xlp9xx.c | 10 +++++++--- drivers/i2c/i2c-core-smbus.c | 21 ++++++++------------- drivers/i2c/i2c-smbus.c | 2 +- include/linux/i2c-smbus.h | 4 ++-- 7 files changed, 35 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/Documentation/i2c/smbus-protocol.rst b/Documentation/i2c/smbus-protocol.rst index c122ed239f7f..c2e29633071e 100644 --- a/Documentation/i2c/smbus-protocol.rst +++ b/Documentation/i2c/smbus-protocol.rst @@ -274,7 +274,7 @@ to know which slave triggered the interrupt. This is implemented the following way in the Linux kernel: * I2C bus drivers which support SMBus alert should call - i2c_setup_smbus_alert() to setup SMBus alert support. + i2c_new_smbus_alert_device() to install SMBus alert support. * I2C drivers for devices which can trigger SMBus alerts should implement the optional alert() callback. diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index 81eb441b2387..a535889acca6 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -333,13 +333,17 @@ static void i2c_parport_attach(struct parport *port) /* Setup SMBus alert if supported */ if (adapter_parm[type].smbus_alert) { - adapter->ara = i2c_setup_smbus_alert(&adapter->adapter, - &adapter->alert_data); - if (adapter->ara) + struct i2c_client *ara; + + ara = i2c_new_smbus_alert_device(&adapter->adapter, + &adapter->alert_data); + if (!IS_ERR(ara)) { + adapter->ara = ara; parport_enable_irq(port); - else + } else { dev_warn(&adapter->pdev->dev, "Failed to register ARA client\n"); + } } /* Add the new adapter to the list */ diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index 19f8eec38717..7d3b9d66ad36 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -118,6 +118,8 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, struct device_node *node) { + struct i2c_client *ara; + if (!node) return -EINVAL; @@ -125,9 +127,12 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, if (!i2c->alert_data.irq) return -EINVAL; - i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data); - if (!i2c->ara) - return -ENODEV; + ara = i2c_new_smbus_alert_device(&i2c->adap, &i2c->alert_data); + if (IS_ERR(ara)) + return PTR_ERR(ara); + + i2c->ara = ara; + return 0; } diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index 8a873975cf12..823945bc3249 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -491,12 +491,16 @@ static int xlp9xx_i2c_get_frequency(struct platform_device *pdev, static int xlp9xx_i2c_smbus_setup(struct xlp9xx_i2c_dev *priv, struct platform_device *pdev) { + struct i2c_client *ara; + if (!priv->alert_data.irq) return -EINVAL; - priv->ara = i2c_setup_smbus_alert(&priv->adapter, &priv->alert_data); - if (!priv->ara) - return -ENODEV; + ara = i2c_new_smbus_alert_device(&priv->adapter, &priv->alert_data); + if (IS_ERR(ara)) + return PTR_ERR(ara); + + priv->ara = ara; return 0; } diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 3ac426a8ab5a..fd2b961f113e 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -666,7 +666,7 @@ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client, EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); /** - * i2c_setup_smbus_alert - Setup SMBus alert support + * i2c_new_smbus_alert_device - get ara client for SMBus alert support * @adapter: the target adapter * @setup: setup data for the SMBus alert handler * Context: can sleep @@ -682,25 +682,24 @@ EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); * should have said it's level triggered. * * This returns the ara client, which should be saved for later use with - * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL - * to indicate an error. + * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or an + * ERRPTR to indicate an error. */ -struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, - struct i2c_smbus_alert_setup *setup) +struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter, + struct i2c_smbus_alert_setup *setup) { struct i2c_board_info ara_board_info = { I2C_BOARD_INFO("smbus_alert", 0x0c), .platform_data = setup, }; - return i2c_new_device(adapter, &ara_board_info); + return i2c_new_client_device(adapter, &ara_board_info); } -EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); +EXPORT_SYMBOL_GPL(i2c_new_smbus_alert_device); #if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) { - struct i2c_client *client; int irq; irq = of_property_match_string(adapter->dev.of_node, "interrupt-names", @@ -710,11 +709,7 @@ int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) else if (irq < 0) return irq; - client = i2c_setup_smbus_alert(adapter, NULL); - if (!client) - return -ENODEV; - - return 0; + return PTR_ERR_OR_ZERO(i2c_new_smbus_alert_device(adapter, NULL)); } EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert); #endif diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 7e2f5d0eacdb..809bcf8387d0 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -184,7 +184,7 @@ static struct i2c_driver smbalert_driver = { * corresponding I2C device driver's alert function. * * It is assumed that ara is a valid i2c client previously returned by - * i2c_setup_smbus_alert(). + * i2c_new_smbus_alert_device(). */ int i2c_handle_smbus_alert(struct i2c_client *ara) { diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index 585ad6fc3847..802aac0d2010 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -31,8 +31,8 @@ struct i2c_smbus_alert_setup { int irq; }; -struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, - struct i2c_smbus_alert_setup *setup); +struct i2c_client *i2c_new_smbus_alert_device(struct i2c_adapter *adapter, + struct i2c_smbus_alert_setup *setup); int i2c_handle_smbus_alert(struct i2c_client *ara); #if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) -- cgit v1.2.3 From a47070aac935b9c0e5d0f99843e0c8784f455ea7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 28 Feb 2020 18:12:21 +0100 Subject: i2c: smbus: remove outdated references to irq level triggers IRQ levels are now handled within the IRQ core. Remove the forgotten references from the documentation. Fixes: 9b9f2b8bc2ac ("i2c: i2c-smbus: Use threaded irq for smbalert") Signed-off-by: Wolfram Sang Reviewed-by: Luca Ceresoli Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-smbus.c | 5 ----- include/linux/i2c-smbus.h | 5 ----- 2 files changed, 10 deletions(-) (limited to 'include') diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index fd2b961f113e..b34d2ff06931 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -676,11 +676,6 @@ EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data_or_emulated); * Handling can be done either through our IRQ handler, or by the * adapter (from its handler, periodic polling, or whatever). * - * NOTE that if we manage the IRQ, we *MUST* know if it's level or - * edge triggered in order to hand it to the workqueue correctly. - * If triggering the alert seems to wedge the system, you probably - * should have said it's level triggered. - * * This returns the ara client, which should be saved for later use with * i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or an * ERRPTR to indicate an error. diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index 802aac0d2010..8c5459034f92 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -15,17 +15,12 @@ /** * i2c_smbus_alert_setup - platform data for the smbus_alert i2c client - * @alert_edge_triggered: whether the alert interrupt is edge (1) or level (0) - * triggered * @irq: IRQ number, if the smbus_alert driver should take care of interrupt * handling * * If irq is not specified, the smbus_alert driver doesn't take care of * interrupt handling. In that case it is up to the I2C bus driver to either * handle the interrupts or to poll for alerts. - * - * If irq is specified then it it crucial that alert_edge_triggered is - * properly set. */ struct i2c_smbus_alert_setup { int irq; -- cgit v1.2.3 From a22ae72b86a4f754e8d25fbf9ea5a8f77365e531 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 9 Mar 2020 14:27:43 -0500 Subject: ASoC: soc-core: disable route checks for legacy devices v5.4 changes in soc-core tightened the checks on soc_dapm_add_routes, which results in the ASoC card probe failing. Introduce a flag to be set in machine drivers to prevent the probe from stopping in case of incomplete topologies or missing routes. This flag is for backwards compatibility only and shall not be used for newer machine drivers. Example with an HDaudio card with a bad topology: [ 236.177898] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: Failed to add route iDisp1_out -> direct -> iDisp1 Tx [ 236.177902] skl_hda_dsp_generic skl_hda_dsp_generic: snd_soc_bind_card: snd_soc_dapm_add_routes failed: -19 with the disable_route_checks set: [ 64.031657] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: Failed to add route iDisp1_out -> direct -> iDisp1 Tx [ 64.031661] skl_hda_dsp_generic skl_hda_dsp_generic: snd_soc_bind_card: disable_route_checks set, ignoring errors on add_routes Fixes: daa480bde6b3a9 ("ASoC: soc-core: tidyup for snd_soc_dapm_add_routes()") Signed-off-by: Pierre-Louis Bossart Acked-by: Kuninori Morimoto Link: https://lore.kernel.org/r/20200309192744.18380-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 28 ++++++++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 8a2266676b2d..efb8bad7b0fa 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1058,6 +1058,7 @@ struct snd_soc_card { const struct snd_soc_dapm_route *of_dapm_routes; int num_of_dapm_routes; bool fully_routed; + bool disable_route_checks; /* lists of probed devices belonging to this card */ struct list_head component_dev_list; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 068d809c349a..b17366bac846 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1256,8 +1256,18 @@ static int soc_probe_component(struct snd_soc_card *card, ret = snd_soc_dapm_add_routes(dapm, component->driver->dapm_routes, component->driver->num_dapm_routes); - if (ret < 0) - goto err_probe; + if (ret < 0) { + if (card->disable_route_checks) { + dev_info(card->dev, + "%s: disable_route_checks set, ignoring errors on add_routes\n", + __func__); + } else { + dev_err(card->dev, + "%s: snd_soc_dapm_add_routes failed: %d\n", + __func__, ret); + goto err_probe; + } + } /* see for_each_card_components */ list_add(&component->card_list, &card->component_dev_list); @@ -1938,8 +1948,18 @@ static int snd_soc_bind_card(struct snd_soc_card *card) ret = snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - if (ret < 0) - goto probe_end; + if (ret < 0) { + if (card->disable_route_checks) { + dev_info(card->dev, + "%s: disable_route_checks set, ignoring errors on add_routes\n", + __func__); + } else { + dev_err(card->dev, + "%s: snd_soc_dapm_add_routes failed: %d\n", + __func__, ret); + goto probe_end; + } + } ret = snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, card->num_of_dapm_routes); -- cgit v1.2.3 From 2dd209f00fc5a1caafa493066c7cd692fd2fd57c Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Mon, 9 Mar 2020 21:26:16 -0700 Subject: blk-mq: Fix a comment in include/linux/blk-mq.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'hctx_list' member of struct blk_mq_hw_ctx is not a list head but instead an entry in q->unused_hctx_list. Fix the comment above this struct member. Fixes: d386732bc142 ("blk-mq: fill header with kernel-doc") Signed-off-by: Bart Van Assche Reviewed-by: Chaitanya Kulkarni Cc: André Almeida Signed-off-by: Jens Axboe --- include/linux/blk-mq.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 11cfd6470b1a..31344d5f83e2 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -162,7 +162,10 @@ struct blk_mq_hw_ctx { struct dentry *sched_debugfs_dir; #endif - /** @hctx_list: List of all hardware queues. */ + /** + * @hctx_list: if this hctx is not in use, this is an entry in + * q->unused_hctx_list. + */ struct list_head hctx_list; /** -- cgit v1.2.3 From 995cbc3ca1ab39fb5cf254181dcfba883c5d6d69 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 9 Mar 2020 13:07:29 +0900 Subject: ASoC: soc.h: add for_each_rtd_codecs/cpus_dai() macro We are using plural form for for_each_xxx() macro. But, for_each_rtd_codec/cpu_dai() are out of this rule. This patch adds plural form macro. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/8736aii326.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9543d9246ca4..09bc45b8bf00 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1177,6 +1177,20 @@ struct snd_soc_pcm_runtime { #define for_each_rtd_cpu_dai_rollback(rtd, i, dai) \ for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);) +#define for_each_rtd_cpu_dais(rtd, i, dai) \ + for ((i) = 0; \ + ((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \ + (i)++) +#define for_each_rtd_cpu_dais_rollback(rtd, i, dai) \ + for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);) +#define for_each_rtd_codec_dais(rtd, i, dai) \ + for ((i) = 0; \ + ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \ + (i)++) +#define for_each_rtd_codec_dais_rollback(rtd, i, dai) \ + for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);) + + void snd_soc_close_delayed_work(struct snd_soc_pcm_runtime *rtd); /* mixer control */ -- cgit v1.2.3 From 17e6dab5013ddb36997011cf6daea7297dfc215e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 9 Mar 2020 13:08:10 +0900 Subject: ASoC: soc.h: remove non plural form for_each_xxx macro Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87tv2ygogl.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 09bc45b8bf00..5e1b4ef1543c 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1163,20 +1163,6 @@ struct snd_soc_pcm_runtime { for ((i) = 0; \ ((i) < rtd->num_components) && ((component) = rtd->components[i]);\ (i)++) -#define for_each_rtd_codec_dai(rtd, i, dai)\ - for ((i) = 0; \ - ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \ - (i)++) -#define for_each_rtd_codec_dai_rollback(rtd, i, dai) \ - for (; (--(i) >= 0) && ((dai) = rtd->codec_dais[i]);) - -#define for_each_rtd_cpu_dai(rtd, i, dai)\ - for ((i) = 0; \ - ((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \ - (i)++) -#define for_each_rtd_cpu_dai_rollback(rtd, i, dai) \ - for (; (--(i) >= 0) && ((dai) = rtd->cpu_dais[i]);) - #define for_each_rtd_cpu_dais(rtd, i, dai) \ for ((i) = 0; \ ((i) < rtd->num_cpus) && ((dai) = rtd->cpu_dais[i]); \ -- cgit v1.2.3 From df817f8e71e3a0256bd3d2d3a4e5399b409698f4 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 9 Mar 2020 13:08:16 +0900 Subject: ASoC: soc-dapm: add for_each_card_dapms() macro To be more readable code, this patch adds new for_each_card_dapms() macro, and replace existing code to it. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87sgiigogf.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 3 +++ sound/soc/soc-dapm.c | 18 +++++++++--------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 5e1b4ef1543c..3aee33c8249e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1117,6 +1117,9 @@ struct snd_soc_card { #define for_each_card_components(card, component) \ list_for_each_entry(component, &(card)->component_dev_list, card_list) +#define for_each_card_dapms(card, dapm) \ + list_for_each_entry(dapm, &card->dapm_list, list) + /* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_pcm_runtime { struct device *dev; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 7374829c6675..ac48303ea26d 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1716,9 +1716,8 @@ static void dapm_seq_run(struct snd_soc_card *card, i, cur_subseq); } - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) soc_dapm_async_complete(d); - } } static void dapm_widget_update(struct snd_soc_card *card) @@ -1949,7 +1948,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) trace_snd_soc_dapm_start(card); - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (dapm_idle_bias_off(d)) d->target_bias_level = SND_SOC_BIAS_OFF; else @@ -2013,10 +2012,10 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) * they're not ground referenced. */ bias = SND_SOC_BIAS_OFF; - list_for_each_entry(d, &card->dapm_list, list) + for_each_card_dapms(card, d) if (d->target_bias_level > bias) bias = d->target_bias_level; - list_for_each_entry(d, &card->dapm_list, list) + for_each_card_dapms(card, d) if (!dapm_idle_bias_off(d)) d->target_bias_level = bias; @@ -2025,7 +2024,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) /* Run card bias changes at first */ dapm_pre_sequence_async(&card->dapm, 0); /* Run other bias changes in parallel */ - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (d != &card->dapm && d->bias_level != d->target_bias_level) async_schedule_domain(dapm_pre_sequence_async, d, &async_domain); @@ -2049,7 +2048,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_seq_run(card, &up_list, event, true); /* Run all the bias changes in parallel */ - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (d != &card->dapm && d->bias_level != d->target_bias_level) async_schedule_domain(dapm_post_sequence_async, d, &async_domain); @@ -2059,7 +2058,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_post_sequence_async(&card->dapm, 0); /* do we need to notify any clients that DAPM event is complete */ - list_for_each_entry(d, &card->dapm_list, list) { + for_each_card_dapms(card, d) { if (!d->component) continue; @@ -4776,6 +4775,7 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm, } INIT_LIST_HEAD(&dapm->list); + /* see for_each_card_dapms */ list_add(&dapm->list, &card->dapm_list); } EXPORT_SYMBOL_GPL(snd_soc_dapm_init); @@ -4822,7 +4822,7 @@ void snd_soc_dapm_shutdown(struct snd_soc_card *card) { struct snd_soc_dapm_context *dapm; - list_for_each_entry(dapm, &card->dapm_list, list) { + for_each_card_dapms(card, dapm) { if (dapm != &card->dapm) { soc_dapm_shutdown_dapm(dapm); if (dapm->bias_level == SND_SOC_BIAS_STANDBY) -- cgit v1.2.3 From 14596692631eadbefba8419698cccfc23bfccd2b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 9 Mar 2020 13:08:21 +0900 Subject: ASoC: soc-dapm: add for_each_card_widgets() macro To be more readable code, this patch adds new for_each_card_widgets() macro, and replace existing code to it. Signed-off-by: Kuninori Morimoto Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/87r1y2goga.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- include/sound/soc.h | 5 +++++ sound/soc/soc-dapm.c | 25 +++++++++++++------------ sound/soc/soc-topology.c | 2 +- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 3aee33c8249e..03054bf9cd37 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1120,6 +1120,11 @@ struct snd_soc_card { #define for_each_card_dapms(card, dapm) \ list_for_each_entry(dapm, &card->dapm_list, list) +#define for_each_card_widgets(card, w)\ + list_for_each_entry(w, &card->widgets, list) +#define for_each_card_widgets_safe(card, w, _w) \ + list_for_each_entry_safe(w, _w, &card->widgets, list) + /* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_pcm_runtime { struct device *dev; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index ac48303ea26d..e00a465a7c32 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -302,7 +302,7 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card) mutex_lock(&card->dapm_mutex); - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { if (w->is_ep) { dapm_mark_dirty(w, "Rechecking endpoints"); if (w->is_ep & SND_SOC_DAPM_EP_SINK) @@ -589,7 +589,7 @@ static void dapm_reset(struct snd_soc_card *card) memset(&card->dapm_stats, 0, sizeof(card->dapm_stats)); - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { w->new_power = w->power; w->power_checked = false; } @@ -833,7 +833,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, *kcontrol = NULL; - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (w == kcontrolw || w->dapm != kcontrolw->dapm) continue; for (i = 0; i < w->num_kcontrols; i++) { @@ -1967,7 +1967,7 @@ static int dapm_power_widgets(struct snd_soc_card *card, int event) dapm_power_one_widget(w, &up_list, &down_list); } - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { switch (w->id) { case snd_soc_dapm_pre: case snd_soc_dapm_post: @@ -2376,7 +2376,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt, if (!cmpnt->card) return 0; - list_for_each_entry(w, &cmpnt->card->widgets, list) { + for_each_card_widgets(cmpnt->card, w) { if (w->dapm != dapm) continue; @@ -2496,7 +2496,7 @@ static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w, *next_w; - list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + for_each_card_widgets_safe(dapm->card, w, next_w) { if (w->dapm != dapm) continue; snd_soc_dapm_free_widget(w); @@ -2511,7 +2511,7 @@ static struct snd_soc_dapm_widget *dapm_find_widget( struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *fallback = NULL; - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (!strcmp(w->name, pin)) { if (w->dapm == dapm) return w; @@ -2910,7 +2910,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, * find src and dest widgets over all widgets but favor a widget from * current DAPM context */ - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (!wsink && !(strcmp(w->name, sink))) { wtsink = w; if (w->dapm == dapm) { @@ -3189,7 +3189,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT); - list_for_each_entry(w, &card->widgets, list) + for_each_card_widgets(card, w) { if (w->new) continue; @@ -3703,6 +3703,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm, w->dapm = dapm; INIT_LIST_HEAD(&w->list); INIT_LIST_HEAD(&w->dirty); + /* see for_each_card_widgets */ list_add_tail(&w->list, &dapm->card->widgets); snd_soc_dapm_for_each_direction(dir) { @@ -4227,7 +4228,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) struct snd_soc_dai *dai; /* For each DAI widget... */ - list_for_each_entry(dai_w, &card->widgets, list) { + for_each_card_widgets(card, dai_w) { switch (dai_w->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: @@ -4246,7 +4247,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card) dai = dai_w->priv; /* ...find all widgets with the same stream and link them */ - list_for_each_entry(w, &card->widgets, list) { + for_each_card_widgets(card, w) { if (w->dapm != dai_w->dapm) continue; @@ -4789,7 +4790,7 @@ static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm) mutex_lock(&card->dapm_mutex); - list_for_each_entry(w, &dapm->card->widgets, list) { + for_each_card_widgets(dapm->card, w) { if (w->dapm != dapm) continue; if (w->power) { diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index 575da6aba807..33909afd3bbc 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -2774,7 +2774,7 @@ void snd_soc_tplg_widget_remove_all(struct snd_soc_dapm_context *dapm, { struct snd_soc_dapm_widget *w, *next_w; - list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { + for_each_card_widgets_safe(dapm->card, w, next_w) { /* make sure we are a widget with correct context */ if (w->dobj.type != SND_SOC_DOBJ_WIDGET || w->dapm != dapm) -- cgit v1.2.3 From 7a86a419ff62c69f91544ea95f4ae6ef83880dcc Mon Sep 17 00:00:00 2001 From: Qiujun Huang Date: Tue, 10 Mar 2020 01:16:19 +0800 Subject: spi: update the structure documentation some members were not described in documentation. Signed-off-by: Qiujun Huang Link: https://lore.kernel.org/r/1583774179-30736-1-git-send-email-hqjagain@gmail.com Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 87105272879b..38286de779e3 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -135,6 +135,8 @@ extern int spi_delay_exec(struct spi_delay *_delay, struct spi_transfer *xfer); * @modalias: Name of the driver to use with this device, or an alias * for that name. This appears in the sysfs "modalias" attribute * for driver coldplugging, and in uevents used for hotplugging + * @driver_override: If the name of a driver is written to this attribute, then + * the device will bind to the named driver and only the named driver. * @cs_gpio: LEGACY: gpio number of the chipselect line (optional, -ENOENT when * not using a GPIO line) use cs_gpiod in new drivers by opting in on * the spi_master. @@ -443,6 +445,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @spi_transfer->ptp_sts_word_post were transmitted. * If the driver does not set this, the SPI core takes the snapshot as * close to the driver hand-over as possible. + * @irq_flags: Interrupt enable state during PTP system timestamping * * Each SPI controller can communicate with one or more @spi_device * children. These make a small bus, sharing MOSI, MISO and SCK signals -- cgit v1.2.3 From 30f2fe40c72bfbdde7bc066cb862bd05014be9f1 Mon Sep 17 00:00:00 2001 From: Yishai Hadas Date: Wed, 19 Feb 2020 21:05:18 +0200 Subject: IB/mlx5: Introduce UAPIs to manage packet pacing Introduce packet pacing uobject and its alloc and destroy methods. This uobject holds mlx5 packet pacing context according to the device specification and enables managing packet pacing device entries that are needed by DEVX applications. Link: https://lore.kernel.org/r/20200219190518.200912-3-leon@kernel.org Signed-off-by: Yishai Hadas Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/Makefile | 1 + drivers/infiniband/hw/mlx5/main.c | 1 + drivers/infiniband/hw/mlx5/mlx5_ib.h | 6 ++ drivers/infiniband/hw/mlx5/qos.c | 136 ++++++++++++++++++++++++++++++ include/uapi/rdma/mlx5_user_ioctl_cmds.h | 17 ++++ include/uapi/rdma/mlx5_user_ioctl_verbs.h | 4 + 6 files changed, 165 insertions(+) create mode 100644 drivers/infiniband/hw/mlx5/qos.c (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/Makefile b/drivers/infiniband/hw/mlx5/Makefile index d0a043ccbe58..2a334800f109 100644 --- a/drivers/infiniband/hw/mlx5/Makefile +++ b/drivers/infiniband/hw/mlx5/Makefile @@ -8,3 +8,4 @@ mlx5_ib-$(CONFIG_INFINIBAND_ON_DEMAND_PAGING) += odp.o mlx5_ib-$(CONFIG_MLX5_ESWITCH) += ib_rep.o mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += devx.o mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += flow.o +mlx5_ib-$(CONFIG_INFINIBAND_USER_ACCESS) += qos.o diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index e4bcfa81b70a..7fc5ce9bdf3d 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -6251,6 +6251,7 @@ ADD_UVERBS_ATTRIBUTES_SIMPLE( static const struct uapi_definition mlx5_ib_defs[] = { UAPI_DEF_CHAIN(mlx5_ib_devx_defs), UAPI_DEF_CHAIN(mlx5_ib_flow_defs), + UAPI_DEF_CHAIN(mlx5_ib_qos_defs), UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_FLOW_ACTION, &mlx5_ib_flow_action), diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index d9bffcc93587..09ce80febec7 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -203,6 +203,11 @@ struct mlx5_ib_flow_matcher { u8 match_criteria_enable; }; +struct mlx5_ib_pp { + u16 index; + struct mlx5_core_dev *mdev; +}; + struct mlx5_ib_flow_db { struct mlx5_ib_flow_prio prios[MLX5_IB_NUM_FLOW_FT]; struct mlx5_ib_flow_prio egress_prios[MLX5_IB_NUM_FLOW_FT]; @@ -1381,6 +1386,7 @@ int mlx5_ib_fill_stat_entry(struct sk_buff *msg, extern const struct uapi_definition mlx5_ib_devx_defs[]; extern const struct uapi_definition mlx5_ib_flow_defs[]; +extern const struct uapi_definition mlx5_ib_qos_defs[]; #if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS) int mlx5_ib_devx_create(struct mlx5_ib_dev *dev, bool is_user); diff --git a/drivers/infiniband/hw/mlx5/qos.c b/drivers/infiniband/hw/mlx5/qos.c new file mode 100644 index 000000000000..f822b06e7c9e --- /dev/null +++ b/drivers/infiniband/hw/mlx5/qos.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB +/* + * Copyright (c) 2020, Mellanox Technologies inc. All rights reserved. + */ + +#include +#include +#include +#include +#include "mlx5_ib.h" + +#define UVERBS_MODULE_NAME mlx5_ib +#include + +static bool pp_is_supported(struct ib_device *device) +{ + struct mlx5_ib_dev *dev = to_mdev(device); + + return (MLX5_CAP_GEN(dev->mdev, qos) && + MLX5_CAP_QOS(dev->mdev, packet_pacing) && + MLX5_CAP_QOS(dev->mdev, packet_pacing_uid)); +} + +static int UVERBS_HANDLER(MLX5_IB_METHOD_PP_OBJ_ALLOC)( + struct uverbs_attr_bundle *attrs) +{ + u8 rl_raw[MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)] = {}; + struct ib_uobject *uobj = uverbs_attr_get_uobject(attrs, + MLX5_IB_ATTR_PP_OBJ_ALLOC_HANDLE); + struct mlx5_ib_dev *dev; + struct mlx5_ib_ucontext *c; + struct mlx5_ib_pp *pp_entry; + void *in_ctx; + u16 uid; + int inlen; + u32 flags; + int err; + + c = to_mucontext(ib_uverbs_get_ucontext(attrs)); + if (IS_ERR(c)) + return PTR_ERR(c); + + /* The allocated entry can be used only by a DEVX context */ + if (!c->devx_uid) + return -EINVAL; + + dev = to_mdev(c->ibucontext.device); + pp_entry = kzalloc(sizeof(*pp_entry), GFP_KERNEL); + if (IS_ERR(pp_entry)) + return PTR_ERR(pp_entry); + + in_ctx = uverbs_attr_get_alloced_ptr(attrs, + MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX); + inlen = uverbs_attr_get_len(attrs, + MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX); + memcpy(rl_raw, in_ctx, inlen); + err = uverbs_get_flags32(&flags, attrs, + MLX5_IB_ATTR_PP_OBJ_ALLOC_FLAGS, + MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX); + if (err) + goto err; + + uid = (flags & MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX) ? + c->devx_uid : MLX5_SHARED_RESOURCE_UID; + + err = mlx5_rl_add_rate_raw(dev->mdev, rl_raw, uid, + (flags & MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX), + &pp_entry->index); + if (err) + goto err; + + err = uverbs_copy_to(attrs, MLX5_IB_ATTR_PP_OBJ_ALLOC_INDEX, + &pp_entry->index, sizeof(pp_entry->index)); + if (err) + goto clean; + + pp_entry->mdev = dev->mdev; + uobj->object = pp_entry; + return 0; + +clean: + mlx5_rl_remove_rate_raw(dev->mdev, pp_entry->index); +err: + kfree(pp_entry); + return err; +} + +static int pp_obj_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct mlx5_ib_pp *pp_entry = uobject->object; + + mlx5_rl_remove_rate_raw(pp_entry->mdev, pp_entry->index); + kfree(pp_entry); + return 0; +} + +DECLARE_UVERBS_NAMED_METHOD( + MLX5_IB_METHOD_PP_OBJ_ALLOC, + UVERBS_ATTR_IDR(MLX5_IB_ATTR_PP_OBJ_ALLOC_HANDLE, + MLX5_IB_OBJECT_PP, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN( + MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX, + UVERBS_ATTR_SIZE(1, + MLX5_ST_SZ_BYTES(set_pp_rate_limit_context)), + UA_MANDATORY, + UA_ALLOC_AND_COPY), + UVERBS_ATTR_FLAGS_IN(MLX5_IB_ATTR_PP_OBJ_ALLOC_FLAGS, + enum mlx5_ib_uapi_pp_alloc_flags, + UA_MANDATORY), + UVERBS_ATTR_PTR_OUT(MLX5_IB_ATTR_PP_OBJ_ALLOC_INDEX, + UVERBS_ATTR_TYPE(u16), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY( + MLX5_IB_METHOD_PP_OBJ_DESTROY, + UVERBS_ATTR_IDR(MLX5_IB_ATTR_PP_OBJ_DESTROY_HANDLE, + MLX5_IB_OBJECT_PP, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_PP, + UVERBS_TYPE_ALLOC_IDR(pp_obj_cleanup), + &UVERBS_METHOD(MLX5_IB_METHOD_PP_OBJ_ALLOC), + &UVERBS_METHOD(MLX5_IB_METHOD_PP_OBJ_DESTROY)); + + +const struct uapi_definition mlx5_ib_qos_defs[] = { + UAPI_DEF_CHAIN_OBJ_TREE_NAMED( + MLX5_IB_OBJECT_PP, + UAPI_DEF_IS_OBJ_SUPPORTED(pp_is_supported)), + {}, +}; diff --git a/include/uapi/rdma/mlx5_user_ioctl_cmds.h b/include/uapi/rdma/mlx5_user_ioctl_cmds.h index afe7da6f2b8e..8f4a417fc70a 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_cmds.h +++ b/include/uapi/rdma/mlx5_user_ioctl_cmds.h @@ -143,6 +143,22 @@ enum mlx5_ib_devx_umem_dereg_attrs { MLX5_IB_ATTR_DEVX_UMEM_DEREG_HANDLE = (1U << UVERBS_ID_NS_SHIFT), }; +enum mlx5_ib_pp_obj_methods { + MLX5_IB_METHOD_PP_OBJ_ALLOC = (1U << UVERBS_ID_NS_SHIFT), + MLX5_IB_METHOD_PP_OBJ_DESTROY, +}; + +enum mlx5_ib_pp_alloc_attrs { + MLX5_IB_ATTR_PP_OBJ_ALLOC_HANDLE = (1U << UVERBS_ID_NS_SHIFT), + MLX5_IB_ATTR_PP_OBJ_ALLOC_CTX, + MLX5_IB_ATTR_PP_OBJ_ALLOC_FLAGS, + MLX5_IB_ATTR_PP_OBJ_ALLOC_INDEX, +}; + +enum mlx5_ib_pp_obj_destroy_attrs { + MLX5_IB_ATTR_PP_OBJ_DESTROY_HANDLE = (1U << UVERBS_ID_NS_SHIFT), +}; + enum mlx5_ib_devx_umem_methods { MLX5_IB_METHOD_DEVX_UMEM_REG = (1U << UVERBS_ID_NS_SHIFT), MLX5_IB_METHOD_DEVX_UMEM_DEREG, @@ -173,6 +189,7 @@ enum mlx5_ib_objects { MLX5_IB_OBJECT_DEVX_ASYNC_CMD_FD, MLX5_IB_OBJECT_DEVX_ASYNC_EVENT_FD, MLX5_IB_OBJECT_VAR, + MLX5_IB_OBJECT_PP, }; enum mlx5_ib_flow_matcher_create_attrs { diff --git a/include/uapi/rdma/mlx5_user_ioctl_verbs.h b/include/uapi/rdma/mlx5_user_ioctl_verbs.h index 88b6ca70c2fe..b4641a7865f7 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_verbs.h +++ b/include/uapi/rdma/mlx5_user_ioctl_verbs.h @@ -73,5 +73,9 @@ struct mlx5_ib_uapi_devx_async_event_hdr { __u8 out_data[]; }; +enum mlx5_ib_uapi_pp_alloc_flags { + MLX5_IB_UAPI_PP_ALLOC_FLAGS_DEDICATED_INDEX = 1 << 0, +}; + #endif -- cgit v1.2.3 From ddf0322db79c5984dc1a1db890f946dd19b7d6d9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 23 Feb 2020 16:41:33 -0700 Subject: io_uring: add IORING_OP_PROVIDE_BUFFERS IORING_OP_PROVIDE_BUFFERS uses the buffer registration infrastructure to support passing in an addr/len that is associated with a buffer ID and buffer group ID. The group ID is used to index and lookup the buffers, while the buffer ID can be used to notify the application which buffer in the group was used. The addr passed in is the starting buffer address, and length is each buffer length. A number of buffers to add with can be specified, in which case addr is incremented by length for each addition, and each buffer increments the buffer ID specified. No validation is done of the buffer ID. If the application provides buffers within the same group with identical buffer IDs, then it'll have a hard time telling which buffer ID was used. The only restriction is that the buffer ID can be a max of 16-bits in size, so USHRT_MAX is the maximum ID that can be used. Signed-off-by: Jens Axboe --- fs/io_uring.c | 138 +++++++++++++++++++++++++++++++++++++++++- include/uapi/linux/io_uring.h | 10 ++- 2 files changed, 145 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/io_uring.c b/fs/io_uring.c index 1f3ae208f6a6..1a58f2042815 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -447,6 +447,15 @@ struct io_splice { unsigned int flags; }; +struct io_provide_buf { + struct file *file; + __u64 addr; + __s32 len; + __u32 bgid; + __u16 nbufs; + __u16 bid; +}; + struct io_async_connect { struct sockaddr_storage address; }; @@ -572,6 +581,7 @@ struct io_kiocb { struct io_madvise madvise; struct io_epoll epoll; struct io_splice splice; + struct io_provide_buf pbuf; }; struct io_async_ctx *io; @@ -799,7 +809,8 @@ static const struct io_op_def io_op_defs[] = { .needs_file = 1, .hash_reg_file = 1, .unbound_nonreg_file = 1, - } + }, + [IORING_OP_PROVIDE_BUFFERS] = {}, }; static void io_wq_submit_work(struct io_wq_work **workptr); @@ -2785,6 +2796,120 @@ static int io_openat(struct io_kiocb *req, bool force_nonblock) return io_openat2(req, force_nonblock); } +static int io_provide_buffers_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) +{ + struct io_provide_buf *p = &req->pbuf; + u64 tmp; + + if (sqe->ioprio || sqe->rw_flags) + return -EINVAL; + + tmp = READ_ONCE(sqe->fd); + if (!tmp || tmp > USHRT_MAX) + return -E2BIG; + p->nbufs = tmp; + p->addr = READ_ONCE(sqe->addr); + p->len = READ_ONCE(sqe->len); + + if (!access_ok(u64_to_user_ptr(p->addr), p->len)) + return -EFAULT; + + p->bgid = READ_ONCE(sqe->buf_group); + tmp = READ_ONCE(sqe->off); + if (tmp > USHRT_MAX) + return -E2BIG; + p->bid = tmp; + return 0; +} + +static int io_add_buffers(struct io_provide_buf *pbuf, struct io_buffer **head) +{ + struct io_buffer *buf; + u64 addr = pbuf->addr; + int i, bid = pbuf->bid; + + for (i = 0; i < pbuf->nbufs; i++) { + buf = kmalloc(sizeof(*buf), GFP_KERNEL); + if (!buf) + break; + + buf->addr = addr; + buf->len = pbuf->len; + buf->bid = bid; + addr += pbuf->len; + bid++; + if (!*head) { + INIT_LIST_HEAD(&buf->list); + *head = buf; + } else { + list_add_tail(&buf->list, &(*head)->list); + } + } + + return i ? i : -ENOMEM; +} + +static void io_ring_submit_unlock(struct io_ring_ctx *ctx, bool needs_lock) +{ + if (needs_lock) + mutex_unlock(&ctx->uring_lock); +} + +static void io_ring_submit_lock(struct io_ring_ctx *ctx, bool needs_lock) +{ + /* + * "Normal" inline submissions always hold the uring_lock, since we + * grab it from the system call. Same is true for the SQPOLL offload. + * The only exception is when we've detached the request and issue it + * from an async worker thread, grab the lock for that case. + */ + if (needs_lock) + mutex_lock(&ctx->uring_lock); +} + +static int io_provide_buffers(struct io_kiocb *req, bool force_nonblock) +{ + struct io_provide_buf *p = &req->pbuf; + struct io_ring_ctx *ctx = req->ctx; + struct io_buffer *head, *list; + int ret = 0; + + io_ring_submit_lock(ctx, !force_nonblock); + + lockdep_assert_held(&ctx->uring_lock); + + list = head = idr_find(&ctx->io_buffer_idr, p->bgid); + + ret = io_add_buffers(p, &head); + if (ret < 0) + goto out; + + if (!list) { + ret = idr_alloc(&ctx->io_buffer_idr, head, p->bgid, p->bgid + 1, + GFP_KERNEL); + if (ret < 0) { + while (!list_empty(&head->list)) { + struct io_buffer *buf; + + buf = list_first_entry(&head->list, + struct io_buffer, list); + list_del(&buf->list); + kfree(buf); + } + kfree(head); + goto out; + } + } +out: + io_ring_submit_unlock(ctx, !force_nonblock); + if (ret < 0) + req_set_fail_links(req); + io_cqring_add_event(req, ret); + io_put_req(req); + return 0; +} + static int io_epoll_ctl_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { @@ -4392,6 +4517,9 @@ static int io_req_defer_prep(struct io_kiocb *req, case IORING_OP_SPLICE: ret = io_splice_prep(req, sqe); break; + case IORING_OP_PROVIDE_BUFFERS: + ret = io_provide_buffers_prep(req, sqe); + break; default: printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", req->opcode); @@ -4669,6 +4797,14 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, } ret = io_splice(req, force_nonblock); break; + case IORING_OP_PROVIDE_BUFFERS: + if (sqe) { + ret = io_provide_buffers_prep(req, sqe); + if (ret) + break; + } + ret = io_provide_buffers(req, force_nonblock); + break; default: ret = -EINVAL; break; diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 53b36311cdac..bc34a57a660b 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -45,8 +45,13 @@ struct io_uring_sqe { __u64 user_data; /* data to be passed back at completion time */ union { struct { - /* index into fixed buffers, if used */ - __u16 buf_index; + /* pack this to avoid bogus arm OABI complaints */ + union { + /* index into fixed buffers, if used */ + __u16 buf_index; + /* for grouped buffer selection */ + __u16 buf_group; + } __attribute__((packed)); /* personality to use, if used */ __u16 personality; __s32 splice_fd_in; @@ -119,6 +124,7 @@ enum { IORING_OP_OPENAT2, IORING_OP_EPOLL_CTL, IORING_OP_SPLICE, + IORING_OP_PROVIDE_BUFFERS, /* this goes last, obviously */ IORING_OP_LAST, -- cgit v1.2.3 From bcda7baaa3f15c7a95db3c024bb046d6e298f76b Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 23 Feb 2020 16:42:51 -0700 Subject: io_uring: support buffer selection for OP_READ and OP_RECV If a server process has tons of pending socket connections, generally it uses epoll to wait for activity. When the socket is ready for reading (or writing), the task can select a buffer and issue a recv/send on the given fd. Now that we have fast (non-async thread) support, a task can have tons of pending reads or writes pending. But that means they need buffers to back that data, and if the number of connections is high enough, having them preallocated for all possible connections is unfeasible. With IORING_OP_PROVIDE_BUFFERS, an application can register buffers to use for any request. The request then sets IOSQE_BUFFER_SELECT in the sqe, and a given group ID in sqe->buf_group. When the fd becomes ready, a free buffer from the specified group is selected. If none are available, the request is terminated with -ENOBUFS. If successful, the CQE on completion will contain the buffer ID chosen in the cqe->flags member, encoded as: (buffer_id << IORING_CQE_BUFFER_SHIFT) | IORING_CQE_F_BUFFER; Once a buffer has been consumed by a request, it is no longer available and must be registered again with IORING_OP_PROVIDE_BUFFERS. Requests need to support this feature. For now, IORING_OP_READ and IORING_OP_RECV support it. This is checked on SQE submission, a CQE with res == -EOPNOTSUPP will be posted if attempted on unsupported requests. Signed-off-by: Jens Axboe --- fs/io_uring.c | 224 ++++++++++++++++++++++++++++++++++-------- include/uapi/linux/io_uring.h | 14 +++ 2 files changed, 199 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/fs/io_uring.c b/fs/io_uring.c index 1a58f2042815..a80b5c189c14 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -395,7 +395,9 @@ struct io_sr_msg { void __user *buf; }; int msg_flags; + int bgid; size_t len; + struct io_buffer *kbuf; }; struct io_open { @@ -490,6 +492,7 @@ enum { REQ_F_LINK_BIT = IOSQE_IO_LINK_BIT, REQ_F_HARDLINK_BIT = IOSQE_IO_HARDLINK_BIT, REQ_F_FORCE_ASYNC_BIT = IOSQE_ASYNC_BIT, + REQ_F_BUFFER_SELECT_BIT = IOSQE_BUFFER_SELECT_BIT, REQ_F_LINK_NEXT_BIT, REQ_F_FAIL_LINK_BIT, @@ -506,6 +509,7 @@ enum { REQ_F_NEED_CLEANUP_BIT, REQ_F_OVERFLOW_BIT, REQ_F_POLLED_BIT, + REQ_F_BUFFER_SELECTED_BIT, }; enum { @@ -519,6 +523,8 @@ enum { REQ_F_HARDLINK = BIT(REQ_F_HARDLINK_BIT), /* IOSQE_ASYNC */ REQ_F_FORCE_ASYNC = BIT(REQ_F_FORCE_ASYNC_BIT), + /* IOSQE_BUFFER_SELECT */ + REQ_F_BUFFER_SELECT = BIT(REQ_F_BUFFER_SELECT_BIT), /* already grabbed next link */ REQ_F_LINK_NEXT = BIT(REQ_F_LINK_NEXT_BIT), @@ -550,6 +556,8 @@ enum { REQ_F_OVERFLOW = BIT(REQ_F_OVERFLOW_BIT), /* already went through poll handler */ REQ_F_POLLED = BIT(REQ_F_POLLED_BIT), + /* buffer already selected */ + REQ_F_BUFFER_SELECTED = BIT(REQ_F_BUFFER_SELECTED_BIT), }; struct async_poll { @@ -612,6 +620,7 @@ struct io_kiocb { struct callback_head task_work; struct hlist_node hash_node; struct async_poll *apoll; + int cflags; }; struct io_wq_work work; }; @@ -661,6 +670,8 @@ struct io_op_def { /* set if opcode supports polled "wait" */ unsigned pollin : 1; unsigned pollout : 1; + /* op supports buffer selection */ + unsigned buffer_select : 1; }; static const struct io_op_def io_op_defs[] = { @@ -770,6 +781,7 @@ static const struct io_op_def io_op_defs[] = { .needs_file = 1, .unbound_nonreg_file = 1, .pollin = 1, + .buffer_select = 1, }, [IORING_OP_WRITE] = { .needs_mm = 1, @@ -794,6 +806,7 @@ static const struct io_op_def io_op_defs[] = { .needs_file = 1, .unbound_nonreg_file = 1, .pollin = 1, + .buffer_select = 1, }, [IORING_OP_OPENAT2] = { .needs_file = 1, @@ -1170,7 +1183,7 @@ static bool io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force) if (cqe) { WRITE_ONCE(cqe->user_data, req->user_data); WRITE_ONCE(cqe->res, req->result); - WRITE_ONCE(cqe->flags, 0); + WRITE_ONCE(cqe->flags, req->cflags); } else { WRITE_ONCE(ctx->rings->cq_overflow, atomic_inc_return(&ctx->cached_cq_overflow)); @@ -1194,7 +1207,7 @@ static bool io_cqring_overflow_flush(struct io_ring_ctx *ctx, bool force) return cqe != NULL; } -static void io_cqring_fill_event(struct io_kiocb *req, long res) +static void __io_cqring_fill_event(struct io_kiocb *req, long res, long cflags) { struct io_ring_ctx *ctx = req->ctx; struct io_uring_cqe *cqe; @@ -1210,7 +1223,7 @@ static void io_cqring_fill_event(struct io_kiocb *req, long res) if (likely(cqe)) { WRITE_ONCE(cqe->user_data, req->user_data); WRITE_ONCE(cqe->res, res); - WRITE_ONCE(cqe->flags, 0); + WRITE_ONCE(cqe->flags, cflags); } else if (ctx->cq_overflow_flushed) { WRITE_ONCE(ctx->rings->cq_overflow, atomic_inc_return(&ctx->cached_cq_overflow)); @@ -1222,23 +1235,34 @@ static void io_cqring_fill_event(struct io_kiocb *req, long res) req->flags |= REQ_F_OVERFLOW; refcount_inc(&req->refs); req->result = res; + req->cflags = cflags; list_add_tail(&req->list, &ctx->cq_overflow_list); } } -static void io_cqring_add_event(struct io_kiocb *req, long res) +static void io_cqring_fill_event(struct io_kiocb *req, long res) +{ + __io_cqring_fill_event(req, res, 0); +} + +static void __io_cqring_add_event(struct io_kiocb *req, long res, long cflags) { struct io_ring_ctx *ctx = req->ctx; unsigned long flags; spin_lock_irqsave(&ctx->completion_lock, flags); - io_cqring_fill_event(req, res); + __io_cqring_fill_event(req, res, cflags); io_commit_cqring(ctx); spin_unlock_irqrestore(&ctx->completion_lock, flags); io_cqring_ev_posted(ctx); } +static void io_cqring_add_event(struct io_kiocb *req, long res) +{ + __io_cqring_add_event(req, res, 0); +} + static inline bool io_is_fallback_req(struct io_kiocb *req) { return req == (struct io_kiocb *) @@ -1660,6 +1684,18 @@ static inline bool io_req_multi_free(struct req_batch *rb, struct io_kiocb *req) return true; } +static int io_put_kbuf(struct io_kiocb *req) +{ + struct io_buffer *kbuf = (struct io_buffer *) req->rw.addr; + int cflags; + + cflags = kbuf->bid << IORING_CQE_BUFFER_SHIFT; + cflags |= IORING_CQE_F_BUFFER; + req->rw.addr = 0; + kfree(kbuf); + return cflags; +} + /* * Find and free completed poll iocbs */ @@ -1671,10 +1707,15 @@ static void io_iopoll_complete(struct io_ring_ctx *ctx, unsigned int *nr_events, rb.to_free = rb.need_iter = 0; while (!list_empty(done)) { + int cflags = 0; + req = list_first_entry(done, struct io_kiocb, list); list_del(&req->list); - io_cqring_fill_event(req, req->result); + if (req->flags & REQ_F_BUFFER_SELECTED) + cflags = io_put_kbuf(req); + + __io_cqring_fill_event(req, req->result, cflags); (*nr_events)++; if (refcount_dec_and_test(&req->refs) && @@ -1849,13 +1890,16 @@ static inline void req_set_fail_links(struct io_kiocb *req) static void io_complete_rw_common(struct kiocb *kiocb, long res) { struct io_kiocb *req = container_of(kiocb, struct io_kiocb, rw.kiocb); + int cflags = 0; if (kiocb->ki_flags & IOCB_WRITE) kiocb_end_write(req); if (res != req->result) req_set_fail_links(req); - io_cqring_add_event(req, res); + if (req->flags & REQ_F_BUFFER_SELECTED) + cflags = io_put_kbuf(req); + __io_cqring_add_event(req, res, cflags); } static void io_complete_rw(struct kiocb *kiocb, long res, long res2) @@ -2033,7 +2077,7 @@ static int io_prep_rw(struct io_kiocb *req, const struct io_uring_sqe *sqe, req->rw.addr = READ_ONCE(sqe->addr); req->rw.len = READ_ONCE(sqe->len); - /* we own ->private, reuse it for the buffer index */ + /* we own ->private, reuse it for the buffer index / buffer ID */ req->rw.kiocb.private = (void *) (unsigned long) READ_ONCE(sqe->buf_index); return 0; @@ -2146,8 +2190,61 @@ static ssize_t io_import_fixed(struct io_kiocb *req, int rw, return len; } +static void io_ring_submit_unlock(struct io_ring_ctx *ctx, bool needs_lock) +{ + if (needs_lock) + mutex_unlock(&ctx->uring_lock); +} + +static void io_ring_submit_lock(struct io_ring_ctx *ctx, bool needs_lock) +{ + /* + * "Normal" inline submissions always hold the uring_lock, since we + * grab it from the system call. Same is true for the SQPOLL offload. + * The only exception is when we've detached the request and issue it + * from an async worker thread, grab the lock for that case. + */ + if (needs_lock) + mutex_lock(&ctx->uring_lock); +} + +static struct io_buffer *io_buffer_select(struct io_kiocb *req, size_t *len, + int bgid, struct io_buffer *kbuf, + bool needs_lock) +{ + struct io_buffer *head; + + if (req->flags & REQ_F_BUFFER_SELECTED) + return kbuf; + + io_ring_submit_lock(req->ctx, needs_lock); + + lockdep_assert_held(&req->ctx->uring_lock); + + head = idr_find(&req->ctx->io_buffer_idr, bgid); + if (head) { + if (!list_empty(&head->list)) { + kbuf = list_last_entry(&head->list, struct io_buffer, + list); + list_del(&kbuf->list); + } else { + kbuf = head; + idr_remove(&req->ctx->io_buffer_idr, bgid); + } + if (*len > kbuf->len) + *len = kbuf->len; + } else { + kbuf = ERR_PTR(-ENOBUFS); + } + + io_ring_submit_unlock(req->ctx, needs_lock); + + return kbuf; +} + static ssize_t io_import_iovec(int rw, struct io_kiocb *req, - struct iovec **iovec, struct iov_iter *iter) + struct iovec **iovec, struct iov_iter *iter, + bool needs_lock) { void __user *buf = u64_to_user_ptr(req->rw.addr); size_t sqe_len = req->rw.len; @@ -2159,12 +2256,29 @@ static ssize_t io_import_iovec(int rw, struct io_kiocb *req, return io_import_fixed(req, rw, iter); } - /* buffer index only valid with fixed read/write */ - if (req->rw.kiocb.private) + /* buffer index only valid with fixed read/write, or buffer select */ + if (req->rw.kiocb.private && !(req->flags & REQ_F_BUFFER_SELECT)) return -EINVAL; if (opcode == IORING_OP_READ || opcode == IORING_OP_WRITE) { ssize_t ret; + + if (req->flags & REQ_F_BUFFER_SELECT) { + struct io_buffer *kbuf = (struct io_buffer *) req->rw.addr; + int bgid; + + bgid = (int) (unsigned long) req->rw.kiocb.private; + kbuf = io_buffer_select(req, &sqe_len, bgid, kbuf, + needs_lock); + if (IS_ERR(kbuf)) { + *iovec = NULL; + return PTR_ERR(kbuf); + } + req->rw.addr = (u64) kbuf; + req->flags |= REQ_F_BUFFER_SELECTED; + buf = u64_to_user_ptr(kbuf->addr); + } + ret = import_single_range(rw, buf, sqe_len, *iovec, iter); *iovec = NULL; return ret < 0 ? ret : sqe_len; @@ -2307,7 +2421,7 @@ static int io_read_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, io = req->io; io->rw.iov = io->rw.fast_iov; req->io = NULL; - ret = io_import_iovec(READ, req, &io->rw.iov, &iter); + ret = io_import_iovec(READ, req, &io->rw.iov, &iter, !force_nonblock); req->io = io; if (ret < 0) return ret; @@ -2324,7 +2438,7 @@ static int io_read(struct io_kiocb *req, bool force_nonblock) size_t iov_count; ssize_t io_size, ret; - ret = io_import_iovec(READ, req, &iovec, &iter); + ret = io_import_iovec(READ, req, &iovec, &iter, !force_nonblock); if (ret < 0) return ret; @@ -2396,7 +2510,7 @@ static int io_write_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe, io = req->io; io->rw.iov = io->rw.fast_iov; req->io = NULL; - ret = io_import_iovec(WRITE, req, &io->rw.iov, &iter); + ret = io_import_iovec(WRITE, req, &io->rw.iov, &iter, !force_nonblock); req->io = io; if (ret < 0) return ret; @@ -2413,7 +2527,7 @@ static int io_write(struct io_kiocb *req, bool force_nonblock) size_t iov_count; ssize_t ret, io_size; - ret = io_import_iovec(WRITE, req, &iovec, &iter); + ret = io_import_iovec(WRITE, req, &iovec, &iter, !force_nonblock); if (ret < 0) return ret; @@ -2850,24 +2964,6 @@ static int io_add_buffers(struct io_provide_buf *pbuf, struct io_buffer **head) return i ? i : -ENOMEM; } -static void io_ring_submit_unlock(struct io_ring_ctx *ctx, bool needs_lock) -{ - if (needs_lock) - mutex_unlock(&ctx->uring_lock); -} - -static void io_ring_submit_lock(struct io_ring_ctx *ctx, bool needs_lock) -{ - /* - * "Normal" inline submissions always hold the uring_lock, since we - * grab it from the system call. Same is true for the SQPOLL offload. - * The only exception is when we've detached the request and issue it - * from an async worker thread, grab the lock for that case. - */ - if (needs_lock) - mutex_lock(&ctx->uring_lock); -} - static int io_provide_buffers(struct io_kiocb *req, bool force_nonblock) { struct io_provide_buf *p = &req->pbuf; @@ -3390,6 +3486,27 @@ static int io_send(struct io_kiocb *req, bool force_nonblock) #endif } +static struct io_buffer *io_recv_buffer_select(struct io_kiocb *req, + int *cflags, bool needs_lock) +{ + struct io_sr_msg *sr = &req->sr_msg; + struct io_buffer *kbuf; + + if (!(req->flags & REQ_F_BUFFER_SELECT)) + return NULL; + + kbuf = io_buffer_select(req, &sr->len, sr->bgid, sr->kbuf, needs_lock); + if (IS_ERR(kbuf)) + return kbuf; + + sr->kbuf = kbuf; + req->flags |= REQ_F_BUFFER_SELECTED; + + *cflags = kbuf->bid << IORING_CQE_BUFFER_SHIFT; + *cflags |= IORING_CQE_F_BUFFER; + return kbuf; +} + static int io_recvmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { @@ -3401,6 +3518,7 @@ static int io_recvmsg_prep(struct io_kiocb *req, sr->msg_flags = READ_ONCE(sqe->msg_flags); sr->msg = u64_to_user_ptr(READ_ONCE(sqe->addr)); sr->len = READ_ONCE(sqe->len); + sr->bgid = READ_ONCE(sqe->buf_group); #ifdef CONFIG_COMPAT if (req->ctx->compat) @@ -3490,8 +3608,9 @@ static int io_recvmsg(struct io_kiocb *req, bool force_nonblock) static int io_recv(struct io_kiocb *req, bool force_nonblock) { #if defined(CONFIG_NET) + struct io_buffer *kbuf = NULL; struct socket *sock; - int ret; + int ret, cflags = 0; if (unlikely(req->ctx->flags & IORING_SETUP_IOPOLL)) return -EINVAL; @@ -3499,15 +3618,25 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) sock = sock_from_file(req->file, &ret); if (sock) { struct io_sr_msg *sr = &req->sr_msg; + void __user *buf = sr->buf; struct msghdr msg; struct iovec iov; unsigned flags; - ret = import_single_range(READ, sr->buf, sr->len, &iov, + kbuf = io_recv_buffer_select(req, &cflags, !force_nonblock); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + else if (kbuf) + buf = u64_to_user_ptr(kbuf->addr); + + ret = import_single_range(READ, buf, sr->len, &iov, &msg.msg_iter); - if (ret) + if (ret) { + kfree(kbuf); return ret; + } + req->flags |= REQ_F_NEED_CLEANUP; msg.msg_name = NULL; msg.msg_control = NULL; msg.msg_controllen = 0; @@ -3528,7 +3657,9 @@ static int io_recv(struct io_kiocb *req, bool force_nonblock) ret = -EINTR; } - io_cqring_add_event(req, ret); + kfree(kbuf); + req->flags &= ~REQ_F_NEED_CLEANUP; + __io_cqring_add_event(req, ret, cflags); if (ret < 0) req_set_fail_links(req); io_put_req(req); @@ -4566,6 +4697,9 @@ static void io_cleanup_req(struct io_kiocb *req) case IORING_OP_READV: case IORING_OP_READ_FIXED: case IORING_OP_READ: + if (req->flags & REQ_F_BUFFER_SELECTED) + kfree((void *)(unsigned long)req->rw.addr); + /* fallthrough */ case IORING_OP_WRITEV: case IORING_OP_WRITE_FIXED: case IORING_OP_WRITE: @@ -4577,6 +4711,10 @@ static void io_cleanup_req(struct io_kiocb *req) if (io->msg.iov != io->msg.fast_iov) kfree(io->msg.iov); break; + case IORING_OP_RECV: + if (req->flags & REQ_F_BUFFER_SELECTED) + kfree(req->sr_msg.kbuf); + break; case IORING_OP_OPENAT: case IORING_OP_OPENAT2: case IORING_OP_STATX: @@ -5154,7 +5292,8 @@ static inline void io_queue_link_head(struct io_kiocb *req) } #define SQE_VALID_FLAGS (IOSQE_FIXED_FILE|IOSQE_IO_DRAIN|IOSQE_IO_LINK| \ - IOSQE_IO_HARDLINK | IOSQE_ASYNC) + IOSQE_IO_HARDLINK | IOSQE_ASYNC | \ + IOSQE_BUFFER_SELECT) static bool io_submit_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, struct io_submit_state *state, struct io_kiocb **link) @@ -5171,6 +5310,12 @@ static bool io_submit_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, goto err_req; } + if ((sqe_flags & IOSQE_BUFFER_SELECT) && + !io_op_defs[req->opcode].buffer_select) { + ret = -EOPNOTSUPP; + goto err_req; + } + id = READ_ONCE(sqe->personality); if (id) { req->work.creds = idr_find(&ctx->personality_idr, id); @@ -5183,7 +5328,8 @@ static bool io_submit_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, /* same numerical values with corresponding REQ_F_*, safe to copy */ req->flags |= sqe_flags & (IOSQE_IO_DRAIN | IOSQE_IO_HARDLINK | - IOSQE_ASYNC | IOSQE_FIXED_FILE); + IOSQE_ASYNC | IOSQE_FIXED_FILE | + IOSQE_BUFFER_SELECT); ret = io_req_set_file(state, req, sqe); if (unlikely(ret)) { diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index bc34a57a660b..9b263d9b24e6 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -66,6 +66,7 @@ enum { IOSQE_IO_LINK_BIT, IOSQE_IO_HARDLINK_BIT, IOSQE_ASYNC_BIT, + IOSQE_BUFFER_SELECT_BIT, }; /* @@ -81,6 +82,8 @@ enum { #define IOSQE_IO_HARDLINK (1U << IOSQE_IO_HARDLINK_BIT) /* always go async */ #define IOSQE_ASYNC (1U << IOSQE_ASYNC_BIT) +/* select buffer from sqe->buf_group */ +#define IOSQE_BUFFER_SELECT (1U << IOSQE_BUFFER_SELECT_BIT) /* * io_uring_setup() flags @@ -155,6 +158,17 @@ struct io_uring_cqe { __u32 flags; }; +/* + * cqe->flags + * + * IORING_CQE_F_BUFFER If set, the upper 16 bits are the buffer ID + */ +#define IORING_CQE_F_BUFFER (1U << 0) + +enum { + IORING_CQE_BUFFER_SHIFT = 16, +}; + /* * Magic offsets for the application to mmap the data it needs */ -- cgit v1.2.3 From 0a384abfae66651b28e4bbe16883b1ff046ba3b3 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 27 Feb 2020 08:11:20 -0700 Subject: net: abstract out normal and compat msghdr import This splits it into two parts, one that imports the message, and one that imports the iovec. This allows a caller to only do the first part, and import the iovec manually afterwards. No functional changes in this patch. Acked-by: David Miller Signed-off-by: Jens Axboe --- include/linux/socket.h | 4 ++++ include/net/compat.h | 3 +++ net/compat.c | 30 +++++++++++++++++++++++------- net/socket.c | 25 +++++++++++++++++++++---- 4 files changed, 51 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/socket.h b/include/linux/socket.h index 2d2313403101..fc59ac825561 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -391,6 +391,10 @@ extern int recvmsg_copy_msghdr(struct msghdr *msg, struct user_msghdr __user *umsg, unsigned flags, struct sockaddr __user **uaddr, struct iovec **iov); +extern int __copy_msghdr_from_user(struct msghdr *kmsg, + struct user_msghdr __user *umsg, + struct sockaddr __user **save_addr, + struct iovec __user **uiov, size_t *nsegs); /* helpers which do the actual work for syscalls */ extern int __sys_recvfrom(int fd, void __user *ubuf, size_t size, diff --git a/include/net/compat.h b/include/net/compat.h index f277653c7e17..e341260642fe 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -38,6 +38,9 @@ struct compat_cmsghdr { #define compat_mmsghdr mmsghdr #endif /* defined(CONFIG_COMPAT) */ +int __get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg, + struct sockaddr __user **save_addr, compat_uptr_t *ptr, + compat_size_t *len); int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *, struct sockaddr __user **, struct iovec **); struct sock_fprog __user *get_compat_bpf_fprog(char __user *optval); diff --git a/net/compat.c b/net/compat.c index 47d99c784947..4bed96e84d9a 100644 --- a/net/compat.c +++ b/net/compat.c @@ -33,10 +33,10 @@ #include #include -int get_compat_msghdr(struct msghdr *kmsg, - struct compat_msghdr __user *umsg, - struct sockaddr __user **save_addr, - struct iovec **iov) +int __get_compat_msghdr(struct msghdr *kmsg, + struct compat_msghdr __user *umsg, + struct sockaddr __user **save_addr, + compat_uptr_t *ptr, compat_size_t *len) { struct compat_msghdr msg; ssize_t err; @@ -79,10 +79,26 @@ int get_compat_msghdr(struct msghdr *kmsg, return -EMSGSIZE; kmsg->msg_iocb = NULL; + *ptr = msg.msg_iov; + *len = msg.msg_iovlen; + return 0; +} + +int get_compat_msghdr(struct msghdr *kmsg, + struct compat_msghdr __user *umsg, + struct sockaddr __user **save_addr, + struct iovec **iov) +{ + compat_uptr_t ptr; + compat_size_t len; + ssize_t err; + + err = __get_compat_msghdr(kmsg, umsg, save_addr, &ptr, &len); + if (err) + return err; - err = compat_import_iovec(save_addr ? READ : WRITE, - compat_ptr(msg.msg_iov), msg.msg_iovlen, - UIO_FASTIOV, iov, &kmsg->msg_iter); + err = compat_import_iovec(save_addr ? READ : WRITE, compat_ptr(ptr), + len, UIO_FASTIOV, iov, &kmsg->msg_iter); return err < 0 ? err : 0; } diff --git a/net/socket.c b/net/socket.c index b79a05de7c6e..70ede74ab24b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2226,10 +2226,10 @@ struct used_address { unsigned int name_len; }; -static int copy_msghdr_from_user(struct msghdr *kmsg, - struct user_msghdr __user *umsg, - struct sockaddr __user **save_addr, - struct iovec **iov) +int __copy_msghdr_from_user(struct msghdr *kmsg, + struct user_msghdr __user *umsg, + struct sockaddr __user **save_addr, + struct iovec __user **uiov, size_t *nsegs) { struct user_msghdr msg; ssize_t err; @@ -2271,6 +2271,23 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, return -EMSGSIZE; kmsg->msg_iocb = NULL; + *uiov = msg.msg_iov; + *nsegs = msg.msg_iovlen; + return 0; +} + +static int copy_msghdr_from_user(struct msghdr *kmsg, + struct user_msghdr __user *umsg, + struct sockaddr __user **save_addr, + struct iovec **iov) +{ + struct user_msghdr msg; + ssize_t err; + + err = __copy_msghdr_from_user(kmsg, umsg, save_addr, &msg.msg_iov, + &msg.msg_iovlen); + if (err) + return err; err = import_iovec(save_addr ? READ : WRITE, msg.msg_iov, msg.msg_iovlen, -- cgit v1.2.3 From 067524e914cb23e20d59480b318fe2625eaee7c8 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 2 Mar 2020 16:32:28 -0700 Subject: io_uring: provide means of removing buffers We have IORING_OP_PROVIDE_BUFFERS, but the only way to remove buffers is to trigger IO on them. The usual case of shrinking a buffer pool would be to just not replenish the buffers when IO completes, and instead just free it. But it may be nice to have a way to manually remove a number of buffers from a given group, and IORING_OP_REMOVE_BUFFERS provides that functionality. Signed-off-by: Jens Axboe --- fs/io_uring.c | 102 ++++++++++++++++++++++++++++++++++-------- include/uapi/linux/io_uring.h | 1 + 2 files changed, 84 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/fs/io_uring.c b/fs/io_uring.c index 455d53fd840f..f131105fa12a 100644 --- a/fs/io_uring.c +++ b/fs/io_uring.c @@ -827,6 +827,7 @@ static const struct io_op_def io_op_defs[] = { .unbound_nonreg_file = 1, }, [IORING_OP_PROVIDE_BUFFERS] = {}, + [IORING_OP_REMOVE_BUFFERS] = {}, }; static void io_wq_submit_work(struct io_wq_work **workptr); @@ -2995,6 +2996,75 @@ static int io_openat(struct io_kiocb *req, bool force_nonblock) return io_openat2(req, force_nonblock); } +static int io_remove_buffers_prep(struct io_kiocb *req, + const struct io_uring_sqe *sqe) +{ + struct io_provide_buf *p = &req->pbuf; + u64 tmp; + + if (sqe->ioprio || sqe->rw_flags || sqe->addr || sqe->len || sqe->off) + return -EINVAL; + + tmp = READ_ONCE(sqe->fd); + if (!tmp || tmp > USHRT_MAX) + return -EINVAL; + + memset(p, 0, sizeof(*p)); + p->nbufs = tmp; + p->bgid = READ_ONCE(sqe->buf_group); + return 0; +} + +static int __io_remove_buffers(struct io_ring_ctx *ctx, struct io_buffer *buf, + int bgid, unsigned nbufs) +{ + unsigned i = 0; + + /* shouldn't happen */ + if (!nbufs) + return 0; + + /* the head kbuf is the list itself */ + while (!list_empty(&buf->list)) { + struct io_buffer *nxt; + + nxt = list_first_entry(&buf->list, struct io_buffer, list); + list_del(&nxt->list); + kfree(nxt); + if (++i == nbufs) + return i; + } + i++; + kfree(buf); + idr_remove(&ctx->io_buffer_idr, bgid); + + return i; +} + +static int io_remove_buffers(struct io_kiocb *req, bool force_nonblock) +{ + struct io_provide_buf *p = &req->pbuf; + struct io_ring_ctx *ctx = req->ctx; + struct io_buffer *head; + int ret = 0; + + io_ring_submit_lock(ctx, !force_nonblock); + + lockdep_assert_held(&ctx->uring_lock); + + ret = -ENOENT; + head = idr_find(&ctx->io_buffer_idr, p->bgid); + if (head) + ret = __io_remove_buffers(ctx, head, p->bgid, p->nbufs); + + io_ring_submit_lock(ctx, !force_nonblock); + if (ret < 0) + req_set_fail_links(req); + io_cqring_add_event(req, ret); + io_put_req(req); + return 0; +} + static int io_provide_buffers_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) { @@ -3070,15 +3140,7 @@ static int io_provide_buffers(struct io_kiocb *req, bool force_nonblock) ret = idr_alloc(&ctx->io_buffer_idr, head, p->bgid, p->bgid + 1, GFP_KERNEL); if (ret < 0) { - while (!list_empty(&head->list)) { - struct io_buffer *buf; - - buf = list_first_entry(&head->list, - struct io_buffer, list); - list_del(&buf->list); - kfree(buf); - } - kfree(head); + __io_remove_buffers(ctx, head, p->bgid, -1U); goto out; } } @@ -4825,6 +4887,9 @@ static int io_req_defer_prep(struct io_kiocb *req, case IORING_OP_PROVIDE_BUFFERS: ret = io_provide_buffers_prep(req, sqe); break; + case IORING_OP_REMOVE_BUFFERS: + ret = io_remove_buffers_prep(req, sqe); + break; default: printk_once(KERN_WARNING "io_uring: unhandled opcode %d\n", req->opcode); @@ -5120,6 +5185,14 @@ static int io_issue_sqe(struct io_kiocb *req, const struct io_uring_sqe *sqe, } ret = io_provide_buffers(req, force_nonblock); break; + case IORING_OP_REMOVE_BUFFERS: + if (sqe) { + ret = io_remove_buffers_prep(req, sqe); + if (ret) + break; + } + ret = io_remove_buffers(req, force_nonblock); + break; default: ret = -EINVAL; break; @@ -6998,16 +7071,7 @@ static int __io_destroy_buffers(int id, void *p, void *data) struct io_ring_ctx *ctx = data; struct io_buffer *buf = p; - /* the head kbuf is the list itself */ - while (!list_empty(&buf->list)) { - struct io_buffer *nxt; - - nxt = list_first_entry(&buf->list, struct io_buffer, list); - list_del(&nxt->list); - kfree(nxt); - } - kfree(buf); - idr_remove(&ctx->io_buffer_idr, id); + __io_remove_buffers(ctx, buf, id, -1U); return 0; } diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index 9b263d9b24e6..cef4c0c0f26b 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -128,6 +128,7 @@ enum { IORING_OP_EPOLL_CTL, IORING_OP_SPLICE, IORING_OP_PROVIDE_BUFFERS, + IORING_OP_REMOVE_BUFFERS, /* this goes last, obviously */ IORING_OP_LAST, -- cgit v1.2.3 From 41e684ef3f37ce6e5eac3fb5b9c7c1853f4b0447 Mon Sep 17 00:00:00 2001 From: Alex Vesker Date: Thu, 5 Mar 2020 14:38:41 +0200 Subject: IB/mlx5: Replace tunnel mpls capability bits for tunnel_offloads Until now the flex parser capability was used in ib_query_device() to indicate tunnel_offloads_caps support for mpls_over_gre/mpls_over_udp. Newer devices and firmware will have configurations with the flexparser but without mpls support. Testing for the flex parser capability was a mistake, the tunnel_stateless capability was intended for detecting mpls and was introduced at the same time as the flex parser capability. Otherwise userspace will be incorrectly informed that a future device supports MPLS when it does not. Link: https://lore.kernel.org/r/20200305123841.196086-1-leon@kernel.org Cc: # 4.17 Fixes: e818e255a58d ("IB/mlx5: Expose MPLS related tunneling offloads") Signed-off-by: Alex Vesker Reviewed-by: Ariel Levkovich Signed-off-by: Leon Romanovsky Signed-off-by: Jason Gunthorpe --- drivers/infiniband/hw/mlx5/main.c | 6 ++---- include/linux/mlx5/mlx5_ifc.h | 6 +++++- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index eba7604eaa76..9c3993c7e9a1 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1192,12 +1192,10 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, if (MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) resp.tunnel_offloads_caps |= MLX5_IB_TUNNELED_OFFLOADS_GRE; - if (MLX5_CAP_GEN(mdev, flex_parser_protocols) & - MLX5_FLEX_PROTO_CW_MPLS_GRE) + if (MLX5_CAP_ETH(mdev, tunnel_stateless_mpls_over_gre)) resp.tunnel_offloads_caps |= MLX5_IB_TUNNELED_OFFLOADS_MPLS_GRE; - if (MLX5_CAP_GEN(mdev, flex_parser_protocols) & - MLX5_FLEX_PROTO_CW_MPLS_UDP) + if (MLX5_CAP_ETH(mdev, tunnel_stateless_mpls_over_udp)) resp.tunnel_offloads_caps |= MLX5_IB_TUNNELED_OFFLOADS_MPLS_UDP; } diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index f9bcbe653fda..f3a7189f9d6d 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -877,7 +877,11 @@ struct mlx5_ifc_per_protocol_networking_offload_caps_bits { u8 swp_csum[0x1]; u8 swp_lso[0x1]; u8 cqe_checksum_full[0x1]; - u8 reserved_at_24[0x5]; + u8 tunnel_stateless_geneve_tx[0x1]; + u8 tunnel_stateless_mpls_over_udp[0x1]; + u8 tunnel_stateless_mpls_over_gre[0x1]; + u8 tunnel_stateless_vxlan_gpe[0x1]; + u8 tunnel_stateless_ipv4_over_vxlan[0x1]; u8 tunnel_stateless_ip_over_ip[0x1]; u8 reserved_at_2a[0x6]; u8 max_vxlan_udp_ports[0x8]; -- cgit v1.2.3 From e56faff57f0b39661093c00e0262d4ab9088830e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 28 Feb 2020 15:02:03 -0600 Subject: PCI: Add pci_speed_string() Add pci_speed_string() to return a text description of the supplied bus or link speed. The slot code previously used the private pci_bus_speed_strings[] array for this purpose, but adding this interface will enable us to consolidate similar code elsewhere. Export pcie_link_speed[] and pci_speed_string() so they can be used by modules. Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.h | 1 + drivers/pci/probe.c | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/pci/slot.c | 38 +------------------------------------- include/linux/pci.h | 2 +- 4 files changed, 43 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f65912e0f30d..809753b10fad 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -310,6 +310,7 @@ void pci_bus_put(struct pci_bus *bus); (speed) == PCIE_SPEED_2_5GT ? 2500*8/10 : \ 0) +const char *pci_speed_string(enum pci_bus_speed speed); enum pci_bus_speed pcie_get_speed_cap(struct pci_dev *dev); enum pcie_link_width pcie_get_width_cap(struct pci_dev *dev); u32 pcie_bandwidth_capable(struct pci_dev *dev, enum pci_bus_speed *speed, diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 512cb4312ddd..4fc41b71cc95 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -640,6 +640,7 @@ void pci_free_host_bridge(struct pci_host_bridge *bridge) } EXPORT_SYMBOL(pci_free_host_bridge); +/* Indexed by PCI_X_SSTATUS_FREQ (secondary bus mode and frequency) */ static const unsigned char pcix_bus_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCI_SPEED_66MHz_PCIX, /* 1 */ @@ -659,6 +660,7 @@ static const unsigned char pcix_bus_speed[] = { PCI_SPEED_133MHz_PCIX_533 /* F */ }; +/* Indexed by PCI_EXP_LNKCAP_SLS, PCI_EXP_LNKSTA_CLS */ const unsigned char pcie_link_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCIE_SPEED_2_5GT, /* 1 */ @@ -677,6 +679,44 @@ const unsigned char pcie_link_speed[] = { PCI_SPEED_UNKNOWN, /* E */ PCI_SPEED_UNKNOWN /* F */ }; +EXPORT_SYMBOL_GPL(pcie_link_speed); + +const char *pci_speed_string(enum pci_bus_speed speed) +{ + /* Indexed by the pci_bus_speed enum */ + static const char *speed_strings[] = { + "33 MHz PCI", /* 0x00 */ + "66 MHz PCI", /* 0x01 */ + "66 MHz PCI-X", /* 0x02 */ + "100 MHz PCI-X", /* 0x03 */ + "133 MHz PCI-X", /* 0x04 */ + NULL, /* 0x05 */ + NULL, /* 0x06 */ + NULL, /* 0x07 */ + NULL, /* 0x08 */ + "66 MHz PCI-X 266", /* 0x09 */ + "100 MHz PCI-X 266", /* 0x0a */ + "133 MHz PCI-X 266", /* 0x0b */ + "Unknown AGP", /* 0x0c */ + "1x AGP", /* 0x0d */ + "2x AGP", /* 0x0e */ + "4x AGP", /* 0x0f */ + "8x AGP", /* 0x10 */ + "66 MHz PCI-X 533", /* 0x11 */ + "100 MHz PCI-X 533", /* 0x12 */ + "133 MHz PCI-X 533", /* 0x13 */ + "2.5 GT/s PCIe", /* 0x14 */ + "5.0 GT/s PCIe", /* 0x15 */ + "8.0 GT/s PCIe", /* 0x16 */ + "16.0 GT/s PCIe", /* 0x17 */ + "32.0 GT/s PCIe", /* 0x18 */ + }; + + if (speed < ARRAY_SIZE(speed_strings)) + return speed_strings[speed]; + return "Unknown"; +} +EXPORT_SYMBOL_GPL(pci_speed_string); void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) { diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index ae4aa0e1f2f4..cc386ef2fa12 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -49,45 +49,9 @@ static ssize_t address_read_file(struct pci_slot *slot, char *buf) slot->number); } -/* these strings match up with the values in pci_bus_speed */ -static const char *pci_bus_speed_strings[] = { - "33 MHz PCI", /* 0x00 */ - "66 MHz PCI", /* 0x01 */ - "66 MHz PCI-X", /* 0x02 */ - "100 MHz PCI-X", /* 0x03 */ - "133 MHz PCI-X", /* 0x04 */ - NULL, /* 0x05 */ - NULL, /* 0x06 */ - NULL, /* 0x07 */ - NULL, /* 0x08 */ - "66 MHz PCI-X 266", /* 0x09 */ - "100 MHz PCI-X 266", /* 0x0a */ - "133 MHz PCI-X 266", /* 0x0b */ - "Unknown AGP", /* 0x0c */ - "1x AGP", /* 0x0d */ - "2x AGP", /* 0x0e */ - "4x AGP", /* 0x0f */ - "8x AGP", /* 0x10 */ - "66 MHz PCI-X 533", /* 0x11 */ - "100 MHz PCI-X 533", /* 0x12 */ - "133 MHz PCI-X 533", /* 0x13 */ - "2.5 GT/s PCIe", /* 0x14 */ - "5.0 GT/s PCIe", /* 0x15 */ - "8.0 GT/s PCIe", /* 0x16 */ - "16.0 GT/s PCIe", /* 0x17 */ - "32.0 GT/s PCIe", /* 0x18 */ -}; - static ssize_t bus_speed_read(enum pci_bus_speed speed, char *buf) { - const char *speed_string; - - if (speed < ARRAY_SIZE(pci_bus_speed_strings)) - speed_string = pci_bus_speed_strings[speed]; - else - speed_string = "Unknown"; - - return sprintf(buf, "%s\n", speed_string); + return sprintf(buf, "%s\n", pci_speed_string(speed)); } static ssize_t max_speed_read_file(struct pci_slot *slot, char *buf) diff --git a/include/linux/pci.h b/include/linux/pci.h index 3840a541a9de..76f4806a154c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -236,7 +236,7 @@ enum pcie_link_width { PCIE_LNK_WIDTH_UNKNOWN = 0xff, }; -/* Based on the PCI Hotplug Spec, but some values are made up by us */ +/* See matching string table in pci_speed_string() */ enum pci_bus_speed { PCI_SPEED_33MHz = 0x00, PCI_SPEED_66MHz = 0x01, -- cgit v1.2.3 From 62751b6808ec6bdc7622849815943cbc7377262a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Mar 2020 11:11:57 +0100 Subject: flow_offload: use flow_action_for_each in flow_action_mixed_hw_stats_types_check() Instead of manually iterating over entries, use flow_action_for_each helper. Move the helper and wrap it to fit to 80 cols on the way. Signed-off-by: Jiri Pirko Acked-by: Pablo Neira Ayuso Acked-by: Edward Cree Signed-off-by: David S. Miller --- include/net/flow_offload.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 64807aa03cee..891e15055708 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -256,6 +256,11 @@ static inline bool flow_offload_has_one_action(const struct flow_action *action) return action->num_entries == 1; } +#define flow_action_for_each(__i, __act, __actions) \ + for (__i = 0, __act = &(__actions)->entries[0]; \ + __i < (__actions)->num_entries; \ + __act = &(__actions)->entries[++__i]) + static inline bool flow_action_mixed_hw_stats_types_check(const struct flow_action *action, struct netlink_ext_ack *extack) @@ -267,8 +272,7 @@ flow_action_mixed_hw_stats_types_check(const struct flow_action *action, if (flow_offload_has_one_action(action)) return true; - for (i = 0; i < action->num_entries; i++) { - action_entry = &action->entries[i]; + flow_action_for_each(i, action_entry, action) { if (i && action_entry->hw_stats_type != last_hw_stats_type) { NL_SET_ERR_MSG_MOD(extack, "Mixing HW stats types for actions is not supported"); return false; @@ -316,9 +320,6 @@ flow_action_basic_hw_stats_types_check(const struct flow_action *action, return flow_action_hw_stats_types_check(action, extack, 0); } -#define flow_action_for_each(__i, __act, __actions) \ - for (__i = 0, __act = &(__actions)->entries[0]; __i < (__actions)->num_entries; __act = &(__actions)->entries[++__i]) - struct flow_rule { struct flow_match match; struct flow_action action; -- cgit v1.2.3 From a393daa8993fd7d6c9c33110d5dac08bc0dc2696 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Mar 2020 16:49:07 +0100 Subject: flow_offload: fix allowed types check Change the check to see if the passed allowed type bit is enabled. Fixes: 319a1d19471e ("flow_offload: check for basic action hw stats type") Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_offload.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 891e15055708..2fda4178ba35 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -306,7 +306,7 @@ flow_action_hw_stats_types_check(const struct flow_action *action, NL_SET_ERR_MSG_MOD(extack, "Driver supports only default HW stats type \"any\""); return false; } else if (allowed_hw_stats_type != 0 && - action_entry->hw_stats_type != allowed_hw_stats_type) { + !(action_entry->hw_stats_type & allowed_hw_stats_type)) { NL_SET_ERR_MSG_MOD(extack, "Driver does not support selected HW stats type"); return false; } -- cgit v1.2.3 From 42d5fe5f9c19a3c6a74186190936df91dcab4aa4 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Mar 2020 16:49:08 +0100 Subject: flow_offload: turn hw_stats_type into dedicated enum Put the values into enum and add an enum to define the bits. Suggested-by: Edward Cree Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_offload.h | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 2fda4178ba35..6849cb5d4883 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -155,11 +155,21 @@ enum flow_action_mangle_base { FLOW_ACT_MANGLE_HDR_TYPE_UDP, }; -#define FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE BIT(0) -#define FLOW_ACTION_HW_STATS_TYPE_DELAYED BIT(1) -#define FLOW_ACTION_HW_STATS_TYPE_ANY (FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE | \ - FLOW_ACTION_HW_STATS_TYPE_DELAYED) -#define FLOW_ACTION_HW_STATS_TYPE_DISABLED 0 +enum flow_action_hw_stats_type_bit { + FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE_BIT, + FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT, +}; + +enum flow_action_hw_stats_type { + FLOW_ACTION_HW_STATS_TYPE_DISABLED = 0, + FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE = + BIT(FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE_BIT), + FLOW_ACTION_HW_STATS_TYPE_DELAYED = + BIT(FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT), + FLOW_ACTION_HW_STATS_TYPE_ANY = + FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE | + FLOW_ACTION_HW_STATS_TYPE_DELAYED, +}; typedef void (*action_destr)(void *priv); @@ -175,7 +185,7 @@ void flow_action_cookie_destroy(struct flow_action_cookie *cookie); struct flow_action_entry { enum flow_action_id id; - u8 hw_stats_type; + enum flow_action_hw_stats_type hw_stats_type; action_destr destructor; void *destructor_priv; union { -- cgit v1.2.3 From a16fa289843d5d4dd7c4d8eb3b2deb15a9d2180e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Tue, 10 Mar 2020 16:49:09 +0100 Subject: flow_offload: restrict driver to pass one allowed bit to flow_action_hw_stats_types_check() The intention of this helper was to allow driver to specify one type that it supports, so not only "any" value would pass. So make the API more strict and allow driver to pass only 1 bit that is going to be checked. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 ++-- include/net/flow_offload.h | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 33d3e70418fb..f285713def77 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2879,7 +2879,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, return -EINVAL; if (!flow_action_hw_stats_types_check(flow_action, extack, - FLOW_ACTION_HW_STATS_TYPE_DELAYED)) + FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT)) return -EOPNOTSUPP; attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; @@ -3374,7 +3374,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, return -EINVAL; if (!flow_action_hw_stats_types_check(flow_action, extack, - FLOW_ACTION_HW_STATS_TYPE_DELAYED)) + FLOW_ACTION_HW_STATS_TYPE_DELAYED_BIT)) return -EOPNOTSUPP; flow_action_for_each(i, act, flow_action) { diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index 6849cb5d4883..d1b1e4aa310a 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -300,9 +300,10 @@ flow_action_first_entry_get(const struct flow_action *action) } static inline bool -flow_action_hw_stats_types_check(const struct flow_action *action, - struct netlink_ext_ack *extack, - u8 allowed_hw_stats_type) +__flow_action_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack, + bool check_allow_bit, + enum flow_action_hw_stats_type_bit allow_bit) { const struct flow_action_entry *action_entry; @@ -311,23 +312,32 @@ flow_action_hw_stats_types_check(const struct flow_action *action, if (!flow_action_mixed_hw_stats_types_check(action, extack)) return false; action_entry = flow_action_first_entry_get(action); - if (allowed_hw_stats_type == 0 && + if (!check_allow_bit && action_entry->hw_stats_type != FLOW_ACTION_HW_STATS_TYPE_ANY) { NL_SET_ERR_MSG_MOD(extack, "Driver supports only default HW stats type \"any\""); return false; - } else if (allowed_hw_stats_type != 0 && - !(action_entry->hw_stats_type & allowed_hw_stats_type)) { + } else if (check_allow_bit && + !(action_entry->hw_stats_type & BIT(allow_bit))) { NL_SET_ERR_MSG_MOD(extack, "Driver does not support selected HW stats type"); return false; } return true; } +static inline bool +flow_action_hw_stats_types_check(const struct flow_action *action, + struct netlink_ext_ack *extack, + enum flow_action_hw_stats_type_bit allow_bit) +{ + return __flow_action_hw_stats_types_check(action, extack, + true, allow_bit); +} + static inline bool flow_action_basic_hw_stats_types_check(const struct flow_action *action, struct netlink_ext_ack *extack) { - return flow_action_hw_stats_types_check(action, extack, 0); + return __flow_action_hw_stats_types_check(action, extack, false, 0); } struct flow_rule { -- cgit v1.2.3 From 812df69beb86b0e6decbb109ee3fa408dcb7fa5d Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 9 Mar 2020 19:15:06 -0700 Subject: net: liquidio: reject unsupported coalescing params Set ethtool_ops->supported_coalesce_params to let the core reject unsupported coalescing parameters. This driver did not previously reject unsupported parameters. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/cavium/liquidio/lio_ethtool.c | 11 +++++++++++ include/linux/ethtool.h | 5 +++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 2b27e3aad9db..16eebfc52109 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -3097,7 +3097,17 @@ static int lio_set_fecparam(struct net_device *netdev, return 0; } +#define LIO_ETHTOOL_COALESCE (ETHTOOL_COALESCE_RX_USECS | \ + ETHTOOL_COALESCE_MAX_FRAMES | \ + ETHTOOL_COALESCE_USE_ADAPTIVE | \ + ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW | \ + ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW | \ + ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH | \ + ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH | \ + ETHTOOL_COALESCE_PKT_RATE_RX_USECS) + static const struct ethtool_ops lio_ethtool_ops = { + .supported_coalesce_params = LIO_ETHTOOL_COALESCE, .get_link_ksettings = lio_get_link_ksettings, .set_link_ksettings = lio_set_link_ksettings, .get_fecparam = lio_get_fecparam, @@ -3128,6 +3138,7 @@ static const struct ethtool_ops lio_ethtool_ops = { }; static const struct ethtool_ops lio_vf_ethtool_ops = { + .supported_coalesce_params = LIO_ETHTOOL_COALESCE, .get_link_ksettings = lio_get_link_ksettings, .get_link = ethtool_op_get_link, .get_drvinfo = lio_get_vf_drvinfo, diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index e464c946bca4..9efeebde3514 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -211,6 +211,11 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ) #define ETHTOOL_COALESCE_USE_ADAPTIVE \ (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | ETHTOOL_COALESCE_USE_ADAPTIVE_TX) +#define ETHTOOL_COALESCE_PKT_RATE_RX_USECS \ + (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | \ + ETHTOOL_COALESCE_RX_USECS_LOW | ETHTOOL_COALESCE_RX_USECS_HIGH | \ + ETHTOOL_COALESCE_PKT_RATE_LOW | ETHTOOL_COALESCE_PKT_RATE_HIGH | \ + ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL) /** * struct ethtool_ops - optional netdev operations -- cgit v1.2.3 From e937cc1dd7966df33a478943817302502a164e25 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 6 Mar 2020 16:28:37 +0200 Subject: dmaengine: Add basic debugfs support Via the /sys/kernel/debug/dmaengine/summary users can get information about the DMA devices and the used channels. Example output on am654-evm with audio using two channels and after running dmatest on 4 channels: dma0 (285c0000.dma-controller): number of channels: 96 dma1 (31150000.dma-controller): number of channels: 267 dma1chan0 | 2b00000.mcasp:tx dma1chan1 | 2b00000.mcasp:rx dma1chan2 | in-use dma1chan3 | in-use dma1chan4 | in-use dma1chan5 | in-use For slave channels we can show the device and the channel name a given channel is requested. For non slave devices the only information we know is that the channel is in use. DMA drivers can implement the optional dbg_summary_show callback to provide controller specific information instead of the generic one. It is easy to extend the generic dmaengine_summary_show() to print additional information about the used channels. I have taken the idea from gpiolib and clk subsystems. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200306142839.17910-2-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++- include/linux/dmaengine.h | 13 +++++++- 2 files changed, 87 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index c3b1283b6d31..509abc8e8378 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -58,6 +58,65 @@ static DEFINE_IDA(dma_ida); static LIST_HEAD(dma_device_list); static long dmaengine_ref_count; +/* --- debugfs implementation --- */ +#ifdef CONFIG_DEBUG_FS +#include + +static void dmaengine_dbg_summary_show(struct seq_file *s, + struct dma_device *dma_dev) +{ + struct dma_chan *chan; + + list_for_each_entry(chan, &dma_dev->channels, device_node) { + if (chan->client_count) { + seq_printf(s, " %-13s| %s", dma_chan_name(chan), + chan->dbg_client_name ?: "in-use"); + + if (chan->router) + seq_printf(s, " (via router: %s)\n", + dev_name(chan->router->dev)); + else + seq_puts(s, "\n"); + } + } +} + +static int dmaengine_summary_show(struct seq_file *s, void *data) +{ + struct dma_device *dma_dev = NULL; + + mutex_lock(&dma_list_mutex); + list_for_each_entry(dma_dev, &dma_device_list, global_node) { + seq_printf(s, "dma%d (%s): number of channels: %u\n", + dma_dev->dev_id, dev_name(dma_dev->dev), + dma_dev->chancnt); + + if (dma_dev->dbg_summary_show) + dma_dev->dbg_summary_show(s, dma_dev); + else + dmaengine_dbg_summary_show(s, dma_dev); + + if (!list_is_last(&dma_dev->global_node, &dma_device_list)) + seq_puts(s, "\n"); + } + mutex_unlock(&dma_list_mutex); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(dmaengine_summary); + +static void __init dmaengine_debugfs_init(void) +{ + struct dentry *rootdir = debugfs_create_dir("dmaengine", NULL); + + /* /sys/kernel/debug/dmaengine/summary */ + debugfs_create_file("summary", 0444, rootdir, NULL, + &dmaengine_summary_fops); +} +#else +static inline void dmaengine_debugfs_init(void) { } +#endif /* DEBUG_FS */ + /* --- sysfs implementation --- */ #define DMA_SLAVE_NAME "slave" @@ -760,6 +819,11 @@ struct dma_chan *dma_request_chan(struct device *dev, const char *name) return chan ? chan : ERR_PTR(-EPROBE_DEFER); found: +#ifdef CONFIG_DEBUG_FS + chan->dbg_client_name = kasprintf(GFP_KERNEL, "%s:%s", dev_name(dev), + name); +#endif + chan->name = kasprintf(GFP_KERNEL, "dma:%s", name); if (!chan->name) return chan; @@ -837,6 +901,11 @@ void dma_release_channel(struct dma_chan *chan) chan->name = NULL; chan->slave = NULL; } + +#ifdef CONFIG_DEBUG_FS + kfree(chan->dbg_client_name); + chan->dbg_client_name = NULL; +#endif mutex_unlock(&dma_list_mutex); } EXPORT_SYMBOL_GPL(dma_release_channel); @@ -1559,6 +1628,11 @@ static int __init dma_bus_init(void) if (err) return err; - return class_register(&dma_devclass); + + err = class_register(&dma_devclass); + if (!err) + dmaengine_debugfs_init(); + + return err; } arch_initcall(dma_bus_init); diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index d3672f065a64..72920b5cf2d7 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -300,6 +300,8 @@ struct dma_router { * @chan_id: channel ID for sysfs * @dev: class device for sysfs * @name: backlink name for sysfs + * @dbg_client_name: slave name for debugfs in format: + * dev_name(requester's dev):channel name, for example: "2b00000.mcasp:tx" * @device_node: used to add this to the device chan list * @local: per-cpu pointer to a struct dma_chan_percpu * @client_count: how many clients are using this channel @@ -318,6 +320,9 @@ struct dma_chan { int chan_id; struct dma_chan_dev *dev; const char *name; +#ifdef CONFIG_DEBUG_FS + char *dbg_client_name; +#endif struct list_head device_node; struct dma_chan_percpu __percpu *local; @@ -806,7 +811,9 @@ struct dma_filter { * called and there are no further references to this structure. This * must be implemented to free resources however many existing drivers * do not and are therefore not safe to unbind while in use. - * + * @dbg_summary_show: optional routine to show contents in debugfs; default code + * will be used when this is omitted, but custom code can show extra, + * controller specific information. */ struct dma_device { struct kref ref; @@ -892,6 +899,10 @@ struct dma_device { struct dma_tx_state *txstate); void (*device_issue_pending)(struct dma_chan *chan); void (*device_release)(struct dma_device *dev); + /* debugfs support */ +#ifdef CONFIG_DEBUG_FS + void (*dbg_summary_show)(struct seq_file *s, struct dma_device *dev); +#endif }; static inline int dmaengine_slave_config(struct dma_chan *chan, -- cgit v1.2.3 From 26cf132de6f79c06025706ddc61e045d591d404d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 6 Mar 2020 16:28:39 +0200 Subject: dmaengine: Create debug directories for DMA devices Create a placeholder directory for each registered DMA device. DMA drivers can use the dmaengine_get_debugfs_root() call to get their debugfs root and can populate with custom files to aim debugging. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200306142839.17910-4-peter.ujfalusi@ti.com Signed-off-by: Vinod Koul --- drivers/dma/dmaengine.c | 28 +++++++++++++++++++++++++++- drivers/dma/dmaengine.h | 16 ++++++++++++++++ include/linux/dmaengine.h | 1 + 3 files changed, 44 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 509abc8e8378..5a442752e07d 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -62,6 +62,22 @@ static long dmaengine_ref_count; #ifdef CONFIG_DEBUG_FS #include +static struct dentry *rootdir; + +static void dmaengine_debug_register(struct dma_device *dma_dev) +{ + dma_dev->dbg_dev_root = debugfs_create_dir(dev_name(dma_dev->dev), + rootdir); + if (IS_ERR(dma_dev->dbg_dev_root)) + dma_dev->dbg_dev_root = NULL; +} + +static void dmaengine_debug_unregister(struct dma_device *dma_dev) +{ + debugfs_remove_recursive(dma_dev->dbg_dev_root); + dma_dev->dbg_dev_root = NULL; +} + static void dmaengine_dbg_summary_show(struct seq_file *s, struct dma_device *dma_dev) { @@ -107,7 +123,7 @@ DEFINE_SHOW_ATTRIBUTE(dmaengine_summary); static void __init dmaengine_debugfs_init(void) { - struct dentry *rootdir = debugfs_create_dir("dmaengine", NULL); + rootdir = debugfs_create_dir("dmaengine", NULL); /* /sys/kernel/debug/dmaengine/summary */ debugfs_create_file("summary", 0444, rootdir, NULL, @@ -115,6 +131,12 @@ static void __init dmaengine_debugfs_init(void) } #else static inline void dmaengine_debugfs_init(void) { } +static inline int dmaengine_debug_register(struct dma_device *dma_dev) +{ + return 0; +} + +static inline void dmaengine_debug_unregister(struct dma_device *dma_dev) { } #endif /* DEBUG_FS */ /* --- sysfs implementation --- */ @@ -1265,6 +1287,8 @@ int dma_async_device_register(struct dma_device *device) dma_channel_rebalance(); mutex_unlock(&dma_list_mutex); + dmaengine_debug_register(device); + return 0; err_out: @@ -1298,6 +1322,8 @@ void dma_async_device_unregister(struct dma_device *device) { struct dma_chan *chan, *n; + dmaengine_debug_unregister(device); + list_for_each_entry_safe(chan, n, &device->channels, device_node) __dma_async_device_channel_unregister(device, chan); diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h index e8a320c9e57c..1bfbd64b1371 100644 --- a/drivers/dma/dmaengine.h +++ b/drivers/dma/dmaengine.h @@ -182,4 +182,20 @@ dmaengine_desc_callback_valid(struct dmaengine_desc_callback *cb) struct dma_chan *dma_get_slave_channel(struct dma_chan *chan); struct dma_chan *dma_get_any_slave_channel(struct dma_device *device); +#ifdef CONFIG_DEBUG_FS +#include + +static inline struct dentry * +dmaengine_get_debugfs_root(struct dma_device *dma_dev) { + return dma_dev->dbg_dev_root; +} +#else +struct dentry; +static inline struct dentry * +dmaengine_get_debugfs_root(struct dma_device *dma_dev) +{ + return NULL; +} +#endif /* CONFIG_DEBUG_FS */ + #endif diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 72920b5cf2d7..21065c04c4ac 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -902,6 +902,7 @@ struct dma_device { /* debugfs support */ #ifdef CONFIG_DEBUG_FS void (*dbg_summary_show)(struct seq_file *s, struct dma_device *dev); + struct dentry *dbg_dev_root; #endif }; -- cgit v1.2.3 From 7c4a4d088283e02cc04252e88dd08b5cdf54e70f Mon Sep 17 00:00:00 2001 From: Tony Luck Date: Tue, 10 Mar 2020 15:18:02 -0700 Subject: dmaengine: idxd: Merge definition of dsa_batch_desc into dsa_hw_desc We don't need a special structure just for batch descriptors. The layout matches the general form for other descriptors. Merge the desc_list_addr field into the union of other aliases for the the third quadword in the structure. Create a union to alias "xfer_size" with "desc_count". Signed-off-by: Tony Luck Acked-by: Dave Jiang Link: https://lore.kernel.org/r/158387868208.35922.5895104426944263789.stgit@djiang5-desk3.ch.intel.com Signed-off-by: Vinod Koul --- include/uapi/linux/idxd.h | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/idxd.h b/include/uapi/linux/idxd.h index 849ef1515d04..1f412fbf561b 100644 --- a/include/uapi/linux/idxd.h +++ b/include/uapi/linux/idxd.h @@ -83,21 +83,6 @@ enum dsa_completion_status { #define DSA_COMP_STATUS_MASK 0x7f #define DSA_COMP_STATUS_WRITE 0x80 -struct dsa_batch_desc { - uint32_t pasid:20; - uint32_t rsvd:11; - uint32_t priv:1; - uint32_t flags:24; - uint32_t opcode:8; - uint64_t completion_addr; - uint64_t desc_list_addr; - uint64_t rsvd1; - uint32_t desc_count; - uint16_t interrupt_handle; - uint16_t rsvd2; - uint8_t rsvd3[24]; -} __attribute__((packed)); - struct dsa_hw_desc { uint32_t pasid:20; uint32_t rsvd:11; @@ -109,6 +94,7 @@ struct dsa_hw_desc { uint64_t src_addr; uint64_t rdback_addr; uint64_t pattern; + uint64_t desc_list_addr; }; union { uint64_t dst_addr; @@ -116,7 +102,10 @@ struct dsa_hw_desc { uint64_t src2_addr; uint64_t comp_pattern; }; - uint32_t xfer_size; + union { + uint32_t xfer_size; + uint32_t desc_count; + }; uint16_t int_handle; uint16_t rsvd1; union { -- cgit v1.2.3 From ee22d0c5e88b82780f143e5bb449ad597b31f444 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Date: Tue, 3 Mar 2020 23:40:48 +0530 Subject: soc/tegra: bpmp: Update ABI header Update the firmware header to support uninitialization of UPHY PLL when the PCIe controller is operating in endpoint mode and host cuts the PCIe reference clock. Signed-off-by: Vidya Sagar Signed-off-by: Lorenzo Pieralisi Acked-by: Thierry Reding --- include/soc/tegra/bpmp-abi.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h index cac6f610b3fe..8f8e73e5cd45 100644 --- a/include/soc/tegra/bpmp-abi.h +++ b/include/soc/tegra/bpmp-abi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2014-2018, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved. */ #ifndef _ABI_BPMP_ABI_H_ @@ -2119,6 +2119,7 @@ enum { CMD_UPHY_PCIE_LANE_MARGIN_STATUS = 2, CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT = 3, CMD_UPHY_PCIE_CONTROLLER_STATE = 4, + CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF = 5, CMD_UPHY_MAX, }; @@ -2151,6 +2152,11 @@ struct cmd_uphy_pcie_controller_state_request { uint8_t enable; } __ABI_PACKED; +struct cmd_uphy_ep_controller_pll_off_request { + /** @brief EP controller number, valid: 0, 4, 5 */ + uint8_t ep_controller; +} __ABI_PACKED; + /** * @ingroup UPHY * @brief Request with #MRQ_UPHY @@ -2165,6 +2171,7 @@ struct cmd_uphy_pcie_controller_state_request { * |CMD_UPHY_PCIE_LANE_MARGIN_STATUS | | * |CMD_UPHY_PCIE_EP_CONTROLLER_PLL_INIT |cmd_uphy_ep_controller_pll_init_request | * |CMD_UPHY_PCIE_CONTROLLER_STATE |cmd_uphy_pcie_controller_state_request | + * |CMD_UPHY_PCIE_EP_CONTROLLER_PLL_OFF |cmd_uphy_ep_controller_pll_off_request | * */ @@ -2178,6 +2185,7 @@ struct mrq_uphy_request { struct cmd_uphy_margin_control_request uphy_set_margin_control; struct cmd_uphy_ep_controller_pll_init_request ep_ctrlr_pll_init; struct cmd_uphy_pcie_controller_state_request controller_state; + struct cmd_uphy_ep_controller_pll_off_request ep_ctrlr_pll_off; } __UNION_ANON; } __ABI_PACKED; -- cgit v1.2.3 From babf3164095b0670435910340c2a1eec37757b57 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Mon, 9 Mar 2020 16:10:51 -0700 Subject: bpf: Add bpf_link_new_file that doesn't install FD Add bpf_link_new_file() API for cases when we need to ensure anon_inode is successfully created before we proceed with expensive BPF program attachment procedure, which will require equally (if not more so) expensive and potentially failing compensation detachment procedure just because anon_inode creation failed. This API allows to simplify code by ensuring first that anon_inode is created and after BPF program is attached proceed with fd_install() that can't fail. After anon_inode file is created, link can't be just kfree()'d anymore, because its destruction will be performed by deferred file_operations->release call. For this, bpf_link API required specifying two separate operations: release() and dealloc(), former performing detachment only, while the latter frees memory used by bpf_link itself. dealloc() needs to be specified, because struct bpf_link is frequently embedded into link type-specific container struct (e.g., struct bpf_raw_tp_link), so bpf_link itself doesn't know how to properly free the memory. In case when anon_inode file was successfully created, but subsequent BPF attachment failed, bpf_link needs to be marked as "defunct", so that file's release() callback will perform only memory deallocation, but no detachment. Convert raw tracepoint and tracing attachment to new API and eliminate detachment from error handling path. Signed-off-by: Andrii Nakryiko Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Link: https://lore.kernel.org/bpf/20200309231051.1270337-1-andriin@fb.com --- include/linux/bpf.h | 3 ++ kernel/bpf/syscall.c | 122 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 91 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 94a329b9da81..4fd91b7c95ea 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1070,13 +1070,16 @@ struct bpf_link; struct bpf_link_ops { void (*release)(struct bpf_link *link); + void (*dealloc)(struct bpf_link *link); }; void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, struct bpf_prog *prog); +void bpf_link_defunct(struct bpf_link *link); void bpf_link_inc(struct bpf_link *link); void bpf_link_put(struct bpf_link *link); int bpf_link_new_fd(struct bpf_link *link); +struct file *bpf_link_new_file(struct bpf_link *link, int *reserved_fd); struct bpf_link *bpf_link_get_from_fd(u32 ufd); int bpf_obj_pin_user(u32 ufd, const char __user *pathname); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 7ce0815793dd..b2f73ecacced 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2188,6 +2188,11 @@ void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, link->prog = prog; } +void bpf_link_defunct(struct bpf_link *link) +{ + link->prog = NULL; +} + void bpf_link_inc(struct bpf_link *link) { atomic64_inc(&link->refcnt); @@ -2196,14 +2201,13 @@ void bpf_link_inc(struct bpf_link *link) /* bpf_link_free is guaranteed to be called from process context */ static void bpf_link_free(struct bpf_link *link) { - struct bpf_prog *prog; - - /* remember prog locally, because release below will free link memory */ - prog = link->prog; - /* extra clean up and kfree of container link struct */ - link->ops->release(link); - /* no more accesing of link members after this point */ - bpf_prog_put(prog); + if (link->prog) { + /* detach BPF program, clean up used resources */ + link->ops->release(link); + bpf_prog_put(link->prog); + } + /* free bpf_link and its containing memory */ + link->ops->dealloc(link); } static void bpf_link_put_deferred(struct work_struct *work) @@ -2281,6 +2285,33 @@ int bpf_link_new_fd(struct bpf_link *link) return anon_inode_getfd("bpf-link", &bpf_link_fops, link, O_CLOEXEC); } +/* Similar to bpf_link_new_fd, create anon_inode for given bpf_link, but + * instead of immediately installing fd in fdtable, just reserve it and + * return. Caller then need to either install it with fd_install(fd, file) or + * release with put_unused_fd(fd). + * This is useful for cases when bpf_link attachment/detachment are + * complicated and expensive operations and should be delayed until all the fd + * reservation and anon_inode creation succeeds. + */ +struct file *bpf_link_new_file(struct bpf_link *link, int *reserved_fd) +{ + struct file *file; + int fd; + + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) + return ERR_PTR(fd); + + file = anon_inode_getfile("bpf_link", &bpf_link_fops, link, O_CLOEXEC); + if (IS_ERR(file)) { + put_unused_fd(fd); + return file; + } + + *reserved_fd = fd; + return file; +} + struct bpf_link *bpf_link_get_from_fd(u32 ufd) { struct fd f = fdget(ufd); @@ -2305,21 +2336,27 @@ struct bpf_tracing_link { }; static void bpf_tracing_link_release(struct bpf_link *link) +{ + WARN_ON_ONCE(bpf_trampoline_unlink_prog(link->prog)); +} + +static void bpf_tracing_link_dealloc(struct bpf_link *link) { struct bpf_tracing_link *tr_link = container_of(link, struct bpf_tracing_link, link); - WARN_ON_ONCE(bpf_trampoline_unlink_prog(link->prog)); kfree(tr_link); } static const struct bpf_link_ops bpf_tracing_link_lops = { .release = bpf_tracing_link_release, + .dealloc = bpf_tracing_link_dealloc, }; static int bpf_tracing_prog_attach(struct bpf_prog *prog) { struct bpf_tracing_link *link; + struct file *link_file; int link_fd, err; if (prog->expected_attach_type != BPF_TRACE_FENTRY && @@ -2337,20 +2374,24 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) } bpf_link_init(&link->link, &bpf_tracing_link_lops, prog); - err = bpf_trampoline_link_prog(prog); - if (err) - goto out_free_link; + link_file = bpf_link_new_file(&link->link, &link_fd); + if (IS_ERR(link_file)) { + kfree(link); + err = PTR_ERR(link_file); + goto out_put_prog; + } - link_fd = bpf_link_new_fd(&link->link); - if (link_fd < 0) { - WARN_ON_ONCE(bpf_trampoline_unlink_prog(prog)); - err = link_fd; - goto out_free_link; + err = bpf_trampoline_link_prog(prog); + if (err) { + bpf_link_defunct(&link->link); + fput(link_file); + put_unused_fd(link_fd); + goto out_put_prog; } + + fd_install(link_fd, link_file); return link_fd; -out_free_link: - kfree(link); out_put_prog: bpf_prog_put(prog); return err; @@ -2368,19 +2409,28 @@ static void bpf_raw_tp_link_release(struct bpf_link *link) bpf_probe_unregister(raw_tp->btp, raw_tp->link.prog); bpf_put_raw_tracepoint(raw_tp->btp); +} + +static void bpf_raw_tp_link_dealloc(struct bpf_link *link) +{ + struct bpf_raw_tp_link *raw_tp = + container_of(link, struct bpf_raw_tp_link, link); + kfree(raw_tp); } static const struct bpf_link_ops bpf_raw_tp_lops = { .release = bpf_raw_tp_link_release, + .dealloc = bpf_raw_tp_link_dealloc, }; #define BPF_RAW_TRACEPOINT_OPEN_LAST_FIELD raw_tracepoint.prog_fd static int bpf_raw_tracepoint_open(const union bpf_attr *attr) { - struct bpf_raw_tp_link *raw_tp; + struct bpf_raw_tp_link *link; struct bpf_raw_event_map *btp; + struct file *link_file; struct bpf_prog *prog; const char *tp_name; char buf[128]; @@ -2431,28 +2481,32 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) goto out_put_prog; } - raw_tp = kzalloc(sizeof(*raw_tp), GFP_USER); - if (!raw_tp) { + link = kzalloc(sizeof(*link), GFP_USER); + if (!link) { err = -ENOMEM; goto out_put_btp; } - bpf_link_init(&raw_tp->link, &bpf_raw_tp_lops, prog); - raw_tp->btp = btp; + bpf_link_init(&link->link, &bpf_raw_tp_lops, prog); + link->btp = btp; - err = bpf_probe_register(raw_tp->btp, prog); - if (err) - goto out_free_tp; + link_file = bpf_link_new_file(&link->link, &link_fd); + if (IS_ERR(link_file)) { + kfree(link); + err = PTR_ERR(link_file); + goto out_put_btp; + } - link_fd = bpf_link_new_fd(&raw_tp->link); - if (link_fd < 0) { - bpf_probe_unregister(raw_tp->btp, prog); - err = link_fd; - goto out_free_tp; + err = bpf_probe_register(link->btp, prog); + if (err) { + bpf_link_defunct(&link->link); + fput(link_file); + put_unused_fd(link_fd); + goto out_put_btp; } + + fd_install(link_fd, link_file); return link_fd; -out_free_tp: - kfree(raw_tp); out_put_btp: bpf_put_raw_tracepoint(btp); out_put_prog: -- cgit v1.2.3 From bbbdeb4720a0759ec90e3bcb20ad28d19e531346 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 11 Mar 2020 07:45:46 -0600 Subject: io_uring: dual license io_uring.h uapi header This just syncs the header it with the liburing version, so there's no confusion on the license of the header parts. Signed-off-by: Jens Axboe --- include/uapi/linux/io_uring.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/io_uring.h b/include/uapi/linux/io_uring.h index cef4c0c0f26b..6d9d2b1cc523 100644 --- a/include/uapi/linux/io_uring.h +++ b/include/uapi/linux/io_uring.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note OR MIT */ /* * Header file for the io_uring interface. * -- cgit v1.2.3 From 46b5889cc2c54bac7d7e727a44d28a298df23cef Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Tue, 14 Jan 2020 10:09:52 +0100 Subject: mtd: implement proper partition handling Instead of collecting partitions in a flat list, create a hierarchy within the mtd_info structure: use a partitions list to keep track of the partitions of an MTD device (which might be itself a partition of another MTD device), a pointer to the parent device (NULL when the MTD device is the root one, not a partition). By also saving directly in mtd_info the offset of the partition, we can get rid of the mtd_part structure. While at it, be consistent in the naming of the mtd_info structures to ease the understanding of the new hierarchy: these structures are usually called 'mtd', unless there are multiple instances of the same structure. In this case, there is usually a parent/child bound so we will call them 'parent' and 'child'. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200114090952.11232-1-miquel.raynal@bootlin.com --- drivers/mtd/mtdchar.c | 12 +- drivers/mtd/mtdcore.c | 250 ++++++++++----- drivers/mtd/mtdpart.c | 695 +++++++++++------------------------------ include/linux/mtd/mtd.h | 125 +++++++- include/linux/mtd/partitions.h | 1 - 5 files changed, 478 insertions(+), 605 deletions(-) (limited to 'include') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index b841008a9eb7..c5935b2f9cd1 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -349,6 +349,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, uint64_t start, uint32_t length, void __user *ptr, uint32_t __user *retp) { + struct mtd_info *master = mtd_get_master(mtd); struct mtd_file_info *mfi = file->private_data; struct mtd_oob_ops ops = {}; uint32_t retlen; @@ -360,7 +361,7 @@ static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd, if (length > 4096) return -EINVAL; - if (!mtd->_write_oob) + if (!master->_write_oob) return -EOPNOTSUPP; ops.ooblen = length; @@ -586,6 +587,7 @@ static int mtdchar_blkpg_ioctl(struct mtd_info *mtd, static int mtdchar_write_ioctl(struct mtd_info *mtd, struct mtd_write_req __user *argp) { + struct mtd_info *master = mtd_get_master(mtd); struct mtd_write_req req; struct mtd_oob_ops ops = {}; const void __user *usr_data, *usr_oob; @@ -597,9 +599,8 @@ static int mtdchar_write_ioctl(struct mtd_info *mtd, usr_data = (const void __user *)(uintptr_t)req.usr_data; usr_oob = (const void __user *)(uintptr_t)req.usr_oob; - if (!mtd->_write_oob) + if (!master->_write_oob) return -EOPNOTSUPP; - ops.mode = req.mode; ops.len = (size_t)req.len; ops.ooblen = (size_t)req.ooblen; @@ -635,6 +636,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; + struct mtd_info *master = mtd_get_master(mtd); void __user *argp = (void __user *)arg; int ret = 0; struct mtd_info_user info; @@ -824,7 +826,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_oobinfo oi; - if (!mtd->ooblayout) + if (!master->ooblayout) return -EOPNOTSUPP; ret = get_oobinfo(mtd, &oi); @@ -918,7 +920,7 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg) { struct nand_ecclayout_user *usrlay; - if (!mtd->ooblayout) + if (!master->ooblayout) return -EOPNOTSUPP; usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 5fac4355b9c2..2916674208b3 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -456,13 +456,14 @@ static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state, int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, struct mtd_pairing_info *info) { - int npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd); + struct mtd_info *master = mtd_get_master(mtd); + int npairs = mtd_wunit_per_eb(master) / mtd_pairing_groups(master); if (wunit < 0 || wunit >= npairs) return -EINVAL; - if (mtd->pairing && mtd->pairing->get_info) - return mtd->pairing->get_info(mtd, wunit, info); + if (master->pairing && master->pairing->get_info) + return master->pairing->get_info(master, wunit, info); info->group = 0; info->pair = wunit; @@ -498,15 +499,16 @@ EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info); int mtd_pairing_info_to_wunit(struct mtd_info *mtd, const struct mtd_pairing_info *info) { - int ngroups = mtd_pairing_groups(mtd); - int npairs = mtd_wunit_per_eb(mtd) / ngroups; + struct mtd_info *master = mtd_get_master(mtd); + int ngroups = mtd_pairing_groups(master); + int npairs = mtd_wunit_per_eb(master) / ngroups; if (!info || info->pair < 0 || info->pair >= npairs || info->group < 0 || info->group >= ngroups) return -EINVAL; - if (mtd->pairing && mtd->pairing->get_wunit) - return mtd->pairing->get_wunit(mtd, info); + if (master->pairing && master->pairing->get_wunit) + return mtd->pairing->get_wunit(master, info); return info->pair; } @@ -524,10 +526,12 @@ EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit); */ int mtd_pairing_groups(struct mtd_info *mtd) { - if (!mtd->pairing || !mtd->pairing->ngroups) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->pairing || !master->pairing->ngroups) return 1; - return mtd->pairing->ngroups; + return master->pairing->ngroups; } EXPORT_SYMBOL_GPL(mtd_pairing_groups); @@ -587,6 +591,7 @@ static int mtd_nvmem_add(struct mtd_info *mtd) int add_mtd_device(struct mtd_info *mtd) { + struct mtd_info *master = mtd_get_master(mtd); struct mtd_notifier *not; int i, error; @@ -608,7 +613,7 @@ int add_mtd_device(struct mtd_info *mtd) (mtd->_read && mtd->_read_oob))) return -EINVAL; - if (WARN_ON((!mtd->erasesize || !mtd->_erase) && + if (WARN_ON((!mtd->erasesize || !master->_erase) && !(mtd->flags & MTD_NO_ERASE))) return -EINVAL; @@ -765,7 +770,8 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd) pr_debug("mtd device won't show a device symlink in sysfs\n"); } - mtd->orig_flags = mtd->flags; + INIT_LIST_HEAD(&mtd->partitions); + mutex_init(&mtd->master.partitions_lock); } /** @@ -971,20 +977,26 @@ EXPORT_SYMBOL_GPL(get_mtd_device); int __get_mtd_device(struct mtd_info *mtd) { + struct mtd_info *master = mtd_get_master(mtd); int err; - if (!try_module_get(mtd->owner)) + if (!try_module_get(master->owner)) return -ENODEV; - if (mtd->_get_device) { - err = mtd->_get_device(mtd); + if (master->_get_device) { + err = master->_get_device(mtd); if (err) { - module_put(mtd->owner); + module_put(master->owner); return err; } } - mtd->usecount++; + + while (mtd->parent) { + mtd->usecount++; + mtd = mtd->parent; + } + return 0; } EXPORT_SYMBOL_GPL(__get_mtd_device); @@ -1038,13 +1050,18 @@ EXPORT_SYMBOL_GPL(put_mtd_device); void __put_mtd_device(struct mtd_info *mtd) { - --mtd->usecount; - BUG_ON(mtd->usecount < 0); + struct mtd_info *master = mtd_get_master(mtd); - if (mtd->_put_device) - mtd->_put_device(mtd); + while (mtd->parent) { + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + mtd = mtd->parent; + } + + if (master->_put_device) + master->_put_device(master); - module_put(mtd->owner); + module_put(master->owner); } EXPORT_SYMBOL_GPL(__put_mtd_device); @@ -1055,9 +1072,13 @@ EXPORT_SYMBOL_GPL(__put_mtd_device); */ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) { + struct mtd_info *master = mtd_get_master(mtd); + u64 mst_ofs = mtd_get_master_ofs(mtd, 0); + int ret; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; - if (!mtd->erasesize || !mtd->_erase) + if (!mtd->erasesize || !master->_erase) return -ENOTSUPP; if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr) @@ -1069,7 +1090,14 @@ int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; ledtrig_mtd_activity(); - return mtd->_erase(mtd, instr); + + instr->addr += mst_ofs; + ret = master->_erase(master, instr); + if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) + instr->fail_addr -= mst_ofs; + + instr->addr -= mst_ofs; + return ret; } EXPORT_SYMBOL_GPL(mtd_erase); @@ -1079,30 +1107,36 @@ EXPORT_SYMBOL_GPL(mtd_erase); int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; *virt = NULL; if (phys) *phys = 0; - if (!mtd->_point) + if (!master->_point) return -EOPNOTSUPP; if (from < 0 || from >= mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_point(mtd, from, len, retlen, virt, phys); + + from = mtd_get_master_ofs(mtd, from); + return master->_point(master, from, len, retlen, virt, phys); } EXPORT_SYMBOL_GPL(mtd_point); /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len) { - if (!mtd->_unpoint) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_unpoint) return -EOPNOTSUPP; if (from < 0 || from >= mtd->size || len > mtd->size - from) return -EINVAL; if (!len) return 0; - return mtd->_unpoint(mtd, from, len); + return master->_unpoint(master, mtd_get_master_ofs(mtd, from), len); } EXPORT_SYMBOL_GPL(mtd_unpoint); @@ -1129,6 +1163,25 @@ unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len, } EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); +static void mtd_update_ecc_stats(struct mtd_info *mtd, struct mtd_info *master, + const struct mtd_ecc_stats *old_stats) +{ + struct mtd_ecc_stats diff; + + if (master == mtd) + return; + + diff = master->ecc_stats; + diff.failed -= old_stats->failed; + diff.corrected -= old_stats->corrected; + + while (mtd->parent) { + mtd->ecc_stats.failed += diff.failed; + mtd->ecc_stats.corrected += diff.corrected; + mtd = mtd->parent; + } +} + int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -1171,8 +1224,10 @@ EXPORT_SYMBOL_GPL(mtd_write); int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; - if (!mtd->_panic_write) + if (!master->_panic_write) return -EOPNOTSUPP; if (to < 0 || to >= mtd->size || len > mtd->size - to) return -EINVAL; @@ -1183,7 +1238,8 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, if (!mtd->oops_panic_write) mtd->oops_panic_write = true; - return mtd->_panic_write(mtd, to, len, retlen, buf); + return master->_panic_write(master, mtd_get_master_ofs(mtd, to), len, + retlen, buf); } EXPORT_SYMBOL_GPL(mtd_panic_write); @@ -1222,7 +1278,10 @@ static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs, int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + struct mtd_info *master = mtd_get_master(mtd); + struct mtd_ecc_stats old_stats = master->ecc_stats; int ret_code; + ops->retlen = ops->oobretlen = 0; ret_code = mtd_check_oob_ops(mtd, from, ops); @@ -1232,14 +1291,17 @@ int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) ledtrig_mtd_activity(); /* Check the validity of a potential fallback on mtd->_read */ - if (!mtd->_read_oob && (!mtd->_read || ops->oobbuf)) + if (!master->_read_oob && (!master->_read || ops->oobbuf)) return -EOPNOTSUPP; - if (mtd->_read_oob) - ret_code = mtd->_read_oob(mtd, from, ops); + from = mtd_get_master_ofs(mtd, from); + if (master->_read_oob) + ret_code = master->_read_oob(master, from, ops); else - ret_code = mtd->_read(mtd, from, ops->len, &ops->retlen, - ops->datbuf); + ret_code = master->_read(master, from, ops->len, &ops->retlen, + ops->datbuf); + + mtd_update_ecc_stats(mtd, master, &old_stats); /* * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics @@ -1258,6 +1320,7 @@ EXPORT_SYMBOL_GPL(mtd_read_oob); int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { + struct mtd_info *master = mtd_get_master(mtd); int ret; ops->retlen = ops->oobretlen = 0; @@ -1272,14 +1335,16 @@ int mtd_write_oob(struct mtd_info *mtd, loff_t to, ledtrig_mtd_activity(); /* Check the validity of a potential fallback on mtd->_write */ - if (!mtd->_write_oob && (!mtd->_write || ops->oobbuf)) + if (!master->_write_oob && (!master->_write || ops->oobbuf)) return -EOPNOTSUPP; - if (mtd->_write_oob) - return mtd->_write_oob(mtd, to, ops); + to = mtd_get_master_ofs(mtd, to); + + if (master->_write_oob) + return master->_write_oob(master, to, ops); else - return mtd->_write(mtd, to, ops->len, &ops->retlen, - ops->datbuf); + return master->_write(master, to, ops->len, &ops->retlen, + ops->datbuf); } EXPORT_SYMBOL_GPL(mtd_write_oob); @@ -1302,15 +1367,17 @@ EXPORT_SYMBOL_GPL(mtd_write_oob); int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc) { + struct mtd_info *master = mtd_get_master(mtd); + memset(oobecc, 0, sizeof(*oobecc)); - if (!mtd || section < 0) + if (!master || section < 0) return -EINVAL; - if (!mtd->ooblayout || !mtd->ooblayout->ecc) + if (!master->ooblayout || !master->ooblayout->ecc) return -ENOTSUPP; - return mtd->ooblayout->ecc(mtd, section, oobecc); + return master->ooblayout->ecc(master, section, oobecc); } EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); @@ -1334,15 +1401,17 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc); int mtd_ooblayout_free(struct mtd_info *mtd, int section, struct mtd_oob_region *oobfree) { + struct mtd_info *master = mtd_get_master(mtd); + memset(oobfree, 0, sizeof(*oobfree)); - if (!mtd || section < 0) + if (!master || section < 0) return -EINVAL; - if (!mtd->ooblayout || !mtd->ooblayout->free) + if (!master->ooblayout || !master->ooblayout->free) return -ENOTSUPP; - return mtd->ooblayout->free(mtd, section, oobfree); + return master->ooblayout->free(master, section, oobfree); } EXPORT_SYMBOL_GPL(mtd_ooblayout_free); @@ -1651,60 +1720,69 @@ EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes); int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - if (!mtd->_get_fact_prot_info) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_get_fact_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_fact_prot_info(mtd, len, retlen, buf); + return master->_get_fact_prot_info(master, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info); int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; - if (!mtd->_read_fact_prot_reg) + if (!master->_read_fact_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); + return master->_read_fact_prot_reg(master, from, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg); int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf) { - if (!mtd->_get_user_prot_info) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_get_user_prot_info) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_get_user_prot_info(mtd, len, retlen, buf); + return master->_get_user_prot_info(master, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_get_user_prot_info); int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; - if (!mtd->_read_user_prot_reg) + if (!master->_read_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); + return master->_read_user_prot_reg(master, from, len, retlen, buf); } EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg); int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, u_char *buf) { + struct mtd_info *master = mtd_get_master(mtd); int ret; *retlen = 0; - if (!mtd->_write_user_prot_reg) + if (!master->_write_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); + ret = master->_write_user_prot_reg(master, to, len, retlen, buf); if (ret) return ret; @@ -1718,80 +1796,105 @@ EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg); int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { - if (!mtd->_lock_user_prot_reg) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_lock_user_prot_reg) return -EOPNOTSUPP; if (!len) return 0; - return mtd->_lock_user_prot_reg(mtd, from, len); + return master->_lock_user_prot_reg(master, from, len); } EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg); /* Chip-supported device locking */ int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - if (!mtd->_lock) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_lock) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs) return -EINVAL; if (!len) return 0; - return mtd->_lock(mtd, ofs, len); + return master->_lock(master, mtd_get_master_ofs(mtd, ofs), len); } EXPORT_SYMBOL_GPL(mtd_lock); int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - if (!mtd->_unlock) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_unlock) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs) return -EINVAL; if (!len) return 0; - return mtd->_unlock(mtd, ofs, len); + return master->_unlock(master, mtd_get_master_ofs(mtd, ofs), len); } EXPORT_SYMBOL_GPL(mtd_unlock); int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) { - if (!mtd->_is_locked) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_is_locked) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs) return -EINVAL; if (!len) return 0; - return mtd->_is_locked(mtd, ofs, len); + return master->_is_locked(master, mtd_get_master_ofs(mtd, ofs), len); } EXPORT_SYMBOL_GPL(mtd_is_locked); int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs) { + struct mtd_info *master = mtd_get_master(mtd); + if (ofs < 0 || ofs >= mtd->size) return -EINVAL; - if (!mtd->_block_isreserved) + if (!master->_block_isreserved) return 0; - return mtd->_block_isreserved(mtd, ofs); + return master->_block_isreserved(master, mtd_get_master_ofs(mtd, ofs)); } EXPORT_SYMBOL_GPL(mtd_block_isreserved); int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) { + struct mtd_info *master = mtd_get_master(mtd); + if (ofs < 0 || ofs >= mtd->size) return -EINVAL; - if (!mtd->_block_isbad) + if (!master->_block_isbad) return 0; - return mtd->_block_isbad(mtd, ofs); + return master->_block_isbad(master, mtd_get_master_ofs(mtd, ofs)); } EXPORT_SYMBOL_GPL(mtd_block_isbad); int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) { - if (!mtd->_block_markbad) + struct mtd_info *master = mtd_get_master(mtd); + int ret; + + if (!master->_block_markbad) return -EOPNOTSUPP; if (ofs < 0 || ofs >= mtd->size) return -EINVAL; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - return mtd->_block_markbad(mtd, ofs); + + ret = master->_block_markbad(master, mtd_get_master_ofs(mtd, ofs)); + if (ret) + return ret; + + while (mtd->parent) { + mtd->ecc_stats.badblocks++; + mtd = mtd->parent; + } + + return 0; } EXPORT_SYMBOL_GPL(mtd_block_markbad); @@ -1841,12 +1944,17 @@ static int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { + struct mtd_info *master = mtd_get_master(mtd); + *retlen = 0; if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - if (!mtd->_writev) + + if (!master->_writev) return default_mtd_writev(mtd, vecs, count, to, retlen); - return mtd->_writev(mtd, vecs, count, to, retlen); + + return master->_writev(master, vecs, count, + mtd_get_master_ofs(mtd, to), retlen); } EXPORT_SYMBOL_GPL(mtd_writev); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 7328c066c5ba..3f6025684f58 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -20,339 +20,52 @@ #include "mtdcore.h" -/* Our partition linked list */ -static LIST_HEAD(mtd_partitions); -static DEFINE_MUTEX(mtd_partitions_mutex); - -/** - * struct mtd_part - our partition node structure - * - * @mtd: struct holding partition details - * @parent: parent mtd - flash device or another partition - * @offset: partition offset relative to the *flash device* - */ -struct mtd_part { - struct mtd_info mtd; - struct mtd_info *parent; - uint64_t offset; - struct list_head list; -}; - -/* - * Given a pointer to the MTD object in the mtd_part structure, we can retrieve - * the pointer to that structure. - */ -static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd) -{ - return container_of(mtd, struct mtd_part, mtd); -} - -static u64 part_absolute_offset(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - - if (!mtd_is_partition(mtd)) - return 0; - - return part_absolute_offset(part->parent) + part->offset; -} - /* * MTD methods which simply translate the effective address and pass through * to the _real_ device. */ -static int part_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - struct mtd_ecc_stats stats; - int res; - - stats = part->parent->ecc_stats; - res = part->parent->_read(part->parent, from + part->offset, len, - retlen, buf); - if (unlikely(mtd_is_eccerr(res))) - mtd->ecc_stats.failed += - part->parent->ecc_stats.failed - stats.failed; - else - mtd->ecc_stats.corrected += - part->parent->ecc_stats.corrected - stats.corrected; - return res; -} - -static int part_point(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, void **virt, resource_size_t *phys) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_point(part->parent, from + part->offset, len, - retlen, virt, phys); -} - -static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_unpoint(part->parent, from + part->offset, len); -} - -static int part_read_oob(struct mtd_info *mtd, loff_t from, - struct mtd_oob_ops *ops) -{ - struct mtd_part *part = mtd_to_part(mtd); - struct mtd_ecc_stats stats; - int res; - - stats = part->parent->ecc_stats; - res = part->parent->_read_oob(part->parent, from + part->offset, ops); - if (unlikely(mtd_is_eccerr(res))) - mtd->ecc_stats.failed += - part->parent->ecc_stats.failed - stats.failed; - else - mtd->ecc_stats.corrected += - part->parent->ecc_stats.corrected - stats.corrected; - return res; -} - -static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_read_user_prot_reg(part->parent, from, len, - retlen, buf); -} - -static int part_get_user_prot_info(struct mtd_info *mtd, size_t len, - size_t *retlen, struct otp_info *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_get_user_prot_info(part->parent, len, retlen, - buf); -} - -static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_read_fact_prot_reg(part->parent, from, len, - retlen, buf); -} - -static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len, - size_t *retlen, struct otp_info *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_get_fact_prot_info(part->parent, len, retlen, - buf); -} - -static int part_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_write(part->parent, to + part->offset, len, - retlen, buf); -} - -static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_panic_write(part->parent, to + part->offset, len, - retlen, buf); -} - -static int part_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_write_oob(part->parent, to + part->offset, ops); -} - -static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len, size_t *retlen, u_char *buf) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_write_user_prot_reg(part->parent, from, len, - retlen, buf); -} - -static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, - size_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_lock_user_prot_reg(part->parent, from, len); -} - -static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, - unsigned long count, loff_t to, size_t *retlen) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_writev(part->parent, vecs, count, - to + part->offset, retlen); -} - -static int part_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct mtd_part *part = mtd_to_part(mtd); - int ret; - - instr->addr += part->offset; - ret = part->parent->_erase(part->parent, instr); - if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN) - instr->fail_addr -= part->offset; - instr->addr -= part->offset; - - return ret; -} - -static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_lock(part->parent, ofs + part->offset, len); -} - -static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_unlock(part->parent, ofs + part->offset, len); -} - -static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_is_locked(part->parent, ofs + part->offset, len); -} - -static void part_sync(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - part->parent->_sync(part->parent); -} - -static int part_suspend(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_suspend(part->parent); -} - -static void part_resume(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - part->parent->_resume(part->parent); -} - -static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_part *part = mtd_to_part(mtd); - ofs += part->offset; - return part->parent->_block_isreserved(part->parent, ofs); -} - -static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_part *part = mtd_to_part(mtd); - ofs += part->offset; - return part->parent->_block_isbad(part->parent, ofs); -} - -static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) -{ - struct mtd_part *part = mtd_to_part(mtd); - int res; - - ofs += part->offset; - res = part->parent->_block_markbad(part->parent, ofs); - if (!res) - mtd->ecc_stats.badblocks++; - return res; -} - -static int part_get_device(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - return part->parent->_get_device(part->parent); -} - -static void part_put_device(struct mtd_info *mtd) -{ - struct mtd_part *part = mtd_to_part(mtd); - part->parent->_put_device(part->parent); -} - -static int part_ooblayout_ecc(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return mtd_ooblayout_ecc(part->parent, section, oobregion); -} - -static int part_ooblayout_free(struct mtd_info *mtd, int section, - struct mtd_oob_region *oobregion) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return mtd_ooblayout_free(part->parent, section, oobregion); -} - -static const struct mtd_ooblayout_ops part_ooblayout_ops = { - .ecc = part_ooblayout_ecc, - .free = part_ooblayout_free, -}; - -static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - struct mtd_part *part = mtd_to_part(mtd); - - return part->parent->_max_bad_blocks(part->parent, - ofs + part->offset, len); -} - -static inline void free_partition(struct mtd_part *p) +static inline void free_partition(struct mtd_info *mtd) { - kfree(p->mtd.name); - kfree(p); + kfree(mtd->name); + kfree(mtd); } -static struct mtd_part *allocate_partition(struct mtd_info *parent, - const struct mtd_partition *part, int partno, - uint64_t cur_offset) +static struct mtd_info *allocate_partition(struct mtd_info *parent, + const struct mtd_partition *part, + int partno, uint64_t cur_offset) { int wr_alignment = (parent->flags & MTD_NO_ERASE) ? parent->writesize : parent->erasesize; - struct mtd_part *slave; + struct mtd_info *child, *master = mtd_get_master(parent); u32 remainder; char *name; u64 tmp; /* allocate the partition structure */ - slave = kzalloc(sizeof(*slave), GFP_KERNEL); + child = kzalloc(sizeof(*child), GFP_KERNEL); name = kstrdup(part->name, GFP_KERNEL); - if (!name || !slave) { + if (!name || !child) { printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", parent->name); kfree(name); - kfree(slave); + kfree(child); return ERR_PTR(-ENOMEM); } /* set up the MTD object for this partition */ - slave->mtd.type = parent->type; - slave->mtd.flags = parent->orig_flags & ~part->mask_flags; - slave->mtd.orig_flags = slave->mtd.flags; - slave->mtd.size = part->size; - slave->mtd.writesize = parent->writesize; - slave->mtd.writebufsize = parent->writebufsize; - slave->mtd.oobsize = parent->oobsize; - slave->mtd.oobavail = parent->oobavail; - slave->mtd.subpage_sft = parent->subpage_sft; - slave->mtd.pairing = parent->pairing; - - slave->mtd.name = name; - slave->mtd.owner = parent->owner; + child->type = parent->type; + child->part.flags = parent->flags & ~part->mask_flags; + child->flags = child->part.flags; + child->size = part->size; + child->writesize = parent->writesize; + child->writebufsize = parent->writebufsize; + child->oobsize = parent->oobsize; + child->oobavail = parent->oobavail; + child->subpage_sft = parent->subpage_sft; + + child->name = name; + child->owner = parent->owner; /* NOTE: Historically, we didn't arrange MTDs as a tree out of * concern for showing the same data in multiple partitions. @@ -360,134 +73,76 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, * so the MTD_PARTITIONED_MASTER option allows that. The master * will have device nodes etc only if this is set, so make the * parent conditional on that option. Note, this is a way to - * distinguish between the master and the partition in sysfs. + * distinguish between the parent and its partitions in sysfs. */ - slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ? - &parent->dev : - parent->dev.parent; - slave->mtd.dev.of_node = part->of_node; - - if (parent->_read) - slave->mtd._read = part_read; - if (parent->_write) - slave->mtd._write = part_write; - - if (parent->_panic_write) - slave->mtd._panic_write = part_panic_write; - - if (parent->_point && parent->_unpoint) { - slave->mtd._point = part_point; - slave->mtd._unpoint = part_unpoint; - } - - if (parent->_read_oob) - slave->mtd._read_oob = part_read_oob; - if (parent->_write_oob) - slave->mtd._write_oob = part_write_oob; - if (parent->_read_user_prot_reg) - slave->mtd._read_user_prot_reg = part_read_user_prot_reg; - if (parent->_read_fact_prot_reg) - slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg; - if (parent->_write_user_prot_reg) - slave->mtd._write_user_prot_reg = part_write_user_prot_reg; - if (parent->_lock_user_prot_reg) - slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg; - if (parent->_get_user_prot_info) - slave->mtd._get_user_prot_info = part_get_user_prot_info; - if (parent->_get_fact_prot_info) - slave->mtd._get_fact_prot_info = part_get_fact_prot_info; - if (parent->_sync) - slave->mtd._sync = part_sync; - if (!partno && !parent->dev.class && parent->_suspend && - parent->_resume) { - slave->mtd._suspend = part_suspend; - slave->mtd._resume = part_resume; - } - if (parent->_writev) - slave->mtd._writev = part_writev; - if (parent->_lock) - slave->mtd._lock = part_lock; - if (parent->_unlock) - slave->mtd._unlock = part_unlock; - if (parent->_is_locked) - slave->mtd._is_locked = part_is_locked; - if (parent->_block_isreserved) - slave->mtd._block_isreserved = part_block_isreserved; - if (parent->_block_isbad) - slave->mtd._block_isbad = part_block_isbad; - if (parent->_block_markbad) - slave->mtd._block_markbad = part_block_markbad; - if (parent->_max_bad_blocks) - slave->mtd._max_bad_blocks = part_max_bad_blocks; - - if (parent->_get_device) - slave->mtd._get_device = part_get_device; - if (parent->_put_device) - slave->mtd._put_device = part_put_device; - - slave->mtd._erase = part_erase; - slave->parent = parent; - slave->offset = part->offset; - - if (slave->offset == MTDPART_OFS_APPEND) - slave->offset = cur_offset; - if (slave->offset == MTDPART_OFS_NXTBLK) { + child->dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ? + &parent->dev : parent->dev.parent; + child->dev.of_node = part->of_node; + child->parent = parent; + child->part.offset = part->offset; + INIT_LIST_HEAD(&child->partitions); + + if (child->part.offset == MTDPART_OFS_APPEND) + child->part.offset = cur_offset; + if (child->part.offset == MTDPART_OFS_NXTBLK) { tmp = cur_offset; - slave->offset = cur_offset; + child->part.offset = cur_offset; remainder = do_div(tmp, wr_alignment); if (remainder) { - slave->offset += wr_alignment - remainder; + child->part.offset += wr_alignment - remainder; printk(KERN_NOTICE "Moving partition %d: " "0x%012llx -> 0x%012llx\n", partno, - (unsigned long long)cur_offset, (unsigned long long)slave->offset); + (unsigned long long)cur_offset, + child->part.offset); } } - if (slave->offset == MTDPART_OFS_RETAIN) { - slave->offset = cur_offset; - if (parent->size - slave->offset >= slave->mtd.size) { - slave->mtd.size = parent->size - slave->offset - - slave->mtd.size; + if (child->part.offset == MTDPART_OFS_RETAIN) { + child->part.offset = cur_offset; + if (parent->size - child->part.offset >= child->size) { + child->size = parent->size - child->part.offset - + child->size; } else { printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n", - part->name, parent->size - slave->offset, - slave->mtd.size); + part->name, parent->size - child->part.offset, + child->size); /* register to preserve ordering */ goto out_register; } } - if (slave->mtd.size == MTDPART_SIZ_FULL) - slave->mtd.size = parent->size - slave->offset; + if (child->size == MTDPART_SIZ_FULL) + child->size = parent->size - child->part.offset; - printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset, - (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name); + printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", + child->part.offset, child->part.offset + child->size, + child->name); /* let's do some sanity checks */ - if (slave->offset >= parent->size) { + if (child->part.offset >= parent->size) { /* let's register it anyway to preserve ordering */ - slave->offset = 0; - slave->mtd.size = 0; + child->part.offset = 0; + child->size = 0; /* Initialize ->erasesize to make add_mtd_device() happy. */ - slave->mtd.erasesize = parent->erasesize; - + child->erasesize = parent->erasesize; printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", part->name); goto out_register; } - if (slave->offset + slave->mtd.size > parent->size) { - slave->mtd.size = parent->size - slave->offset; + if (child->part.offset + child->size > parent->size) { + child->size = parent->size - child->part.offset; printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n", - part->name, parent->name, (unsigned long long)slave->mtd.size); + part->name, parent->name, child->size); } if (parent->numeraseregions > 1) { /* Deal with variable erase size stuff */ int i, max = parent->numeraseregions; - u64 end = slave->offset + slave->mtd.size; + u64 end = child->part.offset + child->size; struct mtd_erase_region_info *regions = parent->eraseregions; /* Find the first erase regions which is part of this * partition. */ - for (i = 0; i < max && regions[i].offset <= slave->offset; i++) + for (i = 0; i < max && regions[i].offset <= child->part.offset; + i++) ; /* The loop searched for the region _behind_ the first one */ if (i > 0) @@ -495,70 +150,68 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent, /* Pick biggest erasesize */ for (; i < max && regions[i].offset < end; i++) { - if (slave->mtd.erasesize < regions[i].erasesize) { - slave->mtd.erasesize = regions[i].erasesize; - } + if (child->erasesize < regions[i].erasesize) + child->erasesize = regions[i].erasesize; } - BUG_ON(slave->mtd.erasesize == 0); + BUG_ON(child->erasesize == 0); } else { /* Single erase size */ - slave->mtd.erasesize = parent->erasesize; + child->erasesize = parent->erasesize; } /* - * Slave erasesize might differ from the master one if the master + * Child erasesize might differ from the parent one if the parent * exposes several regions with different erasesize. Adjust * wr_alignment accordingly. */ - if (!(slave->mtd.flags & MTD_NO_ERASE)) - wr_alignment = slave->mtd.erasesize; + if (!(child->flags & MTD_NO_ERASE)) + wr_alignment = child->erasesize; - tmp = part_absolute_offset(parent) + slave->offset; + tmp = mtd_get_master_ofs(child, 0); remainder = do_div(tmp, wr_alignment); - if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { + if ((child->flags & MTD_WRITEABLE) && remainder) { /* Doesn't start on a boundary of major erase size */ /* FIXME: Let it be writable if it is on a boundary of * _minor_ erase size though */ - slave->mtd.flags &= ~MTD_WRITEABLE; + child->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n", part->name); } - tmp = part_absolute_offset(parent) + slave->mtd.size; + tmp = mtd_get_master_ofs(child, 0) + child->size; remainder = do_div(tmp, wr_alignment); - if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) { - slave->mtd.flags &= ~MTD_WRITEABLE; + if ((child->flags & MTD_WRITEABLE) && remainder) { + child->flags &= ~MTD_WRITEABLE; printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n", part->name); } - mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops); - slave->mtd.ecc_step_size = parent->ecc_step_size; - slave->mtd.ecc_strength = parent->ecc_strength; - slave->mtd.bitflip_threshold = parent->bitflip_threshold; + child->ecc_step_size = parent->ecc_step_size; + child->ecc_strength = parent->ecc_strength; + child->bitflip_threshold = parent->bitflip_threshold; - if (parent->_block_isbad) { + if (master->_block_isbad) { uint64_t offs = 0; - while (offs < slave->mtd.size) { - if (mtd_block_isreserved(parent, offs + slave->offset)) - slave->mtd.ecc_stats.bbtblocks++; - else if (mtd_block_isbad(parent, offs + slave->offset)) - slave->mtd.ecc_stats.badblocks++; - offs += slave->mtd.erasesize; + while (offs < child->size) { + if (mtd_block_isreserved(child, offs)) + child->ecc_stats.bbtblocks++; + else if (mtd_block_isbad(child, offs)) + child->ecc_stats.badblocks++; + offs += child->erasesize; } } out_register: - return slave; + return child; } static ssize_t mtd_partition_offset_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mtd_info *mtd = dev_get_drvdata(dev); - struct mtd_part *part = mtd_to_part(mtd); - return snprintf(buf, PAGE_SIZE, "%llu\n", part->offset); + + return snprintf(buf, PAGE_SIZE, "%lld\n", mtd->part.offset); } static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL); @@ -568,9 +221,9 @@ static const struct attribute *mtd_partition_attrs[] = { NULL }; -static int mtd_add_partition_attrs(struct mtd_part *new) +static int mtd_add_partition_attrs(struct mtd_info *new) { - int ret = sysfs_create_files(&new->mtd.dev.kobj, mtd_partition_attrs); + int ret = sysfs_create_files(&new->dev.kobj, mtd_partition_attrs); if (ret) printk(KERN_WARNING "mtd: failed to create partition attrs, err=%d\n", ret); @@ -580,8 +233,9 @@ static int mtd_add_partition_attrs(struct mtd_part *new) int mtd_add_partition(struct mtd_info *parent, const char *name, long long offset, long long length) { + struct mtd_info *master = mtd_get_master(parent); struct mtd_partition part; - struct mtd_part *new; + struct mtd_info *child; int ret = 0; /* the direct offset is expected */ @@ -600,28 +254,28 @@ int mtd_add_partition(struct mtd_info *parent, const char *name, part.size = length; part.offset = offset; - new = allocate_partition(parent, &part, -1, offset); - if (IS_ERR(new)) - return PTR_ERR(new); + child = allocate_partition(parent, &part, -1, offset); + if (IS_ERR(child)) + return PTR_ERR(child); - mutex_lock(&mtd_partitions_mutex); - list_add(&new->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_add_tail(&child->part.node, &parent->partitions); + mutex_unlock(&master->master.partitions_lock); - ret = add_mtd_device(&new->mtd); + ret = add_mtd_device(child); if (ret) goto err_remove_part; - mtd_add_partition_attrs(new); + mtd_add_partition_attrs(child); return 0; err_remove_part: - mutex_lock(&mtd_partitions_mutex); - list_del(&new->list); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_del(&child->part.node); + mutex_unlock(&master->master.partitions_lock); - free_partition(new); + free_partition(child); return ret; } @@ -630,119 +284,142 @@ EXPORT_SYMBOL_GPL(mtd_add_partition); /** * __mtd_del_partition - delete MTD partition * - * @priv: internal MTD struct for partition to be deleted + * @priv: MTD structure to be deleted * * This function must be called with the partitions mutex locked. */ -static int __mtd_del_partition(struct mtd_part *priv) +static int __mtd_del_partition(struct mtd_info *mtd) { - struct mtd_part *child, *next; + struct mtd_info *child, *next; int err; - list_for_each_entry_safe(child, next, &mtd_partitions, list) { - if (child->parent == &priv->mtd) { - err = __mtd_del_partition(child); - if (err) - return err; - } + list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { + err = __mtd_del_partition(child); + if (err) + return err; } - sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs); + sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs); - err = del_mtd_device(&priv->mtd); + err = del_mtd_device(mtd); if (err) return err; - list_del(&priv->list); - free_partition(priv); + list_del(&child->part.node); + free_partition(mtd); return 0; } /* * This function unregisters and destroy all slave MTD objects which are - * attached to the given MTD object. + * attached to the given MTD object, recursively. */ -int del_mtd_partitions(struct mtd_info *mtd) +static int __del_mtd_partitions(struct mtd_info *mtd) { - struct mtd_part *slave, *next; + struct mtd_info *child, *next; + LIST_HEAD(tmp_list); int ret, err = 0; - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if (slave->parent == mtd) { - ret = __mtd_del_partition(slave); - if (ret < 0) - err = ret; + list_for_each_entry_safe(child, next, &mtd->partitions, part.node) { + if (mtd_has_partitions(child)) + del_mtd_partitions(child); + + pr_info("Deleting %s MTD partition\n", child->name); + ret = del_mtd_device(child); + if (ret < 0) { + pr_err("Error when deleting partition \"%s\" (%d)\n", + child->name, ret); + err = ret; + continue; } - mutex_unlock(&mtd_partitions_mutex); + + list_del(&child->part.node); + free_partition(child); + } return err; } +int del_mtd_partitions(struct mtd_info *mtd) +{ + struct mtd_info *master = mtd_get_master(mtd); + int ret; + + pr_info("Deleting MTD partitions on \"%s\":\n", mtd->name); + + mutex_lock(&master->master.partitions_lock); + ret = __del_mtd_partitions(mtd); + mutex_unlock(&master->master.partitions_lock); + + return ret; +} + int mtd_del_partition(struct mtd_info *mtd, int partno) { - struct mtd_part *slave, *next; + struct mtd_info *child, *master = mtd_get_master(mtd); int ret = -EINVAL; - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry_safe(slave, next, &mtd_partitions, list) - if ((slave->parent == mtd) && - (slave->mtd.index == partno)) { - ret = __mtd_del_partition(slave); + mutex_lock(&master->master.partitions_lock); + list_for_each_entry(child, &mtd->partitions, part.node) { + if (child->index == partno) { + ret = __mtd_del_partition(child); break; } - mutex_unlock(&mtd_partitions_mutex); + } + mutex_unlock(&master->master.partitions_lock); return ret; } EXPORT_SYMBOL_GPL(mtd_del_partition); /* - * This function, given a master MTD object and a partition table, creates - * and registers slave MTD objects which are bound to the master according to - * the partition definitions. + * This function, given a parent MTD object and a partition table, creates + * and registers the child MTD objects which are bound to the parent according + * to the partition definitions. * - * For historical reasons, this function's caller only registers the master + * For historical reasons, this function's caller only registers the parent * if the MTD_PARTITIONED_MASTER config option is set. */ -int add_mtd_partitions(struct mtd_info *master, +int add_mtd_partitions(struct mtd_info *parent, const struct mtd_partition *parts, int nbparts) { - struct mtd_part *slave; + struct mtd_info *child, *master = mtd_get_master(parent); uint64_t cur_offset = 0; int i, ret; - printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); + printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", + nbparts, parent->name); for (i = 0; i < nbparts; i++) { - slave = allocate_partition(master, parts + i, i, cur_offset); - if (IS_ERR(slave)) { - ret = PTR_ERR(slave); + child = allocate_partition(parent, parts + i, i, cur_offset); + if (IS_ERR(child)) { + ret = PTR_ERR(child); goto err_del_partitions; } - mutex_lock(&mtd_partitions_mutex); - list_add(&slave->list, &mtd_partitions); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_add_tail(&child->part.node, &parent->partitions); + mutex_unlock(&master->master.partitions_lock); - ret = add_mtd_device(&slave->mtd); + ret = add_mtd_device(child); if (ret) { - mutex_lock(&mtd_partitions_mutex); - list_del(&slave->list); - mutex_unlock(&mtd_partitions_mutex); + mutex_lock(&master->master.partitions_lock); + list_del(&child->part.node); + mutex_unlock(&master->master.partitions_lock); - free_partition(slave); + free_partition(child); goto err_del_partitions; } - mtd_add_partition_attrs(slave); + mtd_add_partition_attrs(child); + /* Look for subpartitions */ - parse_mtd_partitions(&slave->mtd, parts[i].types, NULL); + parse_mtd_partitions(child, parts[i].types, NULL); - cur_offset = slave->offset + slave->mtd.size; + cur_offset = child->part.offset + child->size; } return 0; @@ -1023,29 +700,11 @@ void mtd_part_parser_cleanup(struct mtd_partitions *parts) } } -int mtd_is_partition(const struct mtd_info *mtd) -{ - struct mtd_part *part; - int ispart = 0; - - mutex_lock(&mtd_partitions_mutex); - list_for_each_entry(part, &mtd_partitions, list) - if (&part->mtd == mtd) { - ispart = 1; - break; - } - mutex_unlock(&mtd_partitions_mutex); - - return ispart; -} -EXPORT_SYMBOL_GPL(mtd_is_partition); - /* Returns the size of the entire flash chip */ uint64_t mtd_get_device_size(const struct mtd_info *mtd) { - if (!mtd_is_partition(mtd)) - return mtd->size; + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); - return mtd_get_device_size(mtd_to_part(mtd)->parent); + return master->size; } EXPORT_SYMBOL_GPL(mtd_get_device_size); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 249e8d9bfbcd..2d1f4a61f4ac 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -194,10 +195,43 @@ struct mtd_debug_info { const char *partid; }; +/** + * struct mtd_part - MTD partition specific fields + * + * @node: list node used to add an MTD partition to the parent partition list + * @offset: offset of the partition relatively to the parent offset + * @flags: original flags (before the mtdpart logic decided to tweak them based + * on flash constraints, like eraseblock/pagesize alignment) + * + * This struct is embedded in mtd_info and contains partition-specific + * properties/fields. + */ +struct mtd_part { + struct list_head node; + u64 offset; + u32 flags; +}; + +/** + * struct mtd_master - MTD master specific fields + * + * @partitions_lock: lock protecting accesses to the partition list. Protects + * not only the master partition list, but also all + * sub-partitions. + * @suspended: et to 1 when the device is suspended, 0 otherwise + * + * This struct is embedded in mtd_info and contains master-specific + * properties/fields. The master is the root MTD device from the MTD partition + * point of view. + */ +struct mtd_master { + struct mutex partitions_lock; + unsigned int suspended : 1; +}; + struct mtd_info { u_char type; uint32_t flags; - uint32_t orig_flags; /* Flags as before running mtd checks */ uint64_t size; // Total size of the MTD /* "Major" erase size for the device. Naïve users may take this @@ -339,8 +373,52 @@ struct mtd_info { int usecount; struct mtd_debug_info dbg; struct nvmem_device *nvmem; + + /* + * Parent device from the MTD partition point of view. + * + * MTD masters do not have any parent, MTD partitions do. The parent + * MTD device can itself be a partition. + */ + struct mtd_info *parent; + + /* List of partitions attached to this MTD device */ + struct list_head partitions; + + union { + struct mtd_part part; + struct mtd_master master; + }; }; +static inline struct mtd_info *mtd_get_master(struct mtd_info *mtd) +{ + while (mtd->parent) + mtd = mtd->parent; + + return mtd; +} + +static inline u64 mtd_get_master_ofs(struct mtd_info *mtd, u64 ofs) +{ + while (mtd->parent) { + ofs += mtd->part.offset; + mtd = mtd->parent; + } + + return ofs; +} + +static inline bool mtd_is_partition(const struct mtd_info *mtd) +{ + return mtd->parent; +} + +static inline bool mtd_has_partitions(const struct mtd_info *mtd) +{ + return !list_empty(&mtd->partitions); +} + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, @@ -392,13 +470,16 @@ static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops) static inline int mtd_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len) { - if (!mtd->_max_bad_blocks) + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->_max_bad_blocks) return -ENOTSUPP; if (mtd->size < (len + ofs) || ofs < 0) return -EINVAL; - return mtd->_max_bad_blocks(mtd, ofs, len); + return master->_max_bad_blocks(master, mtd_get_master_ofs(mtd, ofs), + len); } int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit, @@ -439,8 +520,10 @@ int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs, static inline void mtd_sync(struct mtd_info *mtd) { - if (mtd->_sync) - mtd->_sync(mtd); + struct mtd_info *master = mtd_get_master(mtd); + + if (master->_sync) + master->_sync(master); } int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); @@ -452,13 +535,31 @@ int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs); static inline int mtd_suspend(struct mtd_info *mtd) { - return mtd->_suspend ? mtd->_suspend(mtd) : 0; + struct mtd_info *master = mtd_get_master(mtd); + int ret; + + if (master->master.suspended) + return 0; + + ret = master->_suspend ? master->_suspend(master) : 0; + if (ret) + return ret; + + master->master.suspended = 1; + return 0; } static inline void mtd_resume(struct mtd_info *mtd) { - if (mtd->_resume) - mtd->_resume(mtd); + struct mtd_info *master = mtd_get_master(mtd); + + if (!master->master.suspended) + return; + + if (master->_resume) + master->_resume(master); + + master->master.suspended = 0; } static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) @@ -538,7 +639,9 @@ static inline loff_t mtd_wunit_to_offset(struct mtd_info *mtd, loff_t base, static inline int mtd_has_oob(const struct mtd_info *mtd) { - return mtd->_read_oob && mtd->_write_oob; + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); + + return master->_read_oob && master->_write_oob; } static inline int mtd_type_is_nand(const struct mtd_info *mtd) @@ -548,7 +651,9 @@ static inline int mtd_type_is_nand(const struct mtd_info *mtd) static inline int mtd_can_have_bb(const struct mtd_info *mtd) { - return !!mtd->_block_isbad; + struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd); + + return !!master->_block_isbad; } /* Kernel-side ioctl definitions */ diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 11cb0c50cd84..e545c050d3e8 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -105,7 +105,6 @@ extern void deregister_mtd_parser(struct mtd_part_parser *parser); module_driver(__mtd_part_parser, register_mtd_parser, \ deregister_mtd_parser) -int mtd_is_partition(const struct mtd_info *mtd); int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); -- cgit v1.2.3 From 1292e3efb149ee21d8d33d725eeed4e6b1ade963 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 10 Mar 2020 12:49:43 +0100 Subject: mmc: core: Allow host controllers to require R1B for CMD6 It has turned out that some host controllers can't use R1B for CMD6 and other commands that have R1B associated with them. Therefore invent a new host cap, MMC_CAP_NEED_RSP_BUSY to let them specify this. In __mmc_switch(), let's check the flag and use it to prevent R1B responses from being converted into R1. Note that, this also means that the host are on its own, when it comes to manage the busy timeout. Suggested-by: Sowjanya Komatineni Cc: Tested-by: Anders Roxell Tested-by: Sowjanya Komatineni Tested-by: Faiz Abbas Tested-By: Peter Geis Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_ops.c | 6 ++++-- include/linux/mmc/host.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index da425ee2d9bf..e025604e17d4 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -542,9 +542,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, * If the max_busy_timeout of the host is specified, make sure it's * enough to fit the used timeout_ms. In case it's not, let's instruct * the host to avoid HW busy detection, by converting to a R1 response - * instead of a R1B. + * instead of a R1B. Note, some hosts requires R1B, which also means + * they are on their own when it comes to deal with the busy timeout. */ - if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout)) + if (!(host->caps & MMC_CAP_NEED_RSP_BUSY) && host->max_busy_timeout && + (timeout_ms > host->max_busy_timeout)) use_r1b_resp = false; cmd.opcode = MMC_SWITCH; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ba703384bea0..4c5eb3aa8e72 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -333,6 +333,7 @@ struct mmc_host { MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | \ MMC_CAP_UHS_DDR50) #define MMC_CAP_SYNC_RUNTIME_PM (1 << 21) /* Synced runtime PM suspends. */ +#define MMC_CAP_NEED_RSP_BUSY (1 << 22) /* Commands with R1B can't use R1. */ #define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ #define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ #define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ -- cgit v1.2.3 From c6fbcb70132ffc66696a94dd3d8e6215c750254f Mon Sep 17 00:00:00 2001 From: Jonathan Neuschäfer Date: Sun, 23 Feb 2020 19:06:33 +0100 Subject: mtd: rawnand: Fix a typo ("manufecturer") MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonathan Neuschäfer Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200223180634.8736-1-j.neuschaefer@gmx.net --- include/linux/mtd/rawnand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 4ab9bccfcde0..3c7c15aadcee 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1215,7 +1215,7 @@ static inline struct device_node *nand_get_flash_node(struct nand_chip *chip) * struct nand_flash_dev - NAND Flash Device ID Structure * @name: a human-readable name of the NAND chip * @dev_id: the device ID (the second byte of the full chip ID array) - * @mfr_id: manufecturer ID part of the full chip ID array (refers the same + * @mfr_id: manufacturer ID part of the full chip ID array (refers the same * memory address as ``id[0]``) * @dev_id: device ID part of the full chip ID array (refers the same memory * address as ``id[1]``) -- cgit v1.2.3 From 92270086b7e5ada7ab381c06cc3da2e95ed17088 Mon Sep 17 00:00:00 2001 From: Mason Yang Date: Tue, 3 Mar 2020 15:21:21 +0800 Subject: mtd: rawnand: Add support for manufacturer specific lock/unlock operation Add nand_lock() & nand_unlock() for manufacturer specific lock & unlock operation while the device supports Block Portection function. Signed-off-by: Mason Yang Reviewed-by: Miquel Raynal Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/1583220084-10890-2-git-send-email-masonccyang@mxic.com.tw --- drivers/mtd/nand/raw/nand_base.c | 36 ++++++++++++++++++++++++++++++++++-- include/linux/mtd/rawnand.h | 5 +++++ 2 files changed, 39 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index a3ed6c54963e..a13b91aa3780 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4365,6 +4365,38 @@ static void nand_shutdown(struct mtd_info *mtd) nand_suspend(mtd); } +/** + * nand_lock - [MTD Interface] Lock the NAND flash + * @mtd: MTD device structure + * @ofs: offset byte address + * @len: number of bytes to lock (must be a multiple of block/page size) + */ +static int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->lock_area) + return -ENOTSUPP; + + return chip->lock_area(chip, ofs, len); +} + +/** + * nand_unlock - [MTD Interface] Unlock the NAND flash + * @mtd: MTD device structure + * @ofs: offset byte address + * @len: number of bytes to unlock (must be a multiple of block/page size) + */ +static int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!chip->unlock_area) + return -ENOTSUPP; + + return chip->unlock_area(chip, ofs, len); +} + /* Set default functions */ static void nand_set_defaults(struct nand_chip *chip) { @@ -5791,8 +5823,8 @@ static int nand_scan_tail(struct nand_chip *chip) mtd->_read_oob = nand_read_oob; mtd->_write_oob = nand_write_oob; mtd->_sync = nand_sync; - mtd->_lock = NULL; - mtd->_unlock = NULL; + mtd->_lock = nand_lock; + mtd->_unlock = nand_unlock; mtd->_suspend = nand_suspend; mtd->_resume = nand_resume; mtd->_reboot = nand_shutdown; diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 3c7c15aadcee..49ed50fb44ab 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -1077,6 +1077,8 @@ struct nand_legacy { * @manufacturer: [INTERN] Contains manufacturer information * @manufacturer.desc: [INTERN] Contains manufacturer's description * @manufacturer.priv: [INTERN] Contains manufacturer private information + * @lock_area: [REPLACEABLE] specific NAND chip lock operation + * @unlock_area: [REPLACEABLE] specific NAND chip unlock operation */ struct nand_chip { @@ -1136,6 +1138,9 @@ struct nand_chip { const struct nand_manufacturer *desc; void *priv; } manufacturer; + + int (*lock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len); + int (*unlock_area)(struct nand_chip *chip, loff_t ofs, uint64_t len); }; extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops; -- cgit v1.2.3 From e3a36eb6dfaeea8175c05d5915dcf0b939be6dab Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 11 Mar 2020 17:07:10 +0100 Subject: driver code: clarify and fix platform device DMA mask allocation This does three inter-related things to clarify the usage of the platform device dma_mask field. In the process, fix the bug introduced by cdfee5623290 ("driver core: initialize a default DMA mask for platform device") that caused Artem Tashkinov's laptop to not boot with newer Fedora kernels. This does: - First off, rename the field to "platform_dma_mask" to make it greppable. We have way too many different random fields called "dma_mask" in various data structures, where some of them are actual masks, and some of them are just pointers to the mask. And the structures all have pointers to each other, or embed each other inside themselves, and "pdev" sometimes means "platform device" and sometimes it means "PCI device". So to make it clear in the code when you actually use this new field, give it a unique name (it really should be something even more unique like "platform_device_dma_mask", since it's per platform device, not per platform, but that gets old really fast, and this is unique enough in context). To further clarify when the field gets used, initialize it when we actually start using it with the default value. - Then, use this field instead of the random one-off allocation in platform_device_register_full() that is now unnecessary since we now already have a perfectly fine allocation for it in the platform device structure. - The above then allows us to fix the actual bug, where the error path of platform_device_register_full() would unconditionally free the platform device DMA allocation with 'kfree()'. That kfree() was dont regardless of whether the allocation had been done earlier with the (now removed) kmalloc, or whether setup_pdev_dma_masks() had already been used and the dma_mask pointer pointed to the mask that was part of the platform device. It seems most people never triggered the error path, or only triggered it from a call chain that set an explicit pdevinfo->dma_mask value (and thus caused the unnecessary allocation that was "cleaned up" in the error path) before calling platform_device_register_full(). Robin Murphy points out that in Artem's case the wdat_wdt driver failed in platform_device_add(), and that was the one that had called platform_device_register_full() with pdevinfo.dma_mask = 0, and would have caused that kfree() of pdev.dma_mask corrupting the heap. A later unrelated kmalloc() then oopsed due to the heap corruption. Fixes: cdfee5623290 ("driver core: initialize a default DMA mask for platform device") Reported-bisected-and-tested-by: Artem S. Tashkinov Reviewed-by: Robin Murphy Cc: Greg Kroah-Hartman Signed-off-by: Christoph Hellwig Signed-off-by: Linus Torvalds --- drivers/base/platform.c | 25 ++++++------------------- include/linux/platform_device.h | 2 +- 2 files changed, 7 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 7fa654f1288b..b5ce7b085795 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -363,10 +363,10 @@ static void setup_pdev_dma_masks(struct platform_device *pdev) { if (!pdev->dev.coherent_dma_mask) pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - if (!pdev->dma_mask) - pdev->dma_mask = DMA_BIT_MASK(32); - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &pdev->dma_mask; + if (!pdev->dev.dma_mask) { + pdev->platform_dma_mask = DMA_BIT_MASK(32); + pdev->dev.dma_mask = &pdev->platform_dma_mask; + } }; /** @@ -662,20 +662,8 @@ struct platform_device *platform_device_register_full( pdev->dev.of_node_reused = pdevinfo->of_node_reused; if (pdevinfo->dma_mask) { - /* - * This memory isn't freed when the device is put, - * I don't have a nice idea for that though. Conceptually - * dma_mask in struct device should not be a pointer. - * See http://thread.gmane.org/gmane.linux.kernel.pci/9081 - */ - pdev->dev.dma_mask = - kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL); - if (!pdev->dev.dma_mask) - goto err; - - kmemleak_ignore(pdev->dev.dma_mask); - - *pdev->dev.dma_mask = pdevinfo->dma_mask; + pdev->platform_dma_mask = pdevinfo->dma_mask; + pdev->dev.dma_mask = &pdev->platform_dma_mask; pdev->dev.coherent_dma_mask = pdevinfo->dma_mask; } @@ -700,7 +688,6 @@ struct platform_device *platform_device_register_full( if (ret) { err: ACPI_COMPANION_SET(&pdev->dev, NULL); - kfree(pdev->dev.dma_mask); platform_device_put(pdev); return ERR_PTR(ret); } diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 276a03c24691..041bfa412aa0 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -24,7 +24,7 @@ struct platform_device { int id; bool id_auto; struct device dev; - u64 dma_mask; + u64 platform_dma_mask; u32 num_resources; struct resource *resource; -- cgit v1.2.3 From 9952d90ea2885d7cbf80cd233f694f09a9c0eaec Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Wed, 11 Mar 2020 08:54:00 -0700 Subject: Bluetooth: Handle PM_SUSPEND_PREPARE and PM_POST_SUSPEND Register for PM_SUSPEND_PREPARE and PM_POST_SUSPEND to make sure the Bluetooth controller is prepared correctly for suspend/resume. Implement the registration, scheduling and task handling portions only in this patch. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 23 +++++++++++ net/bluetooth/hci_core.c | 86 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_request.c | 15 +++++++ net/bluetooth/hci_request.h | 2 + 4 files changed, 126 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c498ac113930..d6f694b436bf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -88,6 +88,20 @@ struct discovery_state { unsigned long scan_duration; }; +#define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +enum suspend_tasks { + SUSPEND_POWERING_DOWN, + + SUSPEND_PREPARE_NOTIFIER, + __SUSPEND_NUM_TASKS +}; + +enum suspended_state { + BT_RUNNING = 0, + BT_SUSPENDED, +}; + struct hci_conn_hash { struct list_head list; unsigned int acl_num; @@ -390,6 +404,15 @@ struct hci_dev { void *smp_bredr_data; struct discovery_state discovery; + + struct notifier_block suspend_notifier; + struct work_struct suspend_prepare; + enum suspended_state suspend_state_next; + enum suspended_state suspend_state; + + wait_queue_head_t suspend_wait_q; + DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS); + struct hci_conn_hash conn_hash; struct list_head mgmt_pending; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 196edc039b8e..39aa21a1fe92 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -1787,6 +1789,9 @@ int hci_dev_do_close(struct hci_dev *hdev) clear_bit(HCI_RUNNING, &hdev->flags); hci_sock_dev_event(hdev, HCI_DEV_CLOSE); + if (test_and_clear_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks)) + wake_up(&hdev->suspend_wait_q); + /* After this point our queues are empty * and no tasks are scheduled. */ hdev->close(hdev); @@ -3264,6 +3269,78 @@ void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, } } +static int hci_suspend_wait_event(struct hci_dev *hdev) +{ +#define WAKE_COND \ + (find_first_bit(hdev->suspend_tasks, __SUSPEND_NUM_TASKS) == \ + __SUSPEND_NUM_TASKS) + + int i; + int ret = wait_event_timeout(hdev->suspend_wait_q, + WAKE_COND, SUSPEND_NOTIFIER_TIMEOUT); + + if (ret == 0) { + bt_dev_dbg(hdev, "Timed out waiting for suspend"); + for (i = 0; i < __SUSPEND_NUM_TASKS; ++i) { + if (test_bit(i, hdev->suspend_tasks)) + bt_dev_dbg(hdev, "Bit %d is set", i); + clear_bit(i, hdev->suspend_tasks); + } + + ret = -ETIMEDOUT; + } else { + ret = 0; + } + + return ret; +} + +static void hci_prepare_suspend(struct work_struct *work) +{ + struct hci_dev *hdev = + container_of(work, struct hci_dev, suspend_prepare); + + hci_dev_lock(hdev); + hci_req_prepare_suspend(hdev, hdev->suspend_state_next); + hci_dev_unlock(hdev); +} + +static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct hci_dev *hdev = + container_of(nb, struct hci_dev, suspend_notifier); + int ret = 0; + + /* If powering down, wait for completion. */ + if (mgmt_powering_down(hdev)) { + set_bit(SUSPEND_POWERING_DOWN, hdev->suspend_tasks); + ret = hci_suspend_wait_event(hdev); + if (ret) + goto done; + } + + /* Suspend notifier should only act on events when powered. */ + if (!hdev_is_powered(hdev)) + goto done; + + if (action == PM_SUSPEND_PREPARE) { + hdev->suspend_state_next = BT_SUSPENDED; + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); + + ret = hci_suspend_wait_event(hdev); + } else if (action == PM_POST_SUSPEND) { + hdev->suspend_state_next = BT_RUNNING; + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); + + ret = hci_suspend_wait_event(hdev); + } + +done: + return ret ? notifier_from_errno(-EBUSY) : NOTIFY_STOP; +} /* Alloc HCI device */ struct hci_dev *hci_alloc_dev(void) { @@ -3341,6 +3418,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_WORK(&hdev->tx_work, hci_tx_work); INIT_WORK(&hdev->power_on, hci_power_on); INIT_WORK(&hdev->error_reset, hci_error_reset); + INIT_WORK(&hdev->suspend_prepare, hci_prepare_suspend); INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); @@ -3349,6 +3427,7 @@ struct hci_dev *hci_alloc_dev(void) skb_queue_head_init(&hdev->raw_q); init_waitqueue_head(&hdev->req_wait_q); + init_waitqueue_head(&hdev->suspend_wait_q); INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout); @@ -3460,6 +3539,11 @@ int hci_register_dev(struct hci_dev *hdev) hci_sock_dev_event(hdev, HCI_DEV_REG); hci_dev_hold(hdev); + hdev->suspend_notifier.notifier_call = hci_suspend_notifier; + error = register_pm_notifier(&hdev->suspend_notifier); + if (error) + goto err_wqueue; + queue_work(hdev->req_workqueue, &hdev->power_on); return id; @@ -3493,6 +3577,8 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_dev_do_close(hdev); + unregister_pm_notifier(&hdev->suspend_notifier); + if (!test_bit(HCI_INIT, &hdev->flags) && !hci_dev_test_flag(hdev, HCI_SETUP) && !hci_dev_test_flag(hdev, HCI_CONFIG)) { diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 53179ae856ae..2343166614f0 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -918,6 +918,21 @@ static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance) return adv_instance->scan_rsp_len; } +/* Call with hci_dev_lock */ +void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) +{ + if (next == hdev->suspend_state) { + bt_dev_dbg(hdev, "Same state before and after: %d", next); + goto done; + } + + hdev->suspend_state = next; + +done: + clear_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); + wake_up(&hdev->suspend_wait_q); +} + static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev) { u8 instance = hdev->cur_adv_instance; diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h index a7019fbeadd3..0e81614d235e 100644 --- a/net/bluetooth/hci_request.h +++ b/net/bluetooth/hci_request.h @@ -68,6 +68,8 @@ void __hci_req_update_eir(struct hci_request *req); void hci_req_add_le_scan_disable(struct hci_request *req); void hci_req_add_le_passive_scan(struct hci_request *req); +void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next); + void hci_req_reenable_advertising(struct hci_dev *hdev); void __hci_req_enable_advertising(struct hci_request *req); void __hci_req_disable_advertising(struct hci_request *req); -- cgit v1.2.3 From 4f40afc6c76451daff7d0dcfc8a3d113ccf65bfc Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Wed, 11 Mar 2020 08:54:01 -0700 Subject: Bluetooth: Handle BR/EDR devices during suspend To handle BR/EDR devices, we first disable page scan and disconnect all connected devices. Once that is complete, we add event filters (for devices that can wake the system) and re-enable page scan. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 17 ++++--- include/net/bluetooth/hci_core.h | 10 +++- net/bluetooth/hci_core.c | 22 ++++++-- net/bluetooth/hci_event.c | 24 +++++++++ net/bluetooth/hci_request.c | 106 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4e86f1bb7a87..5f60e135aeb6 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -942,10 +942,14 @@ struct hci_cp_sniff_subrate { #define HCI_OP_RESET 0x0c03 #define HCI_OP_SET_EVENT_FLT 0x0c05 -struct hci_cp_set_event_flt { - __u8 flt_type; - __u8 cond_type; - __u8 condition[]; +#define HCI_SET_EVENT_FLT_SIZE 9 +struct hci_cp_set_event_filter { + __u8 flt_type; + __u8 cond_type; + struct { + bdaddr_t bdaddr; + __u8 auto_accept; + } __packed addr_conn_flt; } __packed; /* Filter types */ @@ -959,8 +963,9 @@ struct hci_cp_set_event_flt { #define HCI_CONN_SETUP_ALLOW_BDADDR 0x02 /* CONN_SETUP Conditions */ -#define HCI_CONN_SETUP_AUTO_OFF 0x01 -#define HCI_CONN_SETUP_AUTO_ON 0x02 +#define HCI_CONN_SETUP_AUTO_OFF 0x01 +#define HCI_CONN_SETUP_AUTO_ON 0x02 +#define HCI_CONN_SETUP_AUTO_ON_WITH_RS 0x03 #define HCI_OP_READ_STORED_LINK_KEY 0x0c0d struct hci_cp_read_stored_link_key { diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d6f694b436bf..1a4d732bdce6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -91,6 +91,10 @@ struct discovery_state { #define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ enum suspend_tasks { + SUSPEND_SCAN_DISABLE, + SUSPEND_SCAN_ENABLE, + SUSPEND_DISCONNECTING, + SUSPEND_POWERING_DOWN, SUSPEND_PREPARE_NOTIFIER, @@ -99,7 +103,8 @@ enum suspend_tasks { enum suspended_state { BT_RUNNING = 0, - BT_SUSPENDED, + BT_SUSPEND_DISCONNECT, + BT_SUSPEND_COMPLETE, }; struct hci_conn_hash { @@ -409,6 +414,8 @@ struct hci_dev { struct work_struct suspend_prepare; enum suspended_state suspend_state_next; enum suspended_state suspend_state; + bool scanning_paused; + bool suspended; wait_queue_head_t suspend_wait_q; DECLARE_BITMAP(suspend_tasks, __SUSPEND_NUM_TASKS); @@ -418,6 +425,7 @@ struct hci_dev { struct list_head mgmt_pending; struct list_head blacklist; struct list_head whitelist; + struct list_head wakeable; struct list_head uuids; struct list_head link_keys; struct list_head long_term_keys; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 39aa21a1fe92..dbd2ad3a26ed 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3325,16 +3325,31 @@ static int hci_suspend_notifier(struct notifier_block *nb, unsigned long action, goto done; if (action == PM_SUSPEND_PREPARE) { - hdev->suspend_state_next = BT_SUSPENDED; + /* Suspend consists of two actions: + * - First, disconnect everything and make the controller not + * connectable (disabling scanning) + * - Second, program event filter/whitelist and enable scan + */ + hdev->suspend_state_next = BT_SUSPEND_DISCONNECT; set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); queue_work(hdev->req_workqueue, &hdev->suspend_prepare); - ret = hci_suspend_wait_event(hdev); + + /* If the disconnect portion failed, don't attempt to complete + * by configuring the whitelist. The suspend notifier will + * follow a cancelled suspend with a PM_POST_SUSPEND + * notification. + */ + if (!ret) { + hdev->suspend_state_next = BT_SUSPEND_COMPLETE; + set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); + queue_work(hdev->req_workqueue, &hdev->suspend_prepare); + ret = hci_suspend_wait_event(hdev); + } } else if (action == PM_POST_SUSPEND) { hdev->suspend_state_next = BT_RUNNING; set_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); queue_work(hdev->req_workqueue, &hdev->suspend_prepare); - ret = hci_suspend_wait_event(hdev); } @@ -3399,6 +3414,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->mgmt_pending); INIT_LIST_HEAD(&hdev->blacklist); INIT_LIST_HEAD(&hdev->whitelist); + INIT_LIST_HEAD(&hdev->wakeable); INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b9186026508e..0908eaa7cacf 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2505,6 +2505,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_complete *ev = (void *) skb->data; + struct inquiry_entry *ie; struct hci_conn *conn; BT_DBG("%s", hdev->name); @@ -2513,6 +2514,21 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, &ev->bdaddr); if (!conn) { + /* Connection may not exist if auto-connected. Check the inquiry + * cache to see if we've already discovered this bdaddr before. + * If found and link is an ACL type, create a connection class + * automatically. + */ + ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); + if (ie && ev->link_type == ACL_LINK) { + conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr, + HCI_ROLE_SLAVE); + if (!conn) { + bt_dev_err(hdev, "no memory for new conn"); + goto unlock; + } + } + if (ev->link_type != SCO_LINK) goto unlock; @@ -2774,6 +2790,14 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_disconn_cfm(conn, ev->reason); hci_conn_del(conn); + /* The suspend notifier is waiting for all devices to disconnect so + * clear the bit from pending tasks and inform the wait queue. + */ + if (list_empty(&hdev->conn_hash.list) && + test_and_clear_bit(SUSPEND_DISCONNECTING, hdev->suspend_tasks)) { + wake_up(&hdev->suspend_wait_q); + } + /* Re-enable advertising if necessary, since it might * have been disabled by the connection. From the * HCI_LE_Set_Advertise_Enable command description in diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 2343166614f0..051e1b16c988 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -918,15 +918,118 @@ static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance) return adv_instance->scan_rsp_len; } +static void hci_req_clear_event_filter(struct hci_request *req) +{ + struct hci_cp_set_event_filter f; + + memset(&f, 0, sizeof(f)); + f.flt_type = HCI_FLT_CLEAR_ALL; + hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &f); + + /* Update page scan state (since we may have modified it when setting + * the event filter). + */ + __hci_req_update_scan(req); +} + +static void hci_req_set_event_filter(struct hci_request *req) +{ + struct bdaddr_list *b; + struct hci_cp_set_event_filter f; + struct hci_dev *hdev = req->hdev; + u8 scan; + + /* Always clear event filter when starting */ + hci_req_clear_event_filter(req); + + list_for_each_entry(b, &hdev->wakeable, list) { + memset(&f, 0, sizeof(f)); + bacpy(&f.addr_conn_flt.bdaddr, &b->bdaddr); + f.flt_type = HCI_FLT_CONN_SETUP; + f.cond_type = HCI_CONN_SETUP_ALLOW_BDADDR; + f.addr_conn_flt.auto_accept = HCI_CONN_SETUP_AUTO_ON; + + bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr); + hci_req_add(req, HCI_OP_SET_EVENT_FLT, sizeof(f), &f); + } + + scan = !list_empty(&hdev->wakeable) ? SCAN_PAGE : SCAN_DISABLED; + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + +static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode) +{ + bt_dev_dbg(hdev, "Request complete opcode=0x%x, status=0x%x", opcode, + status); + if (test_and_clear_bit(SUSPEND_SCAN_ENABLE, hdev->suspend_tasks) || + test_and_clear_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks)) { + wake_up(&hdev->suspend_wait_q); + } +} + /* Call with hci_dev_lock */ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) { + struct hci_conn *conn; + struct hci_request req; + u8 page_scan; + int disconnect_counter; + if (next == hdev->suspend_state) { bt_dev_dbg(hdev, "Same state before and after: %d", next); goto done; } hdev->suspend_state = next; + hci_req_init(&req, hdev); + + if (next == BT_SUSPEND_DISCONNECT) { + /* Mark device as suspended */ + hdev->suspended = true; + + /* Disable page scan */ + page_scan = SCAN_DISABLED; + hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); + + /* Mark task needing completion */ + set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks); + + /* Prevent disconnects from causing scanning to be re-enabled */ + hdev->scanning_paused = true; + + /* Run commands before disconnecting */ + hci_req_run(&req, suspend_req_complete); + + disconnect_counter = 0; + /* Soft disconnect everything (power off) */ + list_for_each_entry(conn, &hdev->conn_hash.list, list) { + hci_disconnect(conn, HCI_ERROR_REMOTE_POWER_OFF); + disconnect_counter++; + } + + if (disconnect_counter > 0) { + bt_dev_dbg(hdev, + "Had %d disconnects. Will wait on them", + disconnect_counter); + set_bit(SUSPEND_DISCONNECTING, hdev->suspend_tasks); + } + } else if (next == BT_SUSPEND_COMPLETE) { + /* Unpause to take care of updating scanning params */ + hdev->scanning_paused = false; + /* Enable event filter for paired devices */ + hci_req_set_event_filter(&req); + /* Pause scan changes again. */ + hdev->scanning_paused = true; + hci_req_run(&req, suspend_req_complete); + } else { + hdev->suspended = false; + hdev->scanning_paused = false; + + hci_req_clear_event_filter(&req); + hci_req_run(&req, suspend_req_complete); + } + + hdev->suspend_state = next; done: clear_bit(SUSPEND_PREPARE_NOTIFIER, hdev->suspend_tasks); @@ -2030,6 +2133,9 @@ void __hci_req_update_scan(struct hci_request *req) if (mgmt_powering_down(hdev)) return; + if (hdev->scanning_paused) + return; + if (hci_dev_test_flag(hdev, HCI_CONNECTABLE) || disconnected_whitelist_entries(hdev)) scan = SCAN_PAGE; -- cgit v1.2.3 From dd522a7429b07e4441871ae75ebbfcf53635bdd4 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Wed, 11 Mar 2020 08:54:02 -0700 Subject: Bluetooth: Handle LE devices during suspend To handle LE devices, we must first disable passive scanning and disconnect all connected devices. Once that is complete, we update the whitelist and re-enable scanning Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_request.c | 166 ++++++++++++++++++++++++++------------- 2 files changed, 113 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1a4d732bdce6..2d58485d0335 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -607,6 +607,7 @@ struct hci_conn_params { struct hci_conn *conn; bool explicit_connect; + bool wakeable; }; extern struct list_head hci_dev_list; diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 051e1b16c988..11624645cfcf 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -34,6 +34,9 @@ #define HCI_REQ_PEND 1 #define HCI_REQ_CANCELED 2 +#define LE_SUSPEND_SCAN_WINDOW 0x0012 +#define LE_SUSPEND_SCAN_INTERVAL 0x0060 + void hci_req_init(struct hci_request *req, struct hci_dev *hdev) { skb_queue_head_init(&req->cmd_q); @@ -654,6 +657,11 @@ void hci_req_add_le_scan_disable(struct hci_request *req) { struct hci_dev *hdev = req->hdev; + if (hdev->scanning_paused) { + bt_dev_dbg(hdev, "Scanning is paused for suspend"); + return; + } + if (use_ext_scan(hdev)) { struct hci_cp_le_set_ext_scan_enable cp; @@ -670,15 +678,55 @@ void hci_req_add_le_scan_disable(struct hci_request *req) } } -static void add_to_white_list(struct hci_request *req, - struct hci_conn_params *params) +static void del_from_white_list(struct hci_request *req, bdaddr_t *bdaddr, + u8 bdaddr_type) +{ + struct hci_cp_le_del_from_white_list cp; + + cp.bdaddr_type = bdaddr_type; + bacpy(&cp.bdaddr, bdaddr); + + bt_dev_dbg(req->hdev, "Remove %pMR (0x%x) from whitelist", &cp.bdaddr, + cp.bdaddr_type); + hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, sizeof(cp), &cp); +} + +/* Adds connection to white list if needed. On error, returns -1. */ +static int add_to_white_list(struct hci_request *req, + struct hci_conn_params *params, u8 *num_entries, + bool allow_rpa) { struct hci_cp_le_add_to_white_list cp; + struct hci_dev *hdev = req->hdev; + + /* Already in white list */ + if (hci_bdaddr_list_lookup(&hdev->le_white_list, ¶ms->addr, + params->addr_type)) + return 0; + + /* Select filter policy to accept all advertising */ + if (*num_entries >= hdev->le_white_list_size) + return -1; + /* White list can not be used with RPAs */ + if (!allow_rpa && + hci_find_irk_by_addr(hdev, ¶ms->addr, params->addr_type)) { + return -1; + } + + /* During suspend, only wakeable devices can be in whitelist */ + if (hdev->suspended && !params->wakeable) + return 0; + + *num_entries += 1; cp.bdaddr_type = params->addr_type; bacpy(&cp.bdaddr, ¶ms->addr); + bt_dev_dbg(hdev, "Add %pMR (0x%x) to whitelist", &cp.bdaddr, + cp.bdaddr_type); hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp); + + return 0; } static u8 update_white_list(struct hci_request *req) @@ -686,7 +734,14 @@ static u8 update_white_list(struct hci_request *req) struct hci_dev *hdev = req->hdev; struct hci_conn_params *params; struct bdaddr_list *b; - uint8_t white_list_entries = 0; + u8 num_entries = 0; + bool pend_conn, pend_report; + /* We allow whitelisting even with RPAs in suspend. In the worst case, + * we won't be able to wake from devices that use the privacy1.2 + * features. Additionally, once we support privacy1.2 and IRK + * offloading, we can update this to also check for those conditions. + */ + bool allow_rpa = hdev->suspended; /* Go through the current white list programmed into the * controller one by one and check if that address is still @@ -695,29 +750,28 @@ static u8 update_white_list(struct hci_request *req) * command to remove it from the controller. */ list_for_each_entry(b, &hdev->le_white_list, list) { - /* If the device is neither in pend_le_conns nor - * pend_le_reports then remove it from the whitelist. + pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns, + &b->bdaddr, + b->bdaddr_type); + pend_report = hci_pend_le_action_lookup(&hdev->pend_le_reports, + &b->bdaddr, + b->bdaddr_type); + + /* If the device is not likely to connect or report, + * remove it from the whitelist. */ - if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, - &b->bdaddr, b->bdaddr_type) && - !hci_pend_le_action_lookup(&hdev->pend_le_reports, - &b->bdaddr, b->bdaddr_type)) { - struct hci_cp_le_del_from_white_list cp; - - cp.bdaddr_type = b->bdaddr_type; - bacpy(&cp.bdaddr, &b->bdaddr); - - hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST, - sizeof(cp), &cp); + if (!pend_conn && !pend_report) { + del_from_white_list(req, &b->bdaddr, b->bdaddr_type); continue; } - if (hci_find_irk_by_addr(hdev, &b->bdaddr, b->bdaddr_type)) { - /* White list can not be used with RPAs */ + /* White list can not be used with RPAs */ + if (!allow_rpa && + hci_find_irk_by_addr(hdev, &b->bdaddr, b->bdaddr_type)) { return 0x00; } - white_list_entries++; + num_entries++; } /* Since all no longer valid white list entries have been @@ -731,47 +785,17 @@ static u8 update_white_list(struct hci_request *req) * white list. */ list_for_each_entry(params, &hdev->pend_le_conns, action) { - if (hci_bdaddr_list_lookup(&hdev->le_white_list, - ¶ms->addr, params->addr_type)) - continue; - - if (white_list_entries >= hdev->le_white_list_size) { - /* Select filter policy to accept all advertising */ + if (add_to_white_list(req, params, &num_entries, allow_rpa)) return 0x00; - } - - if (hci_find_irk_by_addr(hdev, ¶ms->addr, - params->addr_type)) { - /* White list can not be used with RPAs */ - return 0x00; - } - - white_list_entries++; - add_to_white_list(req, params); } /* After adding all new pending connections, walk through * the list of pending reports and also add these to the - * white list if there is still space. + * white list if there is still space. Abort if space runs out. */ list_for_each_entry(params, &hdev->pend_le_reports, action) { - if (hci_bdaddr_list_lookup(&hdev->le_white_list, - ¶ms->addr, params->addr_type)) - continue; - - if (white_list_entries >= hdev->le_white_list_size) { - /* Select filter policy to accept all advertising */ + if (add_to_white_list(req, params, &num_entries, allow_rpa)) return 0x00; - } - - if (hci_find_irk_by_addr(hdev, ¶ms->addr, - params->addr_type)) { - /* White list can not be used with RPAs */ - return 0x00; - } - - white_list_entries++; - add_to_white_list(req, params); } /* Select filter policy to use white list */ @@ -866,6 +890,12 @@ void hci_req_add_le_passive_scan(struct hci_request *req) struct hci_dev *hdev = req->hdev; u8 own_addr_type; u8 filter_policy; + u8 window, interval; + + if (hdev->scanning_paused) { + bt_dev_dbg(hdev, "Scanning is paused for suspend"); + return; + } /* Set require_privacy to false since no SCAN_REQ are send * during passive scanning. Not using an non-resolvable address @@ -896,8 +926,17 @@ void hci_req_add_le_passive_scan(struct hci_request *req) (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)) filter_policy |= 0x02; - hci_req_start_scan(req, LE_SCAN_PASSIVE, hdev->le_scan_interval, - hdev->le_scan_window, own_addr_type, filter_policy); + if (hdev->suspended) { + window = LE_SUSPEND_SCAN_WINDOW; + interval = LE_SUSPEND_SCAN_INTERVAL; + } else { + window = hdev->le_scan_window; + interval = hdev->le_scan_interval; + } + + bt_dev_dbg(hdev, "LE passive scan with whitelist = %d", filter_policy); + hci_req_start_scan(req, LE_SCAN_PASSIVE, interval, window, + own_addr_type, filter_policy); } static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance) @@ -957,6 +996,18 @@ static void hci_req_set_event_filter(struct hci_request *req) hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } +static void hci_req_config_le_suspend_scan(struct hci_request *req) +{ + /* Can't change params without disabling first */ + hci_req_add_le_scan_disable(req); + + /* Configure params and enable scanning */ + hci_req_add_le_passive_scan(req); + + /* Block suspend notifier on response */ + set_bit(SUSPEND_SCAN_ENABLE, req->hdev->suspend_tasks); +} + static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode) { bt_dev_dbg(hdev, "Request complete opcode=0x%x, status=0x%x", opcode, @@ -991,6 +1042,9 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) page_scan = SCAN_DISABLED; hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); + /* Disable LE passive scan */ + hci_req_add_le_scan_disable(&req); + /* Mark task needing completion */ set_bit(SUSPEND_SCAN_DISABLE, hdev->suspend_tasks); @@ -1018,6 +1072,8 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) hdev->scanning_paused = false; /* Enable event filter for paired devices */ hci_req_set_event_filter(&req); + /* Enable passive scan at lower duty cycle */ + hci_req_config_le_suspend_scan(&req); /* Pause scan changes again. */ hdev->scanning_paused = true; hci_req_run(&req, suspend_req_complete); @@ -1026,6 +1082,8 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) hdev->scanning_paused = false; hci_req_clear_event_filter(&req); + /* Reset passive/background scanning to normal */ + hci_req_config_le_suspend_scan(&req); hci_req_run(&req, suspend_req_complete); } -- cgit v1.2.3 From 4867bd007d25a8dfd4ffc558534f7aec8b361789 Mon Sep 17 00:00:00 2001 From: Abhishek Pandit-Subedi Date: Wed, 11 Mar 2020 08:54:03 -0700 Subject: Bluetooth: Pause discovery and advertising during suspend To prevent spurious wake ups, we disable any discovery or advertising when we enter suspend and restore it when we exit suspend. While paused, we disable any management requests to modify discovery or advertising. Signed-off-by: Abhishek Pandit-Subedi Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 11 ++++++++++ net/bluetooth/hci_request.c | 44 ++++++++++++++++++++++++++++++++++++++++ net/bluetooth/mgmt.c | 41 +++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) (limited to 'include') diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2d58485d0335..d4e28773d378 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -91,6 +91,12 @@ struct discovery_state { #define SUSPEND_NOTIFIER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ enum suspend_tasks { + SUSPEND_PAUSE_DISCOVERY, + SUSPEND_UNPAUSE_DISCOVERY, + + SUSPEND_PAUSE_ADVERTISING, + SUSPEND_UNPAUSE_ADVERTISING, + SUSPEND_SCAN_DISABLE, SUSPEND_SCAN_ENABLE, SUSPEND_DISCONNECTING, @@ -410,6 +416,11 @@ struct hci_dev { struct discovery_state discovery; + int discovery_old_state; + bool discovery_paused; + int advertising_old_state; + bool advertising_paused; + struct notifier_block suspend_notifier; struct work_struct suspend_prepare; enum suspended_state suspend_state_next; diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 11624645cfcf..bf83179ab9d1 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -1021,6 +1021,7 @@ static void suspend_req_complete(struct hci_dev *hdev, u8 status, u16 opcode) /* Call with hci_dev_lock */ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) { + int old_state; struct hci_conn *conn; struct hci_request req; u8 page_scan; @@ -1038,6 +1039,28 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) /* Mark device as suspended */ hdev->suspended = true; + /* Pause discovery if not already stopped */ + old_state = hdev->discovery.state; + if (old_state != DISCOVERY_STOPPED) { + set_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks); + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); + queue_work(hdev->req_workqueue, &hdev->discov_update); + } + + hdev->discovery_paused = true; + hdev->discovery_old_state = old_state; + + /* Stop advertising */ + old_state = hci_dev_test_flag(hdev, HCI_ADVERTISING); + if (old_state) { + set_bit(SUSPEND_PAUSE_ADVERTISING, hdev->suspend_tasks); + cancel_delayed_work(&hdev->discov_off); + queue_delayed_work(hdev->req_workqueue, + &hdev->discov_off, 0); + } + + hdev->advertising_paused = true; + hdev->advertising_old_state = old_state; /* Disable page scan */ page_scan = SCAN_DISABLED; hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &page_scan); @@ -1084,6 +1107,27 @@ void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next) hci_req_clear_event_filter(&req); /* Reset passive/background scanning to normal */ hci_req_config_le_suspend_scan(&req); + + /* Unpause advertising */ + hdev->advertising_paused = false; + if (hdev->advertising_old_state) { + set_bit(SUSPEND_UNPAUSE_ADVERTISING, + hdev->suspend_tasks); + hci_dev_set_flag(hdev, HCI_ADVERTISING); + queue_work(hdev->req_workqueue, + &hdev->discoverable_update); + hdev->advertising_old_state = 0; + } + + /* Unpause discovery */ + hdev->discovery_paused = false; + if (hdev->discovery_old_state != DISCOVERY_STOPPED && + hdev->discovery_old_state != DISCOVERY_STOPPING) { + set_bit(SUSPEND_UNPAUSE_DISCOVERY, hdev->suspend_tasks); + hci_discovery_set_state(hdev, DISCOVERY_STARTING); + queue_work(hdev->req_workqueue, &hdev->discov_update); + } + hci_req_run(&req, suspend_req_complete); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b3a7f387da32..6552003a170e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1390,6 +1390,12 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + if (hdev->advertising_paused) { + err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_BUSY); + goto failed; + } + if (!hdev_is_powered(hdev)) { bool changed = false; @@ -3929,6 +3935,13 @@ void mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status) } hci_dev_unlock(hdev); + + /* Handle suspend notifier */ + if (test_and_clear_bit(SUSPEND_UNPAUSE_DISCOVERY, + hdev->suspend_tasks)) { + bt_dev_dbg(hdev, "Unpaused discovery"); + wake_up(&hdev->suspend_wait_q); + } } static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type, @@ -3990,6 +4003,13 @@ static int start_discovery_internal(struct sock *sk, struct hci_dev *hdev, goto failed; } + /* Can't start discovery when it is paused */ + if (hdev->discovery_paused) { + err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY, + &cp->type, sizeof(cp->type)); + goto failed; + } + /* Clear the discovery filter first to free any previously * allocated memory for the UUID list. */ @@ -4157,6 +4177,12 @@ void mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status) } hci_dev_unlock(hdev); + + /* Handle suspend notifier */ + if (test_and_clear_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks)) { + bt_dev_dbg(hdev, "Paused discovery"); + wake_up(&hdev->suspend_wait_q); + } } static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, @@ -4388,6 +4414,17 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, if (match.sk) sock_put(match.sk); + /* Handle suspend notifier */ + if (test_and_clear_bit(SUSPEND_PAUSE_ADVERTISING, + hdev->suspend_tasks)) { + bt_dev_dbg(hdev, "Paused advertising"); + wake_up(&hdev->suspend_wait_q); + } else if (test_and_clear_bit(SUSPEND_UNPAUSE_ADVERTISING, + hdev->suspend_tasks)) { + bt_dev_dbg(hdev, "Unpaused advertising"); + wake_up(&hdev->suspend_wait_q); + } + /* If "Set Advertising" was just disabled and instance advertising was * set up earlier, then re-enable multi-instance advertising. */ @@ -4439,6 +4476,10 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, MGMT_STATUS_INVALID_PARAMS); + if (hdev->advertising_paused) + return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, + MGMT_STATUS_BUSY); + hci_dev_lock(hdev); val = !!cp->val; -- cgit v1.2.3 From 7d3d3254adaa61cba896f71497f56901deb618e5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 11 Mar 2020 12:51:17 +0100 Subject: docs: fix pointers to io-mapping.rst and io_ordering.rst files Those files got moved, but cross-references still point to the wrong places. Fixes: fcd680727157 ("Documentation: Add io-mapping.rst to driver-api manual") Fixes: d1ce350015d8 ("Documentation: Add io_ordering.rst to driver-api manual") Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Alex Shi Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/c0205119db4fef536272cb0a183b6c14c2c8bf4c.1583927470.git.mchehab+huawei@kernel.org Signed-off-by: Jonathan Corbet --- Documentation/PCI/pci.rst | 2 +- Documentation/translations/zh_CN/io_ordering.txt | 4 ++-- arch/unicore32/include/asm/io.h | 2 +- include/linux/io-mapping.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/PCI/pci.rst b/Documentation/PCI/pci.rst index 6864f9a70f5f..8c016d8c9862 100644 --- a/Documentation/PCI/pci.rst +++ b/Documentation/PCI/pci.rst @@ -239,7 +239,7 @@ from the PCI device config space. Use the values in the pci_dev structure as the PCI "bus address" might have been remapped to a "host physical" address by the arch/chip-set specific kernel support. -See Documentation/io-mapping.txt for how to access device registers +See Documentation/driver-api/io-mapping.rst for how to access device registers or device memory. The device driver needs to call pci_request_region() to verify diff --git a/Documentation/translations/zh_CN/io_ordering.txt b/Documentation/translations/zh_CN/io_ordering.txt index 1f8127bdd415..7bb3086227ae 100644 --- a/Documentation/translations/zh_CN/io_ordering.txt +++ b/Documentation/translations/zh_CN/io_ordering.txt @@ -1,4 +1,4 @@ -Chinese translated version of Documentation/io_ordering.txt +Chinese translated version of Documentation/driver-api/io_ordering.rst If you have any comment or update to the content, please contact the original document maintainer directly. However, if you have a problem @@ -8,7 +8,7 @@ or if there is a problem with the translation. Chinese maintainer: Lin Yongting --------------------------------------------------------------------- -Documentation/io_ordering.txt 的中文翻译 +Documentation/driver-api/io_ordering.rst 的中文翻译 如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 diff --git a/arch/unicore32/include/asm/io.h b/arch/unicore32/include/asm/io.h index 3ca74e1cde7d..bd4e7c332f85 100644 --- a/arch/unicore32/include/asm/io.h +++ b/arch/unicore32/include/asm/io.h @@ -27,7 +27,7 @@ extern void __uc32_iounmap(volatile void __iomem *addr); * ioremap and friends. * * ioremap takes a PCI memory address, as specified in - * Documentation/io-mapping.txt. + * Documentation/driver-api/io-mapping.rst. * */ #define ioremap(cookie, size) __uc32_ioremap(cookie, size) diff --git a/include/linux/io-mapping.h b/include/linux/io-mapping.h index 837058bc1c9f..b336622612f3 100644 --- a/include/linux/io-mapping.h +++ b/include/linux/io-mapping.h @@ -16,7 +16,7 @@ * The io_mapping mechanism provides an abstraction for mapping * individual pages from an io device to the CPU in an efficient fashion. * - * See Documentation/io-mapping.txt + * See Documentation/driver-api/io-mapping.rst */ struct io_mapping { -- cgit v1.2.3 From 5febf6d6ae4d488a5dc388c46d96c17f9556238f Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Mon, 24 Feb 2020 10:14:06 -0600 Subject: scsi: Replace zero-length array with flexible-array member The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Link: https://lore.kernel.org/r/20200224161406.GA21454@embeddedor Reviewed-by: Lee Duncan Reviewed-by: Satish Kharat Signed-off-by: Gustavo A. R. Silva Signed-off-by: Martin K. Petersen --- drivers/scsi/fnic/vnic_devcmd.h | 2 +- drivers/scsi/ipr.h | 6 +++--- drivers/scsi/isci/sas.h | 2 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 2 +- drivers/scsi/mvsas/mv_sas.h | 2 +- drivers/scsi/mvumi.h | 4 ++-- drivers/scsi/pmcraid.h | 2 +- drivers/scsi/snic/vnic_devcmd.h | 2 +- drivers/scsi/stex.c | 2 +- include/scsi/iscsi_if.h | 10 +++++----- include/scsi/scsi_bsg_iscsi.h | 2 +- include/scsi/scsi_device.h | 4 ++-- include/scsi/scsi_host.h | 2 +- include/scsi/scsi_ioctl.h | 2 +- include/scsi/srp.h | 8 ++++---- include/uapi/scsi/scsi_bsg_fc.h | 2 +- 16 files changed, 27 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h index c5dde556dc7c..c20d30e36dfc 100644 --- a/drivers/scsi/fnic/vnic_devcmd.h +++ b/drivers/scsi/fnic/vnic_devcmd.h @@ -442,7 +442,7 @@ struct vnic_devcmd_notify { struct vnic_devcmd_provinfo { u8 oui[3]; u8 type; - u8 data[0]; + u8 data[]; }; /* diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index a67baeb36d1f..fd3929a19ab5 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -451,12 +451,12 @@ struct ipr_config_table_hdr64 { struct ipr_config_table { struct ipr_config_table_hdr hdr; - struct ipr_config_table_entry dev[0]; + struct ipr_config_table_entry dev[]; }__attribute__((packed, aligned (4))); struct ipr_config_table64 { struct ipr_config_table_hdr64 hdr64; - struct ipr_config_table_entry64 dev[0]; + struct ipr_config_table_entry64 dev[]; }__attribute__((packed, aligned (8))); struct ipr_config_table_entry_wrapper { @@ -792,7 +792,7 @@ struct ipr_mode_page28 { struct ipr_mode_page_hdr hdr; u8 num_entries; u8 entry_length; - struct ipr_dev_bus_entry bus[0]; + struct ipr_dev_bus_entry bus[]; }__attribute__((packed)); struct ipr_mode_page24 { diff --git a/drivers/scsi/isci/sas.h b/drivers/scsi/isci/sas.h index dc26b4aea99e..15d8f3631ab7 100644 --- a/drivers/scsi/isci/sas.h +++ b/drivers/scsi/isci/sas.h @@ -201,7 +201,7 @@ struct smp_req { u8 func; /* byte 1 */ u8 alloc_resp_len; /* byte 2 */ u8 req_len; /* byte 3 */ - u8 req_data[0]; + u8 req_data[]; } __packed; /* diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index c597d544eb39..778d5e6ce385 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -207,7 +207,7 @@ struct fw_event_work { u8 ignore; u16 event; struct kref refcount; - char event_data[0] __aligned(4); + char event_data[] __aligned(4); }; static void fw_event_work_free(struct kref *r) diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h index 519edc796691..327fdd5ee962 100644 --- a/drivers/scsi/mvsas/mv_sas.h +++ b/drivers/scsi/mvsas/mv_sas.h @@ -394,7 +394,7 @@ struct mvs_info { dma_addr_t bulk_buffer_dma1; #define TRASH_BUCKET_SIZE 0x20000 void *dma_pool; - struct mvs_slot_info slot_info[0]; + struct mvs_slot_info slot_info[]; }; struct mvs_prv_info{ diff --git a/drivers/scsi/mvumi.h b/drivers/scsi/mvumi.h index ec8cc2207536..60d5691fc4ab 100644 --- a/drivers/scsi/mvumi.h +++ b/drivers/scsi/mvumi.h @@ -130,7 +130,7 @@ enum { struct mvumi_hotplug_event { u16 size; u8 dummy[2]; - u8 bitmap[0]; + u8 bitmap[]; }; struct mvumi_driver_event { @@ -290,7 +290,7 @@ struct mvumi_rsp_frame { struct mvumi_ob_data { struct list_head list; - unsigned char data[0]; + unsigned char data[]; }; struct version_info { diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h index a4f7eb8f50a3..15c962108075 100644 --- a/drivers/scsi/pmcraid.h +++ b/drivers/scsi/pmcraid.h @@ -623,7 +623,7 @@ struct pmcraid_aen_msg { u32 hostno; u32 length; u8 reserved[8]; - u8 data[0]; + u8 data[]; }; /* Controller state event message type */ diff --git a/drivers/scsi/snic/vnic_devcmd.h b/drivers/scsi/snic/vnic_devcmd.h index d81b4f0ceaaa..0e0fa38f8d90 100644 --- a/drivers/scsi/snic/vnic_devcmd.h +++ b/drivers/scsi/snic/vnic_devcmd.h @@ -208,7 +208,7 @@ struct vnic_devcmd_notify { struct vnic_devcmd_provinfo { u8 oui[3]; u8 type; - u8 data[0]; + u8 data[]; }; /* diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c index 33287b6bdf0e..d4f10c0d813c 100644 --- a/drivers/scsi/stex.c +++ b/drivers/scsi/stex.c @@ -236,7 +236,7 @@ struct req_msg { u8 data_dir; u8 payload_sz; /* payload size in 4-byte, not used */ u8 cdb[STEX_CDB_LENGTH]; - u32 variable[0]; + u32 variable[]; }; struct status_msg { diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index 92b11c7e0b4f..b0e240b10bf9 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -311,7 +311,7 @@ enum iscsi_param_type { struct iscsi_param_info { uint32_t len; /* Actual length of the param value */ uint16_t param; /* iscsi param */ - uint8_t value[0]; /* length sized value follows */ + uint8_t value[]; /* length sized value follows */ } __packed; struct iscsi_iface_param_info { @@ -320,7 +320,7 @@ struct iscsi_iface_param_info { uint16_t param; /* iscsi param value */ uint8_t iface_type; /* IPv4 or IPv6 */ uint8_t param_type; /* iscsi_param_type */ - uint8_t value[0]; /* length sized value follows */ + uint8_t value[]; /* length sized value follows */ } __packed; /* @@ -697,7 +697,7 @@ enum iscsi_flashnode_param { struct iscsi_flashnode_param_info { uint32_t len; /* Actual length of the param */ uint16_t param; /* iscsi param value */ - uint8_t value[0]; /* length sized value follows */ + uint8_t value[]; /* length sized value follows */ } __packed; enum iscsi_discovery_parent_type { @@ -815,7 +815,7 @@ struct iscsi_stats { * up to ISCSI_STATS_CUSTOM_MAX */ uint32_t custom_length; - struct iscsi_stats_custom custom[0] + struct iscsi_stats_custom custom[] __attribute__ ((aligned (sizeof(uint64_t)))); }; @@ -946,7 +946,7 @@ struct iscsi_offload_host_stats { * up to ISCSI_HOST_STATS_CUSTOM_MAX */ uint32_t custom_length; - struct iscsi_host_stats_custom custom[0] + struct iscsi_host_stats_custom custom[] __aligned(sizeof(uint64_t)); }; diff --git a/include/scsi/scsi_bsg_iscsi.h b/include/scsi/scsi_bsg_iscsi.h index fa0c820a1663..6b8128005af8 100644 --- a/include/scsi/scsi_bsg_iscsi.h +++ b/include/scsi/scsi_bsg_iscsi.h @@ -52,7 +52,7 @@ struct iscsi_bsg_host_vendor { uint64_t vendor_id; /* start of vendor command area */ - uint32_t vendor_cmd[0]; + uint32_t vendor_cmd[]; }; /* Response: diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index f146a4557787..579e24c92f2f 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -230,7 +230,7 @@ struct scsi_device { struct mutex state_mutex; enum scsi_device_state sdev_state; struct task_struct *quiesced_by; - unsigned long sdev_data[0]; + unsigned long sdev_data[]; } __attribute__((aligned(sizeof(unsigned long)))); #define to_scsi_device(d) \ @@ -314,7 +314,7 @@ struct scsi_target { char scsi_level; enum scsi_target_state state; void *hostdata; /* available to low-level driver */ - unsigned long starget_data[0]; /* for the transport */ + unsigned long starget_data[]; /* for the transport */ /* starget_data must be the last element!!!! */ } __attribute__((aligned(sizeof(unsigned long)))); diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 7464394e7d01..822e8cda8d9b 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -683,7 +683,7 @@ struct Scsi_Host { * and also because some compilers (m68k) don't automatically force * alignment to a long boundary. */ - unsigned long hostdata[0] /* Used for storage of host specific stuff */ + unsigned long hostdata[] /* Used for storage of host specific stuff */ __attribute__ ((aligned (sizeof(unsigned long)))); }; diff --git a/include/scsi/scsi_ioctl.h b/include/scsi/scsi_ioctl.h index 4fe69d863b5d..b465799f4d2d 100644 --- a/include/scsi/scsi_ioctl.h +++ b/include/scsi/scsi_ioctl.h @@ -27,7 +27,7 @@ struct scsi_device; typedef struct scsi_ioctl_command { unsigned int inlen; unsigned int outlen; - unsigned char data[0]; + unsigned char data[]; } Scsi_Ioctl_Command; typedef struct scsi_idlun { diff --git a/include/scsi/srp.h b/include/scsi/srp.h index 9220758d5087..177d8026e96f 100644 --- a/include/scsi/srp.h +++ b/include/scsi/srp.h @@ -109,7 +109,7 @@ struct srp_direct_buf { struct srp_indirect_buf { struct srp_direct_buf table_desc; __be32 len; - struct srp_direct_buf desc_list[0]; + struct srp_direct_buf desc_list[]; } __attribute__((packed)); /* Immediate data buffer descriptor as defined in SRP2. */ @@ -244,7 +244,7 @@ struct srp_cmd { u8 reserved4; u8 add_cdb_len; u8 cdb[16]; - u8 add_data[0]; + u8 add_data[]; }; enum { @@ -274,7 +274,7 @@ struct srp_rsp { __be32 data_in_res_cnt; __be32 sense_data_len; __be32 resp_data_len; - u8 data[0]; + u8 data[]; } __attribute__((packed)); struct srp_cred_req { @@ -306,7 +306,7 @@ struct srp_aer_req { struct scsi_lun lun; __be32 sense_data_len; u32 reserved3; - u8 sense_data[0]; + u8 sense_data[]; } __attribute__((packed)); struct srp_aer_rsp { diff --git a/include/uapi/scsi/scsi_bsg_fc.h b/include/uapi/scsi/scsi_bsg_fc.h index 3ae65e93235c..7f5930801f72 100644 --- a/include/uapi/scsi/scsi_bsg_fc.h +++ b/include/uapi/scsi/scsi_bsg_fc.h @@ -209,7 +209,7 @@ struct fc_bsg_host_vendor { __u64 vendor_id; /* start of vendor command area */ - __u32 vendor_cmd[0]; + __u32 vendor_cmd[]; }; /* Response: -- cgit v1.2.3 From cc6b32ee3b914d3036a4d364fd4918b48fc034eb Mon Sep 17 00:00:00 2001 From: Frank Mayhar Date: Thu, 27 Feb 2020 14:59:45 -0500 Subject: scsi: iscsi: Add support for asynchronous iSCSI session destruction iSCSI session destruction can be arbitrarily slow, since it might require network operations and serialization inside the SCSI layer. This patch adds a new user event to trigger the destruction work asynchronously, releasing the rx_queue_mutex as soon as the operation is queued and before it is performed. This change allows other operations to run in other sessions in the meantime, removing one of the major iSCSI bottlenecks for us. To prevent the session from being used after the destruction request, we remove it immediately from the sesslist. This simplifies the locking required during the asynchronous removal. Link: https://lore.kernel.org/r/20200227195945.761719-1-krisman@collabora.com Co-developed-by: Gabriel Krisman Bertazi Co-developed-by: Khazhismel Kumykov Reviewed-by: Lee Duncan Signed-off-by: Gabriel Krisman Bertazi Signed-off-by: Frank Mayhar Signed-off-by: Khazhismel Kumykov Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_iscsi.c | 38 +++++++++++++++++++++++++++++++++++++ include/scsi/iscsi_if.h | 1 + include/scsi/scsi_transport_iscsi.h | 1 + 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index f3f0ecdb09a2..17a45716a0fe 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -95,6 +95,8 @@ static DECLARE_WORK(stop_conn_work, stop_conn_work_fn); static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ static struct workqueue_struct *iscsi_eh_timer_workq; +static struct workqueue_struct *iscsi_destroy_workq; + static DEFINE_IDA(iscsi_sess_ida); /* * list of registered transports and lock that must @@ -1615,6 +1617,7 @@ static struct sock *nls; static DEFINE_MUTEX(rx_queue_mutex); static LIST_HEAD(sesslist); +static LIST_HEAD(sessdestroylist); static DEFINE_SPINLOCK(sesslock); static LIST_HEAD(connlist); static LIST_HEAD(connlist_err); @@ -2035,6 +2038,14 @@ static void __iscsi_unbind_session(struct work_struct *work) ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n"); } +static void __iscsi_destroy_session(struct work_struct *work) +{ + struct iscsi_cls_session *session = + container_of(work, struct iscsi_cls_session, destroy_work); + + session->transport->destroy_session(session); +} + struct iscsi_cls_session * iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, int dd_size) @@ -2057,6 +2068,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, INIT_WORK(&session->block_work, __iscsi_block_session); INIT_WORK(&session->unbind_work, __iscsi_unbind_session); INIT_WORK(&session->scan_work, iscsi_scan_session); + INIT_WORK(&session->destroy_work, __iscsi_destroy_session); spin_lock_init(&session->lock); /* this is released in the dev's release function */ @@ -3631,6 +3643,23 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) else transport->destroy_session(session); break; + case ISCSI_UEVENT_DESTROY_SESSION_ASYNC: + session = iscsi_session_lookup(ev->u.d_session.sid); + if (!session) + err = -EINVAL; + else if (iscsi_session_has_conns(ev->u.d_session.sid)) + err = -EBUSY; + else { + unsigned long flags; + + /* Prevent this session from being found again */ + spin_lock_irqsave(&sesslock, flags); + list_move(&session->sess_list, &sessdestroylist); + spin_unlock_irqrestore(&sesslock, flags); + + queue_work(iscsi_destroy_workq, &session->destroy_work); + } + break; case ISCSI_UEVENT_UNBIND_SESSION: session = iscsi_session_lookup(ev->u.d_session.sid); if (session) @@ -4676,8 +4705,16 @@ static __init int iscsi_transport_init(void) goto release_nls; } + iscsi_destroy_workq = create_singlethread_workqueue("iscsi_destroy"); + if (!iscsi_destroy_workq) { + err = -ENOMEM; + goto destroy_wq; + } + return 0; +destroy_wq: + destroy_workqueue(iscsi_eh_timer_workq); release_nls: netlink_kernel_release(nls); unregister_flashnode_bus: @@ -4699,6 +4736,7 @@ unregister_transport_class: static void __exit iscsi_transport_exit(void) { + destroy_workqueue(iscsi_destroy_workq); destroy_workqueue(iscsi_eh_timer_workq); netlink_kernel_release(nls); bus_unregister(&iscsi_flashnode_bus); diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h index b0e240b10bf9..5225a23f2d0e 100644 --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -60,6 +60,7 @@ enum iscsi_uevent_e { ISCSI_UEVENT_LOGOUT_FLASHNODE_SID = UEVENT_BASE + 30, ISCSI_UEVENT_SET_CHAP = UEVENT_BASE + 31, ISCSI_UEVENT_GET_HOST_STATS = UEVENT_BASE + 32, + ISCSI_UEVENT_DESTROY_SESSION_ASYNC = UEVENT_BASE + 33, /* up events */ ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1, diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 2129dc9e2dec..fa8814245796 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -226,6 +226,7 @@ struct iscsi_cls_session { struct work_struct unblock_work; struct work_struct scan_work; struct work_struct unbind_work; + struct work_struct destroy_work; /* recovery fields */ int recovery_tmo; -- cgit v1.2.3 From 598a90f2002c4c4daee24d76d24e8270c7075eef Mon Sep 17 00:00:00 2001 From: Rajan Shanmugavelu Date: Wed, 12 Feb 2020 17:29:28 -0800 Subject: scsi: qla2xxx: add ring buffer for tracing debug logs Having this log in a ring buffer helps to diagnose qla2xxx driver and firmware issues instead of having to reproduce the problem with extended_logging enabled. This saves cycles and helps when it is hard to reproduce problem. Link: https://lore.kernel.org/r/1581557368-32080-1-git-send-email-rajan.shanmugavelu@oracle.com Reviewed-by: Joe Jin Acked-by: Himanshu Madhani Signed-off-by: Rajan Shanmugavelu Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_dbg.c | 23 ++++++++++++++++++++--- include/trace/events/qla.h | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 include/trace/events/qla.h (limited to 'include') diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 88a56e8480f7..f301a8048b2f 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -73,6 +73,8 @@ #include "qla_def.h" #include +#define CREATE_TRACE_POINTS +#include static uint32_t ql_dbg_offset = 0x800; @@ -2537,15 +2539,30 @@ ql_dbg(uint level, scsi_qla_host_t *vha, uint id, const char *fmt, ...) { va_list va; struct va_format vaf; - - if (!ql_mask_match(level)) - return; + char pbuf[64]; va_start(va, fmt); vaf.fmt = fmt; vaf.va = &va; + if (!ql_mask_match(level)) { + if (vha != NULL) { + const struct pci_dev *pdev = vha->hw->pdev; + /* : Message */ + snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x:%ld: ", + QL_MSGHDR, dev_name(&(pdev->dev)), id, + vha->host_no); + } else { + snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x: : ", + QL_MSGHDR, "0000:00:00.0", id); + } + pbuf[sizeof(pbuf) - 1] = 0; + trace_ql_dbg_log(pbuf, &vaf); + va_end(va); + return; + } + if (vha != NULL) { const struct pci_dev *pdev = vha->hw->pdev; /* : Message */ diff --git a/include/trace/events/qla.h b/include/trace/events/qla.h new file mode 100644 index 000000000000..b71f680968eb --- /dev/null +++ b/include/trace/events/qla.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#if !defined(_TRACE_QLA_H_) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QLA_H_ + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qla + +#define QLA_MSG_MAX 256 + +DECLARE_EVENT_CLASS(qla_log_event, + TP_PROTO(const char *buf, + struct va_format *vaf), + + TP_ARGS(buf, vaf), + + TP_STRUCT__entry( + __string(buf, buf) + __dynamic_array(char, msg, QLA_MSG_MAX) + ), + TP_fast_assign( + __assign_str(buf, buf); + vsnprintf(__get_str(msg), QLA_MSG_MAX, vaf->fmt, *vaf->va); + ), + + TP_printk("%s %s", __get_str(buf), __get_str(msg)) +); + +DEFINE_EVENT(qla_log_event, ql_dbg_log, + TP_PROTO(const char *buf, struct va_format *vaf), + TP_ARGS(buf, vaf) +); + +#endif /* _TRACE_QLA_H */ + +#define TRACE_INCLUDE_FILE qla + +#include -- cgit v1.2.3 From d4d79340fb7b3f6e9d69a47304b165207145b6d1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 2 Mar 2020 09:16:03 +0100 Subject: scsi: docs: convert scsi-generic.txt to ReST Link: https://lore.kernel.org/r/f57b8ddf30397c2c7213e49634e5e9cbd4246368.1583136624.git.mchehab+huawei@kernel.org Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Martin K. Petersen --- Documentation/scsi/index.rst | 1 + Documentation/scsi/scsi-generic.rst | 118 ++++++++++++++++++++++++++++++++++++ Documentation/scsi/scsi-generic.txt | 101 ------------------------------ MAINTAINERS | 2 +- drivers/scsi/Kconfig | 2 +- include/scsi/sg.h | 2 +- 6 files changed, 122 insertions(+), 104 deletions(-) create mode 100644 Documentation/scsi/scsi-generic.rst delete mode 100644 Documentation/scsi/scsi-generic.txt (limited to 'include') diff --git a/Documentation/scsi/index.rst b/Documentation/scsi/index.rst index 471982ef461d..119280f26da6 100644 --- a/Documentation/scsi/index.rst +++ b/Documentation/scsi/index.rst @@ -34,5 +34,6 @@ Linux SCSI Subsystem scsi-changer scsi_eh scsi_fc_transport + scsi-generic scsi_transport_srp/figures diff --git a/Documentation/scsi/scsi-generic.rst b/Documentation/scsi/scsi-generic.rst new file mode 100644 index 000000000000..258505e557a6 --- /dev/null +++ b/Documentation/scsi/scsi-generic.rst @@ -0,0 +1,118 @@ +.. SPDX-License-Identifier: GPL-2.0 + +======================================= +Notes on Linux SCSI Generic (sg) driver +======================================= + + 20020126 + +Introduction +============ +The SCSI Generic driver (sg) is one of the four "high level" SCSI device +drivers along with sd, st and sr (disk, tape and CDROM respectively). Sg +is more generalized (but lower level) than its siblings and tends to be +used on SCSI devices that don't fit into the already serviced categories. +Thus sg is used for scanners, CD writers and reading audio CDs digitally +amongst other things. + +Rather than document the driver's interface here, version information +is provided plus pointers (i.e. URLs) where to find documentation +and examples. + + +Major versions of the sg driver +=============================== +There are three major versions of sg found in the linux kernel (lk): + - sg version 1 (original) from 1992 to early 1999 (lk 2.2.5) . + It is based in the sg_header interface structure. + - sg version 2 from lk 2.2.6 in the 2.2 series. It is based on + an extended version of the sg_header interface structure. + - sg version 3 found in the lk 2.4 series (and the lk 2.5 series). + It adds the sg_io_hdr interface structure. + + +Sg driver documentation +======================= +The most recent documentation of the sg driver is kept at the Linux +Documentation Project's (LDP) site: + +- http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO + +This describes the sg version 3 driver found in the lk 2.4 series. + +The LDP renders documents in single and multiple page HTML, postscript +and pdf. This document can also be found at: + +- http://sg.danny.cz/sg/p/sg_v3_ho.html + +Documentation for the version 2 sg driver found in the lk 2.2 series can +be found at http://sg.danny.cz/sg/. A larger version +is at: http://sg.danny.cz/sg/p/scsi-generic_long.txt. + +The original documentation for the sg driver (prior to lk 2.2.6) can be +found at http://www.torque.net/sg/p/original/SCSI-Programming-HOWTO.txt +and in the LDP archives. + +A changelog with brief notes can be found in the +/usr/src/linux/include/scsi/sg.h file. Note that the glibc maintainers copy +and edit this file (removing its changelog for example) before placing it +in /usr/include/scsi/sg.h . Driver debugging information and other notes +can be found at the top of the /usr/src/linux/drivers/scsi/sg.c file. + +A more general description of the Linux SCSI subsystem of which sg is a +part can be found at http://www.tldp.org/HOWTO/SCSI-2.4-HOWTO . + + +Example code and utilities +========================== +There are two packages of sg utilities: + + ========= ========================================================== + sg3_utils for the sg version 3 driver found in lk 2.4 + sg_utils for the sg version 2 (and original) driver found in lk 2.2 + and earlier + ========= ========================================================== + +Both packages will work in the lk 2.4 series however sg3_utils offers more +capabilities. They can be found at: http://sg.danny.cz/sg/sg3_utils.html and +freecode.com + +Another approach is to look at the applications that use the sg driver. +These include cdrecord, cdparanoia, SANE and cdrdao. + + +Mapping of Linux kernel versions to sg driver versions +====================================================== +Here is a list of linux kernels in the 2.4 series that had new version +of the sg driver: + + - lk 2.4.0 : sg version 3.1.17 + - lk 2.4.7 : sg version 3.1.19 + - lk 2.4.10 : sg version 3.1.20 [#]_ + - lk 2.4.17 : sg version 3.1.22 + +.. [#] There were 3 changes to sg version 3.1.20 by third parties in the + next six linux kernel versions. + +For reference here is a list of linux kernels in the 2.2 series that had +new version of the sg driver: + + - lk 2.2.0 : original sg version [with no version number] + - lk 2.2.6 : sg version 2.1.31 + - lk 2.2.8 : sg version 2.1.32 + - lk 2.2.10 : sg version 2.1.34 [SG_GET_VERSION_NUM ioctl first appeared] + - lk 2.2.14 : sg version 2.1.36 + - lk 2.2.16 : sg version 2.1.38 + - lk 2.2.17 : sg version 2.1.39 + - lk 2.2.20 : sg version 2.1.40 + +The lk 2.5 development series has recently commenced and it currently +contains sg version 3.5.23 which is functionally equivalent to sg +version 3.1.22 found in lk 2.4.17. + + +Douglas Gilbert + +26th January 2002 + +dgilbert@interlog.com diff --git a/Documentation/scsi/scsi-generic.txt b/Documentation/scsi/scsi-generic.txt deleted file mode 100644 index 51be20a6a14d..000000000000 --- a/Documentation/scsi/scsi-generic.txt +++ /dev/null @@ -1,101 +0,0 @@ - Notes on Linux SCSI Generic (sg) driver - --------------------------------------- - 20020126 -Introduction -============ -The SCSI Generic driver (sg) is one of the four "high level" SCSI device -drivers along with sd, st and sr (disk, tape and CDROM respectively). Sg -is more generalized (but lower level) than its siblings and tends to be -used on SCSI devices that don't fit into the already serviced categories. -Thus sg is used for scanners, CD writers and reading audio CDs digitally -amongst other things. - -Rather than document the driver's interface here, version information -is provided plus pointers (i.e. URLs) where to find documentation -and examples. - - -Major versions of the sg driver -=============================== -There are three major versions of sg found in the linux kernel (lk): - - sg version 1 (original) from 1992 to early 1999 (lk 2.2.5) . - It is based in the sg_header interface structure. - - sg version 2 from lk 2.2.6 in the 2.2 series. It is based on - an extended version of the sg_header interface structure. - - sg version 3 found in the lk 2.4 series (and the lk 2.5 series). - It adds the sg_io_hdr interface structure. - - -Sg driver documentation -======================= -The most recent documentation of the sg driver is kept at the Linux -Documentation Project's (LDP) site: -http://www.tldp.org/HOWTO/SCSI-Generic-HOWTO -This describes the sg version 3 driver found in the lk 2.4 series. -The LDP renders documents in single and multiple page HTML, postscript -and pdf. This document can also be found at: -http://sg.danny.cz/sg/p/sg_v3_ho.html - -Documentation for the version 2 sg driver found in the lk 2.2 series can -be found at http://sg.danny.cz/sg/. A larger version -is at: http://sg.danny.cz/sg/p/scsi-generic_long.txt. - -The original documentation for the sg driver (prior to lk 2.2.6) can be -found at http://www.torque.net/sg/p/original/SCSI-Programming-HOWTO.txt -and in the LDP archives. - -A changelog with brief notes can be found in the -/usr/src/linux/include/scsi/sg.h file. Note that the glibc maintainers copy -and edit this file (removing its changelog for example) before placing it -in /usr/include/scsi/sg.h . Driver debugging information and other notes -can be found at the top of the /usr/src/linux/drivers/scsi/sg.c file. - -A more general description of the Linux SCSI subsystem of which sg is a -part can be found at http://www.tldp.org/HOWTO/SCSI-2.4-HOWTO . - - -Example code and utilities -========================== -There are two packages of sg utilities: - - sg3_utils for the sg version 3 driver found in lk 2.4 - - sg_utils for the sg version 2 (and original) driver found in lk 2.2 - and earlier -Both packages will work in the lk 2.4 series however sg3_utils offers more -capabilities. They can be found at: http://sg.danny.cz/sg/sg3_utils.html and -freecode.com - -Another approach is to look at the applications that use the sg driver. -These include cdrecord, cdparanoia, SANE and cdrdao. - - -Mapping of Linux kernel versions to sg driver versions -====================================================== -Here is a list of linux kernels in the 2.4 series that had new version -of the sg driver: - lk 2.4.0 : sg version 3.1.17 - lk 2.4.7 : sg version 3.1.19 - lk 2.4.10 : sg version 3.1.20 ** - lk 2.4.17 : sg version 3.1.22 - -** There were 3 changes to sg version 3.1.20 by third parties in the - next six linux kernel versions. - -For reference here is a list of linux kernels in the 2.2 series that had -new version of the sg driver: - lk 2.2.0 : original sg version [with no version number] - lk 2.2.6 : sg version 2.1.31 - lk 2.2.8 : sg version 2.1.32 - lk 2.2.10 : sg version 2.1.34 [SG_GET_VERSION_NUM ioctl first appeared] - lk 2.2.14 : sg version 2.1.36 - lk 2.2.16 : sg version 2.1.38 - lk 2.2.17 : sg version 2.1.39 - lk 2.2.20 : sg version 2.1.40 - -The lk 2.5 development series has recently commenced and it currently -contains sg version 3.5.23 which is functionally equivalent to sg -version 3.1.22 found in lk 2.4.17 . - - -Douglas Gilbert -26th January 2002 -dgilbert@interlog.com diff --git a/MAINTAINERS b/MAINTAINERS index 5a831cc4916f..af7cc8b330c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14797,7 +14797,7 @@ M: Doug Gilbert L: linux-scsi@vger.kernel.org W: http://sg.danny.cz/sg S: Maintained -F: Documentation/scsi/scsi-generic.txt +F: Documentation/scsi/scsi-generic.rst F: drivers/scsi/sg.c F: include/scsi/sg.h diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 6cb9abb0898d..bdf65b0bb78b 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -133,7 +133,7 @@ config CHR_DEV_SG quality digital reader of audio CDs (). For other devices, it's possible that you'll have to write the driver software yourself. Please read the file - for more information. + for more information. To compile this driver as a module, choose M here and read . The module will be called sg. diff --git a/include/scsi/sg.h b/include/scsi/sg.h index 29c7ad04d2e2..7327e12f3373 100644 --- a/include/scsi/sg.h +++ b/include/scsi/sg.h @@ -24,7 +24,7 @@ * http://sg.danny.cz/sg [alternatively check the MAINTAINERS file] * The documentation for the sg version 3 driver can be found at: * http://sg.danny.cz/sg/p/sg_v3_ho.html - * Also see: /Documentation/scsi/scsi-generic.txt + * Also see: /Documentation/scsi/scsi-generic.rst * * For utility and test programs see: http://sg.danny.cz/sg/sg3_utils.html */ -- cgit v1.2.3 From b0962c53bde9a485c8ebc401fa1dbe821a76bc3e Mon Sep 17 00:00:00 2001 From: "Ewan D. Milne" Date: Wed, 11 Mar 2020 10:39:30 -0400 Subject: scsi: core: avoid repetitive logging of device offline messages Large queues of I/O to offline devices that are eventually submitted when devices are unblocked result in a many repeated "rejecting I/O to offline device" messages. These messages can fill up the dmesg buffer in crash dumps so no useful prior messages remain. In addition, if a serial console is used, the flood of messages can cause a hard lockup in the console code. Introduce a flag indicating the message has already been logged for the device, and reset the flag when scsi_device_set_state() changes the device state. Link: https://lore.kernel.org/r/20200311143930.20674-1-emilne@redhat.com Reviewed-by: Bart van Assche Signed-off-by: Ewan D. Milne Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_lib.c | 8 ++++++-- include/scsi/scsi_device.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9f201194c46a..47835c4b4ee0 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1215,8 +1215,11 @@ scsi_prep_state_check(struct scsi_device *sdev, struct request *req) * commands. The device must be brought online * before trying any recovery commands. */ - sdev_printk(KERN_ERR, sdev, - "rejecting I/O to offline device\n"); + if (!sdev->offline_already) { + sdev->offline_already = true; + sdev_printk(KERN_ERR, sdev, + "rejecting I/O to offline device\n"); + } return BLK_STS_IOERR; case SDEV_DEL: /* @@ -2326,6 +2329,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state) break; } + sdev->offline_already = false; sdev->sdev_state = state; return 0; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 579e24c92f2f..c3cba2aaf934 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -203,6 +203,9 @@ struct scsi_device { unsigned unmap_limit_for_ws:1; /* Use the UNMAP limit for WRITE SAME */ unsigned rpm_autosuspend:1; /* Enable runtime autosuspend at device * creation time */ + + bool offline_already; /* Device offline message logged */ + atomic_t disk_events_disable_depth; /* disable depth for disk events */ DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */ -- cgit v1.2.3 From 0fa81b304a7973a499f844176ca031109487dd31 Mon Sep 17 00:00:00 2001 From: Alexander Bersenev Date: Fri, 6 Mar 2020 01:33:16 +0500 Subject: cdc_ncm: Implement the 32-bit version of NCM Transfer Block The NCM specification defines two formats of transfer blocks: with 16-bit fields (NTB-16) and with 32-bit fields (NTB-32). Currently only NTB-16 is implemented. This patch adds the support of NTB-32. The motivation behind this is that some devices such as E5785 or E5885 from the current generation of Huawei LTE routers do not support NTB-16. The previous generations of Huawei devices are also use NTB-32 by default. Also this patch enables NTB-32 by default for Huawei devices. During the 2019 ValdikSS made five attempts to contact Huawei to add the NTB-16 support to their router firmware, but they were unsuccessful. Signed-off-by: Alexander Bersenev Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 411 ++++++++++++++++++++++++++++++--------- drivers/net/usb/huawei_cdc_ncm.c | 8 +- include/linux/usb/cdc_ncm.h | 15 +- 3 files changed, 340 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index c2c82e6391b4..5569077bd5b8 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -175,7 +175,11 @@ static u32 cdc_ncm_check_tx_max(struct usbnet *dev, u32 new_tx) u32 val, max, min; /* clamp new_tx to sane values */ - min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); + if (ctx->is_ndp16) + min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth16); + else + min = ctx->max_datagram_size + ctx->max_ndp_size + sizeof(struct usb_cdc_ncm_nth32); + max = min_t(u32, CDC_NCM_NTB_MAX_SIZE_TX, le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)); /* some devices set dwNtbOutMaxSize too low for the above default */ @@ -307,10 +311,17 @@ static ssize_t ndp_to_end_store(struct device *d, struct device_attribute *attr if (enable == (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) return len; - if (enable && !ctx->delayed_ndp16) { - ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); - if (!ctx->delayed_ndp16) - return -ENOMEM; + if (enable) { + if (ctx->is_ndp16 && !ctx->delayed_ndp16) { + ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); + if (!ctx->delayed_ndp16) + return -ENOMEM; + } + if (!ctx->is_ndp16 && !ctx->delayed_ndp32) { + ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); + if (!ctx->delayed_ndp32) + return -ENOMEM; + } } /* flush pending data before changing flag */ @@ -512,6 +523,9 @@ static int cdc_ncm_init(struct usbnet *dev) dev_err(&dev->intf->dev, "SET_CRC_MODE failed\n"); } + /* use ndp16 by default */ + ctx->is_ndp16 = 1; + /* set NTB format, if both formats are supported. * * "The host shall only send this command while the NCM Data @@ -519,14 +533,27 @@ static int cdc_ncm_init(struct usbnet *dev) */ if (le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported) & USB_CDC_NCM_NTB32_SUPPORTED) { - dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); - err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - USB_CDC_NCM_NTB16_FORMAT, - iface_no, NULL, 0); - if (err < 0) + if (ctx->drvflags & CDC_NCM_FLAG_PREFER_NTB32) { + ctx->is_ndp16 = 0; + dev_dbg(&dev->intf->dev, "Setting NTB format to 32-bit\n"); + err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + USB_CDC_NCM_NTB32_FORMAT, + iface_no, NULL, 0); + } else { + ctx->is_ndp16 = 1; + dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit\n"); + err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + USB_CDC_NCM_NTB16_FORMAT, + iface_no, NULL, 0); + } + if (err < 0) { + ctx->is_ndp16 = 1; dev_err(&dev->intf->dev, "SET_NTB_FORMAT failed\n"); + } } /* set initial device values */ @@ -549,7 +576,10 @@ static int cdc_ncm_init(struct usbnet *dev) ctx->tx_max_datagrams = CDC_NCM_DPT_DATAGRAMS_MAX; /* set up maximum NDP size */ - ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); + if (ctx->is_ndp16) + ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp16) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe16); + else + ctx->max_ndp_size = sizeof(struct usb_cdc_ncm_ndp32) + (ctx->tx_max_datagrams + 1) * sizeof(struct usb_cdc_ncm_dpe32); /* initial coalescing timer interval */ ctx->timer_interval = CDC_NCM_TIMER_INTERVAL_USEC * NSEC_PER_USEC; @@ -734,7 +764,10 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) ctx->tx_curr_skb = NULL; } - kfree(ctx->delayed_ndp16); + if (ctx->is_ndp16) + kfree(ctx->delayed_ndp16); + else + kfree(ctx->delayed_ndp32); kfree(ctx); } @@ -772,10 +805,8 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ u8 *buf; int len; int temp; - int err; u8 iface_no; struct usb_cdc_parsed_header hdr; - __le16 curr_ntb_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -879,32 +910,6 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ goto error2; } - /* - * Some Huawei devices have been observed to come out of reset in NDP32 mode. - * Let's check if this is the case, and set the device to NDP16 mode again if - * needed. - */ - if (ctx->drvflags & CDC_NCM_FLAG_RESET_NTB16) { - err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_FORMAT, - USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, - 0, iface_no, &curr_ntb_format, 2); - if (err < 0) { - goto error2; - } - - if (curr_ntb_format == cpu_to_le16(USB_CDC_NCM_NTB32_FORMAT)) { - dev_info(&intf->dev, "resetting NTB format to 16-bit"); - err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - USB_CDC_NCM_NTB16_FORMAT, - iface_no, NULL, 0); - - if (err < 0) - goto error2; - } - } - cdc_ncm_find_endpoints(dev, ctx->data); cdc_ncm_find_endpoints(dev, ctx->control); if (!dev->in || !dev->out || !dev->status) { @@ -929,9 +934,15 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ /* Allocate the delayed NDP if needed. */ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { - ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); - if (!ctx->delayed_ndp16) - goto error2; + if (ctx->is_ndp16) { + ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); + if (!ctx->delayed_ndp16) + goto error2; + } else { + ctx->delayed_ndp32 = kzalloc(ctx->max_ndp_size, GFP_KERNEL); + if (!ctx->delayed_ndp32) + goto error2; + } dev_info(&intf->dev, "NDP will be placed at end of frame for this device."); } @@ -1055,7 +1066,7 @@ static void cdc_ncm_align_tail(struct sk_buff *skb, size_t modulus, size_t remai /* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly * allocating a new one within skb */ -static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) +static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) { struct usb_cdc_ncm_ndp16 *ndp16 = NULL; struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data; @@ -1110,12 +1121,73 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ return ndp16; } +static struct usb_cdc_ncm_ndp32 *cdc_ncm_ndp32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve) +{ + struct usb_cdc_ncm_ndp32 *ndp32 = NULL; + struct usb_cdc_ncm_nth32 *nth32 = (void *)skb->data; + size_t ndpoffset = le32_to_cpu(nth32->dwNdpIndex); + + /* If NDP should be moved to the end of the NCM package, we can't follow the + * NTH32 header as we would normally do. NDP isn't written to the SKB yet, and + * the wNdpIndex field in the header is actually not consistent with reality. It will be later. + */ + if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { + if (ctx->delayed_ndp32->dwSignature == sign) + return ctx->delayed_ndp32; + + /* We can only push a single NDP to the end. Return + * NULL to send what we've already got and queue this + * skb for later. + */ + else if (ctx->delayed_ndp32->dwSignature) + return NULL; + } + + /* follow the chain of NDPs, looking for a match */ + while (ndpoffset) { + ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb->data + ndpoffset); + if (ndp32->dwSignature == sign) + return ndp32; + ndpoffset = le32_to_cpu(ndp32->dwNextNdpIndex); + } + + /* align new NDP */ + if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) + cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size); + + /* verify that there is room for the NDP and the datagram (reserve) */ + if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size) + return NULL; + + /* link to it */ + if (ndp32) + ndp32->dwNextNdpIndex = cpu_to_le32(skb->len); + else + nth32->dwNdpIndex = cpu_to_le32(skb->len); + + /* push a new empty NDP */ + if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) + ndp32 = skb_put_zero(skb, ctx->max_ndp_size); + else + ndp32 = ctx->delayed_ndp32; + + ndp32->dwSignature = sign; + ndp32->wLength = cpu_to_le32(sizeof(struct usb_cdc_ncm_ndp32) + sizeof(struct usb_cdc_ncm_dpe32)); + return ndp32; +} + struct sk_buff * cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) { struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; - struct usb_cdc_ncm_nth16 *nth16; - struct usb_cdc_ncm_ndp16 *ndp16; + union { + struct usb_cdc_ncm_nth16 *nth16; + struct usb_cdc_ncm_nth32 *nth32; + } nth; + union { + struct usb_cdc_ncm_ndp16 *ndp16; + struct usb_cdc_ncm_ndp32 *ndp32; + } ndp; struct sk_buff *skb_out; u16 n = 0, index, ndplen; u8 ready2send = 0; @@ -1179,11 +1251,19 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) } ctx->tx_low_mem_val--; } - /* fill out the initial 16-bit NTB header */ - nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16)); - nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); - nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); - nth16->wSequence = cpu_to_le16(ctx->tx_seq++); + if (ctx->is_ndp16) { + /* fill out the initial 16-bit NTB header */ + nth.nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16)); + nth.nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN); + nth.nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)); + nth.nth16->wSequence = cpu_to_le16(ctx->tx_seq++); + } else { + /* fill out the initial 32-bit NTB header */ + nth.nth32 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth32)); + nth.nth32->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH32_SIGN); + nth.nth32->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth32)); + nth.nth32->wSequence = cpu_to_le16(ctx->tx_seq++); + } /* count total number of frames in this NTB */ ctx->tx_curr_frame_num = 0; @@ -1205,13 +1285,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) } /* get the appropriate NDP for this skb */ - ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); + if (ctx->is_ndp16) + ndp.ndp16 = cdc_ncm_ndp16(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); + else + ndp.ndp32 = cdc_ncm_ndp32(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); /* align beginning of next frame */ cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size); /* check if we had enough room left for both NDP and frame */ - if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) { + if ((ctx->is_ndp16 && !ndp.ndp16) || (!ctx->is_ndp16 && !ndp.ndp32) || + skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) { if (n == 0) { /* won't fit, MTU problem? */ dev_kfree_skb_any(skb); @@ -1233,13 +1317,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) } /* calculate frame number withing this NDP */ - ndplen = le16_to_cpu(ndp16->wLength); - index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1; + if (ctx->is_ndp16) { + ndplen = le16_to_cpu(ndp.ndp16->wLength); + index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1; + + /* OK, add this skb */ + ndp.ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len); + ndp.ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); + ndp.ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); + } else { + ndplen = le16_to_cpu(ndp.ndp32->wLength); + index = (ndplen - sizeof(struct usb_cdc_ncm_ndp32)) / sizeof(struct usb_cdc_ncm_dpe32) - 1; - /* OK, add this skb */ - ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len); - ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len); - ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16)); + ndp.ndp32->dpe32[index].dwDatagramLength = cpu_to_le32(skb->len); + ndp.ndp32->dpe32[index].dwDatagramIndex = cpu_to_le32(skb_out->len); + ndp.ndp32->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe32)); + } skb_put_data(skb_out, skb->data, skb->len); ctx->tx_curr_frame_payload += skb->len; /* count real tx payload data */ dev_kfree_skb_any(skb); @@ -1286,13 +1379,22 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* If requested, put NDP at end of frame. */ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { - nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; - cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size); - nth16->wNdpIndex = cpu_to_le16(skb_out->len); - skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size); + if (ctx->is_ndp16) { + nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; + cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size); + nth.nth16->wNdpIndex = cpu_to_le16(skb_out->len); + skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size); + + /* Zero out delayed NDP - signature checking will naturally fail. */ + ndp.ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size); + } else { + nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data; + cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size - ctx->max_ndp_size); + nth.nth32->dwNdpIndex = cpu_to_le32(skb_out->len); + skb_put_data(skb_out, ctx->delayed_ndp32, ctx->max_ndp_size); - /* Zero out delayed NDP - signature checking will naturally fail. */ - ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size); + ndp.ndp32 = memset(ctx->delayed_ndp32, 0, ctx->max_ndp_size); + } } /* If collected data size is less or equal ctx->min_tx_pkt @@ -1314,8 +1416,13 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) } /* set final frame length */ - nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; - nth16->wBlockLength = cpu_to_le16(skb_out->len); + if (ctx->is_ndp16) { + nth.nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; + nth.nth16->wBlockLength = cpu_to_le16(skb_out->len); + } else { + nth.nth32 = (struct usb_cdc_ncm_nth32 *)skb_out->data; + nth.nth32->dwBlockLength = cpu_to_le32(skb_out->len); + } /* return skb */ ctx->tx_curr_skb = NULL; @@ -1398,7 +1505,12 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) goto error; spin_lock_bh(&ctx->mtx); - skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); + + if (ctx->is_ndp16) + skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); + else + skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)); + spin_unlock_bh(&ctx->mtx); return skb_out; @@ -1459,6 +1571,54 @@ error: } EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16); +int cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) +{ + struct usbnet *dev = netdev_priv(skb_in->dev); + struct usb_cdc_ncm_nth32 *nth32; + int len; + int ret = -EINVAL; + + if (ctx == NULL) + goto error; + + if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth32) + + sizeof(struct usb_cdc_ncm_ndp32))) { + netif_dbg(dev, rx_err, dev->net, "frame too short\n"); + goto error; + } + + nth32 = (struct usb_cdc_ncm_nth32 *)skb_in->data; + + if (nth32->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH32_SIGN)) { + netif_dbg(dev, rx_err, dev->net, + "invalid NTH32 signature <%#010x>\n", + le32_to_cpu(nth32->dwSignature)); + goto error; + } + + len = le32_to_cpu(nth32->dwBlockLength); + if (len > ctx->rx_max) { + netif_dbg(dev, rx_err, dev->net, + "unsupported NTB block length %u/%u\n", len, + ctx->rx_max); + goto error; + } + + if ((ctx->rx_seq + 1) != le16_to_cpu(nth32->wSequence) && + (ctx->rx_seq || le16_to_cpu(nth32->wSequence)) && + !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth32->wSequence))) { + netif_dbg(dev, rx_err, dev->net, + "sequence number glitch prev=%d curr=%d\n", + ctx->rx_seq, le16_to_cpu(nth32->wSequence)); + } + ctx->rx_seq = le16_to_cpu(nth32->wSequence); + + ret = le32_to_cpu(nth32->dwNdpIndex); +error: + return ret; +} +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth32); + /* verify NDP header and return number of datagrams, or negative error */ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) { @@ -1495,6 +1655,42 @@ error: } EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); +/* verify NDP header and return number of datagrams, or negative error */ +int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset) +{ + struct usbnet *dev = netdev_priv(skb_in->dev); + struct usb_cdc_ncm_ndp32 *ndp32; + int ret = -EINVAL; + + if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp32)) > skb_in->len) { + netif_dbg(dev, rx_err, dev->net, "invalid NDP offset <%u>\n", + ndpoffset); + goto error; + } + ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset); + + if (le16_to_cpu(ndp32->wLength) < USB_CDC_NCM_NDP32_LENGTH_MIN) { + netif_dbg(dev, rx_err, dev->net, "invalid DPT32 length <%u>\n", + le16_to_cpu(ndp32->wLength)); + goto error; + } + + ret = ((le16_to_cpu(ndp32->wLength) - + sizeof(struct usb_cdc_ncm_ndp32)) / + sizeof(struct usb_cdc_ncm_dpe32)); + ret--; /* we process NDP entries except for the last one */ + + if ((sizeof(struct usb_cdc_ncm_ndp32) + + ret * (sizeof(struct usb_cdc_ncm_dpe32))) > skb_in->len) { + netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret); + ret = -EINVAL; + } + +error: + return ret; +} +EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp32); + int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) { struct sk_buff *skb; @@ -1503,34 +1699,66 @@ int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) int nframes; int x; int offset; - struct usb_cdc_ncm_ndp16 *ndp16; - struct usb_cdc_ncm_dpe16 *dpe16; + union { + struct usb_cdc_ncm_ndp16 *ndp16; + struct usb_cdc_ncm_ndp32 *ndp32; + } ndp; + union { + struct usb_cdc_ncm_dpe16 *dpe16; + struct usb_cdc_ncm_dpe32 *dpe32; + } dpe; + int ndpoffset; int loopcount = 50; /* arbitrary max preventing infinite loop */ u32 payload = 0; - ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); + if (ctx->is_ndp16) + ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); + else + ndpoffset = cdc_ncm_rx_verify_nth32(ctx, skb_in); + if (ndpoffset < 0) goto error; next_ndp: - nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); - if (nframes < 0) - goto error; + if (ctx->is_ndp16) { + nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); + if (nframes < 0) + goto error; - ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); + ndp.ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); - if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { - netif_dbg(dev, rx_err, dev->net, - "invalid DPT16 signature <%#010x>\n", - le32_to_cpu(ndp16->dwSignature)); - goto err_ndp; + if (ndp.ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { + netif_dbg(dev, rx_err, dev->net, + "invalid DPT16 signature <%#010x>\n", + le32_to_cpu(ndp.ndp16->dwSignature)); + goto err_ndp; + } + dpe.dpe16 = ndp.ndp16->dpe16; + } else { + nframes = cdc_ncm_rx_verify_ndp32(skb_in, ndpoffset); + if (nframes < 0) + goto error; + + ndp.ndp32 = (struct usb_cdc_ncm_ndp32 *)(skb_in->data + ndpoffset); + + if (ndp.ndp32->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP32_NOCRC_SIGN)) { + netif_dbg(dev, rx_err, dev->net, + "invalid DPT32 signature <%#010x>\n", + le32_to_cpu(ndp.ndp32->dwSignature)); + goto err_ndp; + } + dpe.dpe32 = ndp.ndp32->dpe32; } - dpe16 = ndp16->dpe16; - for (x = 0; x < nframes; x++, dpe16++) { - offset = le16_to_cpu(dpe16->wDatagramIndex); - len = le16_to_cpu(dpe16->wDatagramLength); + for (x = 0; x < nframes; x++) { + if (ctx->is_ndp16) { + offset = le16_to_cpu(dpe.dpe16->wDatagramIndex); + len = le16_to_cpu(dpe.dpe16->wDatagramLength); + } else { + offset = le32_to_cpu(dpe.dpe32->dwDatagramIndex); + len = le32_to_cpu(dpe.dpe32->dwDatagramLength); + } /* * CDC NCM ch. 3.7 @@ -1561,10 +1789,19 @@ next_ndp: usbnet_skb_return(dev, skb); payload += len; /* count payload bytes in this NTB */ } + + if (ctx->is_ndp16) + dpe.dpe16++; + else + dpe.dpe32++; } err_ndp: /* are there more NDPs to process? */ - ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); + if (ctx->is_ndp16) + ndpoffset = le16_to_cpu(ndp.ndp16->wNextNdpIndex); + else + ndpoffset = le32_to_cpu(ndp.ndp32->dwNextNdpIndex); + if (ndpoffset && loopcount--) goto next_ndp; diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c index e15a472c6a54..099d84827004 100644 --- a/drivers/net/usb/huawei_cdc_ncm.c +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -77,11 +77,11 @@ static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev, */ drvflags |= CDC_NCM_FLAG_NDP_TO_END; - /* Additionally, it has been reported that some Huawei E3372H devices, with - * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence - * needing to be set to the NTB16 one again. + /* For many Huawei devices the NTB32 mode is the default and the best mode + * they work with. Huawei E5785 and E5885 devices refuse to work in NTB16 mode at all. */ - drvflags |= CDC_NCM_FLAG_RESET_NTB16; + drvflags |= CDC_NCM_FLAG_PREFER_NTB32; + ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags); if (ret) goto err; diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 1646c06989df..0ce4377545f8 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -46,9 +46,12 @@ #define CDC_NCM_DATA_ALTSETTING_NCM 1 #define CDC_NCM_DATA_ALTSETTING_MBIM 2 -/* CDC NCM subclass 3.2.1 */ +/* CDC NCM subclass 3.3.1 */ #define USB_CDC_NCM_NDP16_LENGTH_MIN 0x10 +/* CDC NCM subclass 3.3.2 */ +#define USB_CDC_NCM_NDP32_LENGTH_MIN 0x20 + /* Maximum NTB length */ #define CDC_NCM_NTB_MAX_SIZE_TX 32768 /* bytes */ #define CDC_NCM_NTB_MAX_SIZE_RX 32768 /* bytes */ @@ -84,7 +87,7 @@ /* Driver flags */ #define CDC_NCM_FLAG_NDP_TO_END 0x02 /* NDP is placed at end of frame */ #define CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE 0x04 /* Avoid altsetting toggle during init */ -#define CDC_NCM_FLAG_RESET_NTB16 0x08 /* set NDP16 one more time after altsetting switch */ +#define CDC_NCM_FLAG_PREFER_NTB32 0x08 /* prefer NDP32 over NDP16 */ #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) @@ -113,7 +116,11 @@ struct cdc_ncm_ctx { u32 timer_interval; u32 max_ndp_size; - struct usb_cdc_ncm_ndp16 *delayed_ndp16; + u8 is_ndp16; + union { + struct usb_cdc_ncm_ndp16 *delayed_ndp16; + struct usb_cdc_ncm_ndp32 *delayed_ndp32; + }; u32 tx_timer_pending; u32 tx_curr_frame_num; @@ -150,6 +157,8 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset); +int cdc_ncm_rx_verify_nth32(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); +int cdc_ncm_rx_verify_ndp32(struct sk_buff *skb_in, int ndpoffset); struct sk_buff * cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in); -- cgit v1.2.3 From 4cda75275f9f89f9485b0ca4d6950c95258a9bce Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Tue, 10 Mar 2020 17:53:35 +0100 Subject: net: sched: make newly activated qdiscs visible In their .attach callback, mq[prio] only add the qdiscs of the currently active TX queues to the device's qdisc hash list. If a user later increases the number of active TX queues, their qdiscs are not visible via eg. 'tc qdisc show'. Add a hook to netif_set_real_num_tx_queues() that walks all active TX queues and adds those which are missing to the hash list. CC: Eric Dumazet CC: Jamal Hadi Salim CC: Cong Wang CC: Jiri Pirko Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- include/net/sch_generic.h | 6 ++++++ net/core/dev.c | 1 + net/sched/sch_generic.c | 21 +++++++++++++++++++++ 3 files changed, 28 insertions(+) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 151208704ed2..7bfc45c5b602 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -153,6 +153,11 @@ static inline bool qdisc_is_empty(const struct Qdisc *qdisc) return !READ_ONCE(qdisc->q.qlen); } +static inline bool qdisc_hashed(struct Qdisc *qdisc) +{ + return hash_hashed(&qdisc->hash); +} + static inline bool qdisc_run_begin(struct Qdisc *qdisc) { if (qdisc->flags & TCQ_F_NOLOCK) { @@ -629,6 +634,7 @@ void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *); void qdisc_class_hash_destroy(struct Qdisc_class_hash *); int dev_qdisc_change_tx_queue_len(struct net_device *dev); +void dev_qdisc_set_real_num_tx_queues(struct net_device *dev); void dev_init_scheduler(struct net_device *dev); void dev_shutdown(struct net_device *dev); void dev_activate(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index 25dab1598803..ccc03abeee52 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2875,6 +2875,7 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) netif_setup_tc(dev, txq); dev->real_num_tx_queues = txq; + dev_qdisc_set_real_num_tx_queues(dev); if (disabling) { synchronize_net(); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 6c9595f1048a..36a40ebcf0ee 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1268,6 +1268,27 @@ int dev_qdisc_change_tx_queue_len(struct net_device *dev) return ret; } +void dev_qdisc_set_real_num_tx_queues(struct net_device *dev) +{ +#ifdef CONFIG_NET_SCHED + struct Qdisc *sch = dev->qdisc; + unsigned int ntx; + + if (!sch) + return; + + ASSERT_RTNL(); + + for (ntx = 0; ntx < dev->real_num_tx_queues; ntx++) { + struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx); + struct Qdisc *qdisc = dev_queue->qdisc; + + if (qdisc && !qdisc_hashed(qdisc)) + qdisc_hash_add(qdisc, false); + } +#endif +} + static void dev_init_scheduler_queue(struct net_device *dev, struct netdev_queue *dev_queue, void *_qdisc) -- cgit v1.2.3 From 2677625387056136e256c743e3285b4fe3da87bb Mon Sep 17 00:00:00 2001 From: Paolo Lungaroni Date: Wed, 11 Mar 2020 17:54:06 +0100 Subject: seg6: fix SRv6 L2 tunnels to use IANA-assigned protocol number The Internet Assigned Numbers Authority (IANA) has recently assigned a protocol number value of 143 for Ethernet [1]. Before this assignment, encapsulation mechanisms such as Segment Routing used the IPv6-NoNxt protocol number (59) to indicate that the encapsulated payload is an Ethernet frame. In this patch, we add the definition of the Ethernet protocol number to the kernel headers and update the SRv6 L2 tunnels to use it. [1] https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml Signed-off-by: Paolo Lungaroni Reviewed-by: Andrea Mayer Acked-by: Ahmed Abdelsalam Signed-off-by: David S. Miller --- include/uapi/linux/in.h | 2 ++ net/ipv6/seg6_iptunnel.c | 2 +- net/ipv6/seg6_local.c | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index 1521073b6348..8533bf07450f 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -74,6 +74,8 @@ enum { #define IPPROTO_UDPLITE IPPROTO_UDPLITE IPPROTO_MPLS = 137, /* MPLS in IP (RFC 4023) */ #define IPPROTO_MPLS IPPROTO_MPLS + IPPROTO_ETHERNET = 143, /* Ethernet-within-IPv6 Encapsulation */ +#define IPPROTO_ETHERNET IPPROTO_ETHERNET IPPROTO_RAW = 255, /* Raw IP packets */ #define IPPROTO_RAW IPPROTO_RAW IPPROTO_MPTCP = 262, /* Multipath TCP connection */ diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index ab7f124ff5d7..8c52efe299cc 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -268,7 +268,7 @@ static int seg6_do_srh(struct sk_buff *skb) skb_mac_header_rebuild(skb); skb_push(skb, skb->mac_len); - err = seg6_do_srh_encap(skb, tinfo->srh, NEXTHDR_NONE); + err = seg6_do_srh_encap(skb, tinfo->srh, IPPROTO_ETHERNET); if (err) return err; diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 7cbc19731997..8165802d8e05 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -282,7 +282,7 @@ static int input_action_end_dx2(struct sk_buff *skb, struct net_device *odev; struct ethhdr *eth; - if (!decap_and_validate(skb, NEXTHDR_NONE)) + if (!decap_and_validate(skb, IPPROTO_ETHERNET)) goto drop; if (!pskb_may_pull(skb, ETH_HLEN)) -- cgit v1.2.3 From e6c6d7d4a15d499821c41b7e43d92b29c98293ad Mon Sep 17 00:00:00 2001 From: Sean Young Date: Fri, 17 Jan 2020 17:46:36 +0100 Subject: media: rc: make scancodes 64 bit There are many protocols that encode more than 32 bit. We want 64 bit support so that BPF IR decoders can decode more than 32 bit. None of the existing kernel IR decoders/encoders support 64 bit, for now. The MSC_SCAN event can only contain 32 bit scancodes, so we only generate MSC_SCAN events if the scancode fits into 32 bits. The full 64 bit scancode can be read from the lirc chardev. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/bpf-lirc.c | 5 --- drivers/media/rc/lirc_dev.c | 7 +--- drivers/media/rc/rc-main.c | 80 ++++++++++++++++++++++++++++++++------------- include/media/rc-core.h | 8 ++--- include/media/rc-map.h | 4 +-- 5 files changed, 65 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c index 0a0ce620e4a2..0f3417d161b8 100644 --- a/drivers/media/rc/bpf-lirc.c +++ b/drivers/media/rc/bpf-lirc.c @@ -35,11 +35,6 @@ static const struct bpf_func_proto rc_repeat_proto = { .arg1_type = ARG_PTR_TO_CTX, }; -/* - * Currently rc-core does not support 64-bit scancodes, but there are many - * known protocols with more than 32 bits. So, define the interface as u64 - * as a future-proof. - */ BPF_CALL_4(bpf_rc_keydown, u32*, sample, u32, protocol, u64, scancode, u32, toggle) { diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 9a8c1cf54ac4..583e4f32a0da 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -269,12 +269,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, goto out_unlock; } - /* - * The scancode field in lirc_scancode is 64-bit simply - * to future-proof it, since there are IR protocols encode - * use more than 32 bits. For now only 32-bit protocols - * are supported. - */ + /* We only have encoders for 32-bit protocols. */ if (scan.scancode > U32_MAX || !rc_validate_scancode(scan.rc_proto, scan.scancode)) { ret = -EINVAL; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 6f80c251f641..d7064d664d52 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -163,6 +163,41 @@ static struct rc_map_list empty_map = { } }; +/** + * scancode_to_u64() - converts scancode in &struct input_keymap_entry + * @ke: keymap entry containing scancode to be converted. + * @scancode: pointer to the location where converted scancode should + * be stored. + * + * This function is a version of input_scancode_to_scalar specialized for + * rc-core. + */ +static int scancode_to_u64(const struct input_keymap_entry *ke, u64 *scancode) +{ + switch (ke->len) { + case 1: + *scancode = *((u8 *)ke->scancode); + break; + + case 2: + *scancode = *((u16 *)ke->scancode); + break; + + case 4: + *scancode = *((u32 *)ke->scancode); + break; + + case 8: + *scancode = *((u64 *)ke->scancode); + break; + + default: + return -EINVAL; + } + + return 0; +} + /** * ir_create_table() - initializes a scancode table * @dev: the rc_dev device @@ -285,13 +320,13 @@ static unsigned int ir_update_mapping(struct rc_dev *dev, /* Did the user wish to remove the mapping? */ if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) { - dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04x\n", + dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04llx\n", index, rc_map->scan[index].scancode); rc_map->len--; memmove(&rc_map->scan[index], &rc_map->scan[index+ 1], (rc_map->len - index) * sizeof(struct rc_map_table)); } else { - dev_dbg(&dev->dev, "#%d: %s scan 0x%04x with key 0x%04x\n", + dev_dbg(&dev->dev, "#%d: %s scan 0x%04llx with key 0x%04x\n", index, old_keycode == KEY_RESERVED ? "New" : "Replacing", rc_map->scan[index].scancode, new_keycode); @@ -334,8 +369,7 @@ static unsigned int ir_update_mapping(struct rc_dev *dev, */ static unsigned int ir_establish_scancode(struct rc_dev *dev, struct rc_map *rc_map, - unsigned int scancode, - bool resize) + u64 scancode, bool resize) { unsigned int i; @@ -394,7 +428,7 @@ static int ir_setkeycode(struct input_dev *idev, struct rc_dev *rdev = input_get_drvdata(idev); struct rc_map *rc_map = &rdev->rc_map; unsigned int index; - unsigned int scancode; + u64 scancode; int retval = 0; unsigned long flags; @@ -407,7 +441,7 @@ static int ir_setkeycode(struct input_dev *idev, goto out; } } else { - retval = input_scancode_to_scalar(ke, &scancode); + retval = scancode_to_u64(ke, &scancode); if (retval) goto out; @@ -434,8 +468,7 @@ out: * * return: -ENOMEM if all keycodes could not be inserted, otherwise zero. */ -static int ir_setkeytable(struct rc_dev *dev, - const struct rc_map *from) +static int ir_setkeytable(struct rc_dev *dev, const struct rc_map *from) { struct rc_map *rc_map = &dev->rc_map; unsigned int i, index; @@ -466,7 +499,7 @@ static int ir_setkeytable(struct rc_dev *dev, static int rc_map_cmp(const void *key, const void *elt) { - const unsigned int *scancode = key; + const u64 *scancode = key; const struct rc_map_table *e = elt; if (*scancode < e->scancode) @@ -487,7 +520,7 @@ static int rc_map_cmp(const void *key, const void *elt) * return: index in the table, -1U if not found */ static unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map, - unsigned int scancode) + u64 scancode) { struct rc_map_table *res; @@ -516,7 +549,7 @@ static int ir_getkeycode(struct input_dev *idev, struct rc_map_table *entry; unsigned long flags; unsigned int index; - unsigned int scancode; + u64 scancode; int retval; spin_lock_irqsave(&rc_map->lock, flags); @@ -524,7 +557,7 @@ static int ir_getkeycode(struct input_dev *idev, if (ke->flags & INPUT_KEYMAP_BY_INDEX) { index = ke->index; } else { - retval = input_scancode_to_scalar(ke, &scancode); + retval = scancode_to_u64(ke, &scancode); if (retval) goto out; @@ -538,7 +571,6 @@ static int ir_getkeycode(struct input_dev *idev, ke->keycode = entry->keycode; ke->len = sizeof(entry->scancode); memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode)); - } else if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) { /* * We do not really know the valid range of scancodes @@ -570,7 +602,7 @@ out: * * return: the corresponding keycode, or KEY_RESERVED */ -u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode) +u32 rc_g_keycode_from_table(struct rc_dev *dev, u64 scancode) { struct rc_map *rc_map = &dev->rc_map; unsigned int keycode; @@ -586,7 +618,7 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode) spin_unlock_irqrestore(&rc_map->lock, flags); if (keycode != KEY_RESERVED) - dev_dbg(&dev->dev, "%s: scancode 0x%04x keycode 0x%02x\n", + dev_dbg(&dev->dev, "%s: scancode 0x%04llx keycode 0x%02x\n", dev->device_name, scancode, keycode); return keycode; @@ -719,8 +751,11 @@ void rc_repeat(struct rc_dev *dev) spin_lock_irqsave(&dev->keylock, flags); - input_event(dev->input_dev, EV_MSC, MSC_SCAN, dev->last_scancode); - input_sync(dev->input_dev); + if (dev->last_scancode <= U32_MAX) { + input_event(dev->input_dev, EV_MSC, MSC_SCAN, + dev->last_scancode); + input_sync(dev->input_dev); + } if (dev->keypressed) { dev->keyup_jiffies = jiffies + timeout; @@ -743,7 +778,7 @@ EXPORT_SYMBOL_GPL(rc_repeat); * called with keylock held. */ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, - u32 scancode, u32 keycode, u8 toggle) + u64 scancode, u32 keycode, u8 toggle) { bool new_event = (!dev->keypressed || dev->last_protocol != protocol || @@ -761,7 +796,8 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, if (new_event && dev->keypressed) ir_do_keyup(dev, false); - input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); + if (scancode <= U32_MAX) + input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); dev->last_protocol = protocol; dev->last_scancode = scancode; @@ -772,7 +808,7 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, /* Register a keypress */ dev->keypressed = true; - dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n", + dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08llx\n", dev->device_name, keycode, protocol, scancode); input_report_key(dev->input_dev, keycode, 1); @@ -809,7 +845,7 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, * This routine is used to signal that a key has been pressed on the * remote control. */ -void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u32 scancode, +void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u64 scancode, u8 toggle) { unsigned long flags; @@ -840,7 +876,7 @@ EXPORT_SYMBOL_GPL(rc_keydown); * remote control. The driver must manually call rc_keyup() at a later stage. */ void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol, - u32 scancode, u8 toggle) + u64 scancode, u8 toggle) { unsigned long flags; u32 keycode = rc_g_keycode_from_table(dev, scancode); diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 1f695d9c200a..d3f85df64bb2 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -192,7 +192,7 @@ struct rc_dev { struct timer_list timer_repeat; u32 last_keycode; enum rc_proto last_protocol; - u32 last_scancode; + u64 last_scancode; u8 last_toggle; u32 timeout; u32 min_timeout; @@ -284,12 +284,12 @@ int devm_rc_register_device(struct device *parent, struct rc_dev *dev); void rc_unregister_device(struct rc_dev *dev); void rc_repeat(struct rc_dev *dev); -void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u32 scancode, +void rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u64 scancode, u8 toggle); void rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol, - u32 scancode, u8 toggle); + u64 scancode, u8 toggle); void rc_keyup(struct rc_dev *dev); -u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode); +u32 rc_g_keycode_from_table(struct rc_dev *dev, u64 scancode); /* * From rc-raw.c diff --git a/include/media/rc-map.h b/include/media/rc-map.h index d22810dcd85c..0ce896f10202 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -85,11 +85,11 @@ /** * struct rc_map_table - represents a scancode/keycode pair * - * @scancode: scan code (u32) + * @scancode: scan code (u64) * @keycode: Linux input keycode */ struct rc_map_table { - u32 scancode; + u64 scancode; u32 keycode; }; -- cgit v1.2.3 From 9a8da6082ddb4689baf34dcdf2a53985e25753f3 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Mar 2020 09:31:31 +0100 Subject: tty: serial: ifx6x60: Convert to GPIO descriptors This driver for the Intel MID never seems to have been properly integrated upstream: the platform data in is not used anywhere in the kernel and haven't been since it was merged into the kernel in 2010. There might be out-of-tree users, so I don't want to delete the driver, but I will refactor it to use GPIO descriptors, which means that out-of-tree users will need to adapt. There are several examples in the kernel of how to provide the resources necessary for using GPIO descriptors to pass in the GPIO lines, for the MID platform in particular, it will suffice to inspect the code in files like: arch/x86/platform/intel-mid/device_libs/platform_bt.c This refactoring transfers all GPIOs in the driver, including a hard-coded "PMU reset" in the driver to use GPIO descriptors instead. The following named GPIO descriptors need to be supplied: - reset - power - mrdy - srdy - rst_out - pmu_reset Cc: Russ Gorby Signed-off-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200311083131.693908-2-linus.walleij@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ifx6x60.c | 170 ++++++++++++++---------------------------- drivers/tty/serial/ifx6x60.h | 13 ++-- include/linux/spi/ifx_modem.h | 5 -- 3 files changed, 65 insertions(+), 123 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c index 32a0ccef9339..7d16fe41932f 100644 --- a/drivers/tty/serial/ifx6x60.c +++ b/drivers/tty/serial/ifx6x60.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,7 +61,6 @@ #define IFX_SPI_HEADER_F (-2) #define PO_POST_DELAY 200 -#define IFX_MDM_RST_PMU 4 /* forward reference */ static void ifx_spi_handle_srdy(struct ifx_spi_device *ifx_dev); @@ -81,7 +80,7 @@ static struct notifier_block ifx_modem_reboot_notifier_block = { static int ifx_modem_power_off(struct ifx_spi_device *ifx_dev) { - gpio_set_value(IFX_MDM_RST_PMU, 1); + gpiod_set_value(ifx_dev->gpio.pmu_reset, 1); msleep(PO_POST_DELAY); return 0; @@ -107,7 +106,7 @@ static int ifx_modem_reboot_callback(struct notifier_block *nfb, */ static inline void mrdy_set_high(struct ifx_spi_device *ifx) { - gpio_set_value(ifx->gpio.mrdy, 1); + gpiod_set_value(ifx->gpio.mrdy, 1); } /** @@ -117,7 +116,7 @@ static inline void mrdy_set_high(struct ifx_spi_device *ifx) */ static inline void mrdy_set_low(struct ifx_spi_device *ifx) { - gpio_set_value(ifx->gpio.mrdy, 0); + gpiod_set_value(ifx->gpio.mrdy, 0); } /** @@ -244,7 +243,7 @@ static inline void swap_buf_32(unsigned char *buf, int len, void *end) */ static void mrdy_assert(struct ifx_spi_device *ifx_dev) { - int val = gpio_get_value(ifx_dev->gpio.srdy); + int val = gpiod_get_value(ifx_dev->gpio.srdy); if (!val) { if (!test_and_set_bit(IFX_SPI_STATE_TIMER_PENDING, &ifx_dev->flags)) { @@ -691,7 +690,7 @@ complete_exit: clear_bit(IFX_SPI_STATE_IO_IN_PROGRESS, &(ifx_dev->flags)); queue_length = kfifo_len(&ifx_dev->tx_fifo); - srdy = gpio_get_value(ifx_dev->gpio.srdy); + srdy = gpiod_get_value(ifx_dev->gpio.srdy); if (!srdy) ifx_spi_power_state_clear(ifx_dev, IFX_SPI_POWER_SRDY); @@ -898,7 +897,7 @@ static irqreturn_t ifx_spi_srdy_interrupt(int irq, void *dev) static irqreturn_t ifx_spi_reset_interrupt(int irq, void *dev) { struct ifx_spi_device *ifx_dev = dev; - int val = gpio_get_value(ifx_dev->gpio.reset_out); + int val = gpiod_get_value(ifx_dev->gpio.reset_out); int solreset = test_bit(MR_START, &ifx_dev->mdm_reset_state); if (val == 0) { @@ -954,14 +953,14 @@ static int ifx_spi_reset(struct ifx_spi_device *ifx_dev) * to reset properly */ set_bit(MR_START, &ifx_dev->mdm_reset_state); - gpio_set_value(ifx_dev->gpio.po, 0); - gpio_set_value(ifx_dev->gpio.reset, 0); + gpiod_set_value(ifx_dev->gpio.po, 0); + gpiod_set_value(ifx_dev->gpio.reset, 0); msleep(25); - gpio_set_value(ifx_dev->gpio.reset, 1); + gpiod_set_value(ifx_dev->gpio.reset, 1); msleep(1); - gpio_set_value(ifx_dev->gpio.po, 1); + gpiod_set_value(ifx_dev->gpio.po, 1); msleep(1); - gpio_set_value(ifx_dev->gpio.po, 0); + gpiod_set_value(ifx_dev->gpio.po, 0); ret = wait_event_timeout(ifx_dev->mdm_reset_wait, test_bit(MR_COMPLETE, &ifx_dev->mdm_reset_state), @@ -1080,107 +1079,68 @@ static int ifx_spi_spi_probe(struct spi_device *spi) goto error_ret; } - ifx_dev->gpio.reset = pl_data->rst_pmu; - ifx_dev->gpio.po = pl_data->pwr_on; - ifx_dev->gpio.mrdy = pl_data->mrdy; - ifx_dev->gpio.srdy = pl_data->srdy; - ifx_dev->gpio.reset_out = pl_data->rst_out; - - dev_info(dev, "gpios %d, %d, %d, %d, %d", - ifx_dev->gpio.reset, ifx_dev->gpio.po, ifx_dev->gpio.mrdy, - ifx_dev->gpio.srdy, ifx_dev->gpio.reset_out); - - /* Configure gpios */ - ret = gpio_request(ifx_dev->gpio.reset, "ifxModem"); - if (ret < 0) { - dev_err(dev, "Unable to allocate GPIO%d (RESET)", - ifx_dev->gpio.reset); + ifx_dev->gpio.reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ifx_dev->gpio.reset)) { + dev_err(dev, "could not obtain reset GPIO\n"); + ret = PTR_ERR(ifx_dev->gpio.reset); goto error_ret; } - ret += gpio_direction_output(ifx_dev->gpio.reset, 0); - ret += gpio_export(ifx_dev->gpio.reset, 1); - if (ret) { - dev_err(dev, "Unable to configure GPIO%d (RESET)", - ifx_dev->gpio.reset); - ret = -EBUSY; - goto error_ret2; - } - - ret = gpio_request(ifx_dev->gpio.po, "ifxModem"); - ret += gpio_direction_output(ifx_dev->gpio.po, 0); - ret += gpio_export(ifx_dev->gpio.po, 1); - if (ret) { - dev_err(dev, "Unable to configure GPIO%d (ON)", - ifx_dev->gpio.po); - ret = -EBUSY; - goto error_ret3; - } - - ret = gpio_request(ifx_dev->gpio.mrdy, "ifxModem"); - if (ret < 0) { - dev_err(dev, "Unable to allocate GPIO%d (MRDY)", - ifx_dev->gpio.mrdy); - goto error_ret3; - } - ret += gpio_export(ifx_dev->gpio.mrdy, 1); - ret += gpio_direction_output(ifx_dev->gpio.mrdy, 0); - if (ret) { - dev_err(dev, "Unable to configure GPIO%d (MRDY)", - ifx_dev->gpio.mrdy); - ret = -EBUSY; - goto error_ret4; + gpiod_set_consumer_name(ifx_dev->gpio.reset, "ifxModem reset"); + ifx_dev->gpio.po = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(ifx_dev->gpio.po)) { + dev_err(dev, "could not obtain power GPIO\n"); + ret = PTR_ERR(ifx_dev->gpio.po); + goto error_ret; } - - ret = gpio_request(ifx_dev->gpio.srdy, "ifxModem"); - if (ret < 0) { - dev_err(dev, "Unable to allocate GPIO%d (SRDY)", - ifx_dev->gpio.srdy); - ret = -EBUSY; - goto error_ret4; + gpiod_set_consumer_name(ifx_dev->gpio.po, "ifxModem power"); + ifx_dev->gpio.mrdy = devm_gpiod_get(dev, "mrdy", GPIOD_OUT_LOW); + if (IS_ERR(ifx_dev->gpio.mrdy)) { + dev_err(dev, "could not obtain mrdy GPIO\n"); + ret = PTR_ERR(ifx_dev->gpio.mrdy); + goto error_ret; } - ret += gpio_export(ifx_dev->gpio.srdy, 1); - ret += gpio_direction_input(ifx_dev->gpio.srdy); - if (ret) { - dev_err(dev, "Unable to configure GPIO%d (SRDY)", - ifx_dev->gpio.srdy); - ret = -EBUSY; - goto error_ret5; + gpiod_set_consumer_name(ifx_dev->gpio.mrdy, "ifxModem mrdy"); + ifx_dev->gpio.srdy = devm_gpiod_get(dev, "srdy", GPIOD_IN); + if (IS_ERR(ifx_dev->gpio.srdy)) { + dev_err(dev, "could not obtain srdy GPIO\n"); + ret = PTR_ERR(ifx_dev->gpio.srdy); + goto error_ret; } - - ret = gpio_request(ifx_dev->gpio.reset_out, "ifxModem"); - if (ret < 0) { - dev_err(dev, "Unable to allocate GPIO%d (RESET_OUT)", - ifx_dev->gpio.reset_out); - goto error_ret5; + gpiod_set_consumer_name(ifx_dev->gpio.srdy, "ifxModem srdy"); + ifx_dev->gpio.reset_out = devm_gpiod_get(dev, "rst_out", GPIOD_IN); + if (IS_ERR(ifx_dev->gpio.reset_out)) { + dev_err(dev, "could not obtain rst_out GPIO\n"); + ret = PTR_ERR(ifx_dev->gpio.reset_out); + goto error_ret; } - ret += gpio_export(ifx_dev->gpio.reset_out, 1); - ret += gpio_direction_input(ifx_dev->gpio.reset_out); - if (ret) { - dev_err(dev, "Unable to configure GPIO%d (RESET_OUT)", - ifx_dev->gpio.reset_out); - ret = -EBUSY; - goto error_ret6; + gpiod_set_consumer_name(ifx_dev->gpio.reset_out, "ifxModem reset out"); + ifx_dev->gpio.pmu_reset = devm_gpiod_get(dev, "pmu_reset", GPIOD_ASIS); + if (IS_ERR(ifx_dev->gpio.pmu_reset)) { + dev_err(dev, "could not obtain pmu_reset GPIO\n"); + ret = PTR_ERR(ifx_dev->gpio.pmu_reset); + goto error_ret; } + gpiod_set_consumer_name(ifx_dev->gpio.pmu_reset, "ifxModem PMU reset"); - ret = request_irq(gpio_to_irq(ifx_dev->gpio.reset_out), + ret = request_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_spi_reset_interrupt, IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, DRVNAME, ifx_dev); if (ret) { dev_err(dev, "Unable to get irq %x\n", - gpio_to_irq(ifx_dev->gpio.reset_out)); - goto error_ret6; + gpiod_to_irq(ifx_dev->gpio.reset_out)); + goto error_ret; } ret = ifx_spi_reset(ifx_dev); - ret = request_irq(gpio_to_irq(ifx_dev->gpio.srdy), + ret = request_irq(gpiod_to_irq(ifx_dev->gpio.srdy), ifx_spi_srdy_interrupt, IRQF_TRIGGER_RISING, DRVNAME, ifx_dev); if (ret) { dev_err(dev, "Unable to get irq %x", - gpio_to_irq(ifx_dev->gpio.srdy)); - goto error_ret7; + gpiod_to_irq(ifx_dev->gpio.srdy)); + goto error_ret2; } /* set pm runtime power state and register with power system */ @@ -1191,7 +1151,7 @@ static int ifx_spi_spi_probe(struct spi_device *spi) /* no outgoing tty open at this point, this just satisfies the * modem's read and should reset communication properly */ - srdy = gpio_get_value(ifx_dev->gpio.srdy); + srdy = gpiod_get_value(ifx_dev->gpio.srdy); if (srdy) { mrdy_assert(ifx_dev); @@ -1200,18 +1160,8 @@ static int ifx_spi_spi_probe(struct spi_device *spi) mrdy_set_low(ifx_dev); return 0; -error_ret7: - free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev); -error_ret6: - gpio_free(ifx_dev->gpio.srdy); -error_ret5: - gpio_free(ifx_dev->gpio.mrdy); -error_ret4: - gpio_free(ifx_dev->gpio.reset); -error_ret3: - gpio_free(ifx_dev->gpio.po); error_ret2: - gpio_free(ifx_dev->gpio.reset_out); + free_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_dev); error_ret: ifx_spi_free_device(ifx_dev); saved_ifx_dev = NULL; @@ -1235,14 +1185,8 @@ static int ifx_spi_spi_remove(struct spi_device *spi) pm_runtime_disable(&spi->dev); /* free irq */ - free_irq(gpio_to_irq(ifx_dev->gpio.reset_out), ifx_dev); - free_irq(gpio_to_irq(ifx_dev->gpio.srdy), ifx_dev); - - gpio_free(ifx_dev->gpio.srdy); - gpio_free(ifx_dev->gpio.mrdy); - gpio_free(ifx_dev->gpio.reset); - gpio_free(ifx_dev->gpio.po); - gpio_free(ifx_dev->gpio.reset_out); + free_irq(gpiod_to_irq(ifx_dev->gpio.reset_out), ifx_dev); + free_irq(gpiod_to_irq(ifx_dev->gpio.srdy), ifx_dev); /* free allocations */ ifx_spi_free_device(ifx_dev); diff --git a/drivers/tty/serial/ifx6x60.h b/drivers/tty/serial/ifx6x60.h index cacca5be7390..ecb841d928a7 100644 --- a/drivers/tty/serial/ifx6x60.h +++ b/drivers/tty/serial/ifx6x60.h @@ -10,6 +10,8 @@ #ifndef _IFX6X60_H #define _IFX6X60_H +struct gpio_desc; + #define DRVNAME "ifx6x60" #define TTYNAME "ttyIFX" @@ -94,11 +96,12 @@ struct ifx_spi_device { struct { /* gpio lines */ - unsigned short srdy; /* slave-ready gpio */ - unsigned short mrdy; /* master-ready gpio */ - unsigned short reset; /* modem-reset gpio */ - unsigned short po; /* modem-on gpio */ - unsigned short reset_out; /* modem-in-reset gpio */ + struct gpio_desc *srdy; /* slave-ready gpio */ + struct gpio_desc *mrdy; /* master-ready gpio */ + struct gpio_desc *reset; /* modem-reset gpio */ + struct gpio_desc *po; /* modem-on gpio */ + struct gpio_desc *reset_out; /* modem-in-reset gpio */ + struct gpio_desc *pmu_reset; /* PMU reset gpio */ /* state/stats */ int unack_srdy_int_nb; } gpio; diff --git a/include/linux/spi/ifx_modem.h b/include/linux/spi/ifx_modem.h index 694268c78d5d..6d19b09139d0 100644 --- a/include/linux/spi/ifx_modem.h +++ b/include/linux/spi/ifx_modem.h @@ -3,12 +3,7 @@ #define LINUX_IFX_MODEM_H struct ifx_modem_platform_data { - unsigned short rst_out; /* modem reset out */ - unsigned short pwr_on; /* power on */ - unsigned short rst_pmu; /* reset modem */ unsigned short tx_pwr; /* modem power threshold */ - unsigned short srdy; /* SRDY */ - unsigned short mrdy; /* MRDY */ unsigned char modem_type; /* Modem type */ unsigned long max_hz; /* max SPI frequency */ unsigned short use_dma:1; /* spi protocol driver supplies -- cgit v1.2.3 From 34471abfc8fedcebf7c45a5e37ef383b8bb16de3 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Sat, 22 Feb 2020 08:08:49 +0800 Subject: thermal: of-thermal: add API for getting sensor ID from DT This patch adds new API thermal_zone_of_get_sensor_id() to provide the feature of getting sensor ID from DT thermal zone's node. It's useful for thermal driver to register the specific thermal zone devices from DT in a common way. Signed-off-by: Anson Huang Reviewed-by: Dong Aisheng Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1582330132-13461-2-git-send-email-Anson.Huang@nxp.com --- drivers/thermal/of-thermal.c | 62 ++++++++++++++++++++++++++++++++------------ include/linux/thermal.h | 10 +++++++ 2 files changed, 56 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index ef0baa954ff0..874a47d6923f 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -448,6 +448,50 @@ thermal_zone_of_add_sensor(struct device_node *zone, return tzd; } +/** + * thermal_zone_of_get_sensor_id - get sensor ID from a DT thermal zone + * @tz_np: a valid thermal zone device node. + * @sensor_np: a sensor node of a valid sensor device. + * @id: the sensor ID returned if success. + * + * This function will get sensor ID from a given thermal zone node and + * the sensor node must match the temperature provider @sensor_np. + * + * Return: 0 on success, proper error code otherwise. + */ + +int thermal_zone_of_get_sensor_id(struct device_node *tz_np, + struct device_node *sensor_np, + u32 *id) +{ + struct of_phandle_args sensor_specs; + int ret; + + ret = of_parse_phandle_with_args(tz_np, + "thermal-sensors", + "#thermal-sensor-cells", + 0, + &sensor_specs); + if (ret) + return ret; + + if (sensor_specs.np != sensor_np) { + of_node_put(sensor_specs.np); + return -ENODEV; + } + + if (sensor_specs.args_count > 1) + pr_warn("%pOFn: too many cells in sensor specifier %d\n", + sensor_specs.np, sensor_specs.args_count); + + *id = sensor_specs.args_count ? sensor_specs.args[0] : 0; + + of_node_put(sensor_specs.np); + + return 0; +} +EXPORT_SYMBOL_GPL(thermal_zone_of_get_sensor_id); + /** * thermal_zone_of_sensor_register - registers a sensor to a DT thermal zone * @dev: a valid struct device pointer of a sensor device. Must contain @@ -499,36 +543,22 @@ thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void *data, sensor_np = of_node_get(dev->of_node); for_each_available_child_of_node(np, child) { - struct of_phandle_args sensor_specs; int ret, id; /* For now, thermal framework supports only 1 sensor per zone */ - ret = of_parse_phandle_with_args(child, "thermal-sensors", - "#thermal-sensor-cells", - 0, &sensor_specs); + ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id); if (ret) continue; - if (sensor_specs.args_count >= 1) { - id = sensor_specs.args[0]; - WARN(sensor_specs.args_count > 1, - "%pOFn: too many cells in sensor specifier %d\n", - sensor_specs.np, sensor_specs.args_count); - } else { - id = 0; - } - - if (sensor_specs.np == sensor_np && id == sensor_id) { + if (id == sensor_id) { tzd = thermal_zone_of_add_sensor(child, sensor_np, data, ops); if (!IS_ERR(tzd)) tzd->ops->set_mode(tzd, THERMAL_DEVICE_ENABLED); - of_node_put(sensor_specs.np); of_node_put(child); goto exit; } - of_node_put(sensor_specs.np); } exit: of_node_put(sensor_np); diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 126913c6a53b..53e6f677761f 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -364,6 +364,9 @@ struct thermal_trip { /* Function declarations */ #ifdef CONFIG_THERMAL_OF +int thermal_zone_of_get_sensor_id(struct device_node *tz_np, + struct device_node *sensor_np, + u32 *id); struct thermal_zone_device * thermal_zone_of_sensor_register(struct device *dev, int id, void *data, const struct thermal_zone_of_device_ops *ops); @@ -375,6 +378,13 @@ struct thermal_zone_device *devm_thermal_zone_of_sensor_register( void devm_thermal_zone_of_sensor_unregister(struct device *dev, struct thermal_zone_device *tz); #else + +static int thermal_zone_of_get_sensor_id(struct device_node *tz_np, + struct device_node *sensor_np, + u32 *id) +{ + return -ENOENT; +} static inline struct thermal_zone_device * thermal_zone_of_sensor_register(struct device *dev, int id, void *data, const struct thermal_zone_of_device_ops *ops) -- cgit v1.2.3 From 15a26319c41962b7cf87603bc51d667f43d2ca67 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 3 Mar 2020 16:04:43 +0800 Subject: thermal: Fix build warning of !defined(CONFIG_THERMAL_OF) Add "inline" to thermal_zone_of_get_sensor_id() function to avoid below build warning of !defined(CONFIG_THERMAL_OF). In file included from drivers/hwmon/hwmon.c:22: include/linux/thermal.h:382:12: warning: 'thermal_zone_of_get_sensor_id' defined but not used [-Wunused-function] 382 | static int thermal_zone_of_get_sensor_id(struct device_node *tz_np, Signed-off-by: Anson Huang Reported-by: Stephen Rothwell Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/1583222684-10229-1-git-send-email-Anson.Huang@nxp.com --- include/linux/thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 53e6f677761f..c91b1e344d56 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -379,7 +379,7 @@ void devm_thermal_zone_of_sensor_unregister(struct device *dev, struct thermal_zone_device *tz); #else -static int thermal_zone_of_get_sensor_id(struct device_node *tz_np, +static inline int thermal_zone_of_get_sensor_id(struct device_node *tz_np, struct device_node *sensor_np, u32 *id) { -- cgit v1.2.3 From 9e2b4be377f0d715d9d910507890f9620cc22a9d Mon Sep 17 00:00:00 2001 From: Nayna Jain Date: Sun, 8 Mar 2020 20:57:51 -0400 Subject: ima: add a new CONFIG for loading arch-specific policies Every time a new architecture defines the IMA architecture specific functions - arch_ima_get_secureboot() and arch_ima_get_policy(), the IMA include file needs to be updated. To avoid this "noise", this patch defines a new IMA Kconfig IMA_SECURE_AND_OR_TRUSTED_BOOT option, allowing the different architectures to select it. Suggested-by: Linus Torvalds Signed-off-by: Nayna Jain Acked-by: Ard Biesheuvel Acked-by: Philipp Rudo (s390) Acked-by: Michael Ellerman (powerpc) Signed-off-by: Mimi Zohar --- arch/powerpc/Kconfig | 1 + arch/s390/Kconfig | 1 + arch/s390/kernel/Makefile | 2 +- arch/x86/Kconfig | 1 + arch/x86/kernel/Makefile | 4 +--- include/linux/ima.h | 3 +-- security/integrity/ima/Kconfig | 7 +++++++ 7 files changed, 13 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 497b7d0b2d7e..5b9f1cba2a44 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -979,6 +979,7 @@ config PPC_SECURE_BOOT bool depends on PPC_POWERNV depends on IMA_ARCH_POLICY + imply IMA_SECURE_AND_OR_TRUSTED_BOOT help Systems with firmware secure boot enabled need to define security policies to extend secure boot to the OS. This config allows a user diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 8abe77536d9d..59c216af6264 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -195,6 +195,7 @@ config S390 select ARCH_HAS_FORCE_DMA_UNENCRYPTED select SWIOTLB select GENERIC_ALLOCATOR + imply IMA_SECURE_AND_OR_TRUSTED_BOOT config SCHED_OMIT_FRAME_POINTER diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index 2b1203cf7be6..578a6fa82ea4 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -70,7 +70,7 @@ obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o -obj-$(CONFIG_IMA) += ima_arch.o +obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf_common.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index beea77046f9b..dcf5b1729f7c 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -230,6 +230,7 @@ config X86 select VIRT_TO_BUS select X86_FEATURE_NAMES if PROC_FS select PROC_PID_ARCH_STATUS if PROC_FS + imply IMA_SECURE_AND_OR_TRUSTED_BOOT if EFI config INSTRUCTION_DECODER def_bool y diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 9b294c13809a..cfef37a27def 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -154,6 +154,4 @@ ifeq ($(CONFIG_X86_64),y) obj-y += vsmp_64.o endif -ifdef CONFIG_EFI -obj-$(CONFIG_IMA) += ima_arch.o -endif +obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o diff --git a/include/linux/ima.h b/include/linux/ima.h index 1659217e9b60..aefe758f4466 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -30,8 +30,7 @@ extern void ima_kexec_cmdline(const void *buf, int size); extern void ima_add_kexec_buffer(struct kimage *image); #endif -#if (defined(CONFIG_X86) && defined(CONFIG_EFI)) || defined(CONFIG_S390) \ - || defined(CONFIG_PPC_SECURE_BOOT) +#ifdef CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT extern bool arch_ima_get_secureboot(void); extern const char * const *arch_get_ima_policy(void); #else diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 3f3ee4e2eb0d..edde88dbe576 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -327,3 +327,10 @@ config IMA_QUEUE_EARLY_BOOT_KEYS depends on IMA_MEASURE_ASYMMETRIC_KEYS depends on SYSTEM_TRUSTED_KEYRING default y + +config IMA_SECURE_AND_OR_TRUSTED_BOOT + bool + depends on IMA_ARCH_POLICY + help + This option is selected by architectures to enable secure and/or + trusted boot based on IMA runtime policies. -- cgit v1.2.3 From 3cd54a4c3c32f38fe0a57edeed10f95944dd0878 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 4 Mar 2020 14:44:05 -0800 Subject: crypto: aead - improve documentation for scatterlist layout Properly document the scatterlist layout for AEAD ciphers. Reported-by: Gilad Ben-Yossef Cc: Stephan Mueller Signed-off-by: Eric Biggers Signed-off-by: Herbert Xu --- include/crypto/aead.h | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/crypto/aead.h b/include/crypto/aead.h index 1b3ebe8593c0..62c68550aab6 100644 --- a/include/crypto/aead.h +++ b/include/crypto/aead.h @@ -43,27 +43,33 @@ * * Memory Structure: * - * To support the needs of the most prominent user of AEAD ciphers, namely - * IPSEC, the AEAD ciphers have a special memory layout the caller must adhere - * to. - * - * The scatter list pointing to the input data must contain: - * - * * for RFC4106 ciphers, the concatenation of - * associated authentication data || IV || plaintext or ciphertext. Note, the - * same IV (buffer) is also set with the aead_request_set_crypt call. Note, - * the API call of aead_request_set_ad must provide the length of the AAD and - * the IV. The API call of aead_request_set_crypt only points to the size of - * the input plaintext or ciphertext. - * - * * for "normal" AEAD ciphers, the concatenation of - * associated authentication data || plaintext or ciphertext. - * - * It is important to note that if multiple scatter gather list entries form - * the input data mentioned above, the first entry must not point to a NULL - * buffer. If there is any potential where the AAD buffer can be NULL, the - * calling code must contain a precaution to ensure that this does not result - * in the first scatter gather list entry pointing to a NULL buffer. + * The source scatterlist must contain the concatenation of + * associated data || plaintext or ciphertext. + * + * The destination scatterlist has the same layout, except that the plaintext + * (resp. ciphertext) will grow (resp. shrink) by the authentication tag size + * during encryption (resp. decryption). + * + * In-place encryption/decryption is enabled by using the same scatterlist + * pointer for both the source and destination. + * + * Even in the out-of-place case, space must be reserved in the destination for + * the associated data, even though it won't be written to. This makes the + * in-place and out-of-place cases more consistent. It is permissible for the + * "destination" associated data to alias the "source" associated data. + * + * As with the other scatterlist crypto APIs, zero-length scatterlist elements + * are not allowed in the used part of the scatterlist. Thus, if there is no + * associated data, the first element must point to the plaintext/ciphertext. + * + * To meet the needs of IPsec, a special quirk applies to rfc4106, rfc4309, + * rfc4543, and rfc7539esp ciphers. For these ciphers, the final 'ivsize' bytes + * of the associated data buffer must contain a second copy of the IV. This is + * in addition to the copy passed to aead_request_set_crypt(). These two IV + * copies must not differ; different implementations of the same algorithm may + * behave differently in that case. Note that the algorithm might not actually + * treat the IV as associated data; nevertheless the length passed to + * aead_request_set_ad() must include it. */ struct crypto_aead; -- cgit v1.2.3 From 0bc68af9137dc3f30b161de4ce546c7799f88d1e Mon Sep 17 00:00:00 2001 From: Shivamurthy Shastri Date: Wed, 11 Mar 2020 18:57:33 +0100 Subject: mtd: spinand: micron: identify SPI NAND device with Continuous Read mode Add SPINAND_HAS_CR_FEAT_BIT flag to identify the SPI NAND device with the Continuous Read mode. Some of the Micron SPI NAND devices have the "Continuous Read" feature enabled by default, which does not fit the subsystem needs. In this mode, the READ CACHE command doesn't require the starting column address. The device always output the data starting from the first column of the cache register, and once the end of the cache register reached, the data output continues through the next page. With the continuous read mode, it is possible to read out the entire block using a single READ command, and once the end of the block reached, the output pins become High-Z state. However, during this mode the read command doesn't output the OOB area. Hence, we disable the feature at probe time. Signed-off-by: Shivamurthy Shastri Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/linux-mtd/20200311175735.2007-5-sshivamurthy@micron.com --- drivers/mtd/nand/spi/micron.c | 16 ++++++++++++++++ include/linux/mtd/spinand.h | 1 + 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index 26925714a9fb..956f7710aca2 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -18,6 +18,8 @@ #define MICRON_STATUS_ECC_4TO6_BITFLIPS (3 << 4) #define MICRON_STATUS_ECC_7TO8_BITFLIPS (5 << 4) +#define MICRON_CFG_CR BIT(0) + static SPINAND_OP_VARIANTS(read_cache_variants, SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0), SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0), @@ -137,7 +139,21 @@ static const struct spinand_info micron_spinand_table[] = { micron_8_ecc_get_status)), }; +static int micron_spinand_init(struct spinand_device *spinand) +{ + /* + * M70A device series enable Continuous Read feature at Power-up, + * which is not supported. Disable this bit to avoid any possible + * failure. + */ + if (spinand->flags & SPINAND_HAS_CR_FEAT_BIT) + return spinand_upd_cfg(spinand, MICRON_CFG_CR, 0); + + return 0; +} + static const struct spinand_manufacturer_ops micron_spinand_manuf_ops = { + .init = micron_spinand_init, }; const struct spinand_manufacturer micron_spinand_manufacturer = { diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index f4c4ae87181b..1077c45721ff 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -284,6 +284,7 @@ struct spinand_ecc_info { }; #define SPINAND_HAS_QE_BIT BIT(0) +#define SPINAND_HAS_CR_FEAT_BIT BIT(1) /** * struct spinand_info - Structure used to describe SPI NAND chips -- cgit v1.2.3 From 30a2da7b7e225ef6c87a660419ea04d3cef3f6a7 Mon Sep 17 00:00:00 2001 From: Sahitya Tummala Date: Wed, 11 Mar 2020 16:07:50 +0530 Subject: block: Fix use-after-free issue accessing struct io_cq There is a potential race between ioc_release_fn() and ioc_clear_queue() as shown below, due to which below kernel crash is observed. It also can result into use-after-free issue. context#1: context#2: ioc_release_fn() __ioc_clear_queue() gets the same icq ->spin_lock(&ioc->lock); ->spin_lock(&ioc->lock); ->ioc_destroy_icq(icq); ->list_del_init(&icq->q_node); ->call_rcu(&icq->__rcu_head, icq_free_icq_rcu); ->spin_unlock(&ioc->lock); ->ioc_destroy_icq(icq); ->hlist_del_init(&icq->ioc_node); This results into below crash as this memory is now used by icq->__rcu_head in context#1. There is a chance that icq could be free'd as well. 22150.386550: <6> Unable to handle kernel write to read-only memory at virtual address ffffffaa8d31ca50 ... Call trace: 22150.607350: <2> ioc_destroy_icq+0x44/0x110 22150.611202: <2> ioc_clear_queue+0xac/0x148 22150.615056: <2> blk_cleanup_queue+0x11c/0x1a0 22150.619174: <2> __scsi_remove_device+0xdc/0x128 22150.623465: <2> scsi_forget_host+0x2c/0x78 22150.627315: <2> scsi_remove_host+0x7c/0x2a0 22150.631257: <2> usb_stor_disconnect+0x74/0xc8 22150.635371: <2> usb_unbind_interface+0xc8/0x278 22150.639665: <2> device_release_driver_internal+0x198/0x250 22150.644897: <2> device_release_driver+0x24/0x30 22150.649176: <2> bus_remove_device+0xec/0x140 22150.653204: <2> device_del+0x270/0x460 22150.656712: <2> usb_disable_device+0x120/0x390 22150.660918: <2> usb_disconnect+0xf4/0x2e0 22150.664684: <2> hub_event+0xd70/0x17e8 22150.668197: <2> process_one_work+0x210/0x480 22150.672222: <2> worker_thread+0x32c/0x4c8 Fix this by adding a new ICQ_DESTROYED flag in ioc_destroy_icq() to indicate this icq is once marked as destroyed. Also, ensure __ioc_clear_queue() is accessing icq within rcu_read_lock/unlock so that icq doesn't get free'd up while it is still using it. Signed-off-by: Sahitya Tummala Co-developed-by: Pradeep P V K Signed-off-by: Pradeep P V K Signed-off-by: Jens Axboe --- block/blk-ioc.c | 7 +++++++ include/linux/iocontext.h | 1 + 2 files changed, 8 insertions(+) (limited to 'include') diff --git a/block/blk-ioc.c b/block/blk-ioc.c index 5ed59ac6ae58..9df50fb507ca 100644 --- a/block/blk-ioc.c +++ b/block/blk-ioc.c @@ -84,6 +84,7 @@ static void ioc_destroy_icq(struct io_cq *icq) * making it impossible to determine icq_cache. Record it in @icq. */ icq->__rcu_icq_cache = et->icq_cache; + icq->flags |= ICQ_DESTROYED; call_rcu(&icq->__rcu_head, icq_free_icq_rcu); } @@ -212,15 +213,21 @@ static void __ioc_clear_queue(struct list_head *icq_list) { unsigned long flags; + rcu_read_lock(); while (!list_empty(icq_list)) { struct io_cq *icq = list_entry(icq_list->next, struct io_cq, q_node); struct io_context *ioc = icq->ioc; spin_lock_irqsave(&ioc->lock, flags); + if (icq->flags & ICQ_DESTROYED) { + spin_unlock_irqrestore(&ioc->lock, flags); + continue; + } ioc_destroy_icq(icq); spin_unlock_irqrestore(&ioc->lock, flags); } + rcu_read_unlock(); } /** diff --git a/include/linux/iocontext.h b/include/linux/iocontext.h index dba15ca8e60b..1dcd9198beb7 100644 --- a/include/linux/iocontext.h +++ b/include/linux/iocontext.h @@ -8,6 +8,7 @@ enum { ICQ_EXITED = 1 << 2, + ICQ_DESTROYED = 1 << 3, }; /* -- cgit v1.2.3 From f17a14789e55f45514d1d72a4e51dcc6bdd8d463 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 12 Mar 2020 10:52:14 +0100 Subject: ASoC: export DPCM runtime update functions This makes DPCM runtime update functions available for external calling. As an example, virtualised ASoC component drivers may need to call these when managing shared DAPM routes that are used by more than one driver (i.e. when host driver and guest drivers have a DAPM path from guest PCM to host DAI where some parts are owned by host driver and others by guest driver). Signed-off-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20200312095214.15126-3-guennadi.liakhovetski@linux.intel.com Signed-off-by: Mark Brown --- include/sound/soc-dpcm.h | 4 ++-- sound/soc/soc-dapm.c | 8 ++++---- sound/soc/soc-pcm.c | 5 ++++- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 40223577ec4a..0f6c50b17bba 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -132,8 +132,8 @@ int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe, struct snd_pcm_substream * snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream); -/* internal use only */ -int soc_dpcm_runtime_update(struct snd_soc_card *); +/* update audio routing between PCMs and any DAI links */ +int snd_soc_dpcm_runtime_update(struct snd_soc_card *card); #ifdef CONFIG_DEBUG_FS void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index e00a465a7c32..d5eb52fe115b 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -2291,7 +2291,7 @@ int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm, card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power); @@ -2356,7 +2356,7 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm, card->update = NULL; mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return ret; } EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power); @@ -3396,7 +3396,7 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return change; } @@ -3501,7 +3501,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, mutex_unlock(&card->dapm_mutex); if (ret > 0) - soc_dpcm_runtime_update(card); + snd_soc_dpcm_runtime_update(card); return change; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index bf4c5dc903ce..2b915f41e955 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -295,6 +295,7 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream) { snd_soc_runtime_action(rtd, stream, 1); } +EXPORT_SYMBOL_GPL(snd_soc_runtime_activate); /** * snd_soc_runtime_deactivate() - Decrement active count for PCM runtime components @@ -310,6 +311,7 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) { snd_soc_runtime_action(rtd, stream, -1); } +EXPORT_SYMBOL_GPL(snd_soc_runtime_deactivate); /** * snd_soc_runtime_ignore_pmdown_time() - Check whether to ignore the power down delay @@ -2969,7 +2971,7 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new) /* Called by DAPM mixer/mux changes to update audio routing between PCMs and * any DAI links. */ -int soc_dpcm_runtime_update(struct snd_soc_card *card) +int snd_soc_dpcm_runtime_update(struct snd_soc_card *card) { struct snd_soc_pcm_runtime *fe; int ret = 0; @@ -2993,6 +2995,7 @@ out: mutex_unlock(&card->mutex); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update); static void dpcm_fe_dai_cleanup(struct snd_pcm_substream *fe_substream) { -- cgit v1.2.3 From 9acb9fe18d863aacc99948963f8d5d447dc311be Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 10 Mar 2020 20:50:07 +0800 Subject: PCI: Add Loongson vendor ID Add the Loongson vendor ID to pci_ids.h to be used by the controller driver in the future. The Loongson vendor ID can be found at the following link: https://git.kernel.org/pub/scm/utils/pciutils/pciutils.git/tree/pci.ids Signed-off-by: Tiezhu Yang Signed-off-by: Jens Axboe --- include/linux/pci_ids.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 352c0d708720..977e66875a96 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -148,6 +148,8 @@ /* Vendors and devices. Sort key: vendor first, device next. */ +#define PCI_VENDOR_ID_LOONGSON 0x0014 + #define PCI_VENDOR_ID_TTTECH 0x0357 #define PCI_DEVICE_ID_TTTECH_MC322 0x000a -- cgit v1.2.3 From 9243c6f3e012a92dd900d97ef45efaf8a8edc448 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Sat, 7 Mar 2020 15:56:59 +0100 Subject: block: Document genhd capability flags The kernel documentation includes a brief section about genhd capabilities, but it turns out that the only documented capability (GENHD_FL_MEDIA_CHANGE_NOTIFY) isn't used any more. This patch removes that flag, and documents the rest, based on my understanding of the current uses of these flags in the kernel. The documentation is kept in the header file, alongside the declarations, in the hope that it will be kept up-to-date in future; the kernel documentation is changed to include the documentation generated from the header file. Because the ultimate goal is to provide some end-user documentation (or end-administrator documentation), the comments are perhaps more user-oriented than might be expected. Since the values are shown to users in hexadecimal, the documentation lists them in hexadecimal, and the constant declarations are adjusted to match. Reviewed-by: Matthew Wilcox (Oracle) Signed-off-by: Stephen Kitt Signed-off-by: Jens Axboe --- Documentation/block/capability.rst | 16 +++------ include/linux/genhd.h | 69 ++++++++++++++++++++++++++++++++------ 2 files changed, 62 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/Documentation/block/capability.rst b/Documentation/block/capability.rst index 2cf258d64bbe..160a5148b915 100644 --- a/Documentation/block/capability.rst +++ b/Documentation/block/capability.rst @@ -2,17 +2,9 @@ Generic Block Device Capability =============================== -This file documents the sysfs file block//capability +This file documents the sysfs file ``block//capability``. -capability is a hex word indicating which capabilities a specific disk -supports. For more information on bits not listed here, see -include/linux/genhd.h +``capability`` is a bitfield, printed in hexadecimal, indicating which +capabilities a specific block device supports: -GENHD_FL_MEDIA_CHANGE_NOTIFY ----------------------------- - -Value: 4 - -When this bit is set, the disk supports Asynchronous Notification -of media change events. These events will be broadcast to user -space via kernel uevent. +.. kernel-doc:: include/linux/genhd.h diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 6fbe58538ad6..e7244fb6b6ad 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -133,17 +133,64 @@ struct hd_struct { struct rcu_work rcu_work; }; -#define GENHD_FL_REMOVABLE 1 -/* 2 is unused */ -#define GENHD_FL_MEDIA_CHANGE_NOTIFY 4 -#define GENHD_FL_CD 8 -#define GENHD_FL_UP 16 -#define GENHD_FL_SUPPRESS_PARTITION_INFO 32 -#define GENHD_FL_EXT_DEVT 64 /* allow extended devt */ -#define GENHD_FL_NATIVE_CAPACITY 128 -#define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 256 -#define GENHD_FL_NO_PART_SCAN 512 -#define GENHD_FL_HIDDEN 1024 +/** + * DOC: genhd capability flags + * + * ``GENHD_FL_REMOVABLE`` (0x0001): indicates that the block device + * gives access to removable media. + * When set, the device remains present even when media is not + * inserted. + * Must not be set for devices which are removed entirely when the + * media is removed. + * + * ``GENHD_FL_CD`` (0x0008): the block device is a CD-ROM-style + * device. + * Affects responses to the ``CDROM_GET_CAPABILITY`` ioctl. + * + * ``GENHD_FL_UP`` (0x0010): indicates that the block device is "up", + * with a similar meaning to network interfaces. + * + * ``GENHD_FL_SUPPRESS_PARTITION_INFO`` (0x0020): don't include + * partition information in ``/proc/partitions`` or in the output of + * printk_all_partitions(). + * Used for the null block device and some MMC devices. + * + * ``GENHD_FL_EXT_DEVT`` (0x0040): the driver supports extended + * dynamic ``dev_t``, i.e. it wants extended device numbers + * (``BLOCK_EXT_MAJOR``). + * This affects the maximum number of partitions. + * + * ``GENHD_FL_NATIVE_CAPACITY`` (0x0080): based on information in the + * partition table, the device's capacity has been extended to its + * native capacity; i.e. the device has hidden capacity used by one + * of the partitions (this is a flag used so that native capacity is + * only ever unlocked once). + * + * ``GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE`` (0x0100): event polling is + * blocked whenever a writer holds an exclusive lock. + * + * ``GENHD_FL_NO_PART_SCAN`` (0x0200): partition scanning is disabled. + * Used for loop devices in their default settings and some MMC + * devices. + * + * ``GENHD_FL_HIDDEN`` (0x0400): the block device is hidden; it + * doesn't produce events, doesn't appear in sysfs, and doesn't have + * an associated ``bdev``. + * Implies ``GENHD_FL_SUPPRESS_PARTITION_INFO`` and + * ``GENHD_FL_NO_PART_SCAN``. + * Used for multipath devices. + */ +#define GENHD_FL_REMOVABLE 0x0001 +/* 2 is unused (used to be GENHD_FL_DRIVERFS) */ +/* 4 is unused (used to be GENHD_FL_MEDIA_CHANGE_NOTIFY) */ +#define GENHD_FL_CD 0x0008 +#define GENHD_FL_UP 0x0010 +#define GENHD_FL_SUPPRESS_PARTITION_INFO 0x0020 +#define GENHD_FL_EXT_DEVT 0x0040 +#define GENHD_FL_NATIVE_CAPACITY 0x0080 +#define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 0x0100 +#define GENHD_FL_NO_PART_SCAN 0x0200 +#define GENHD_FL_HIDDEN 0x0400 enum { DISK_EVENT_MEDIA_CHANGE = 1 << 0, /* media changed */ -- cgit v1.2.3 From b53df2e7442c73a932fb74228147fb946e531585 Mon Sep 17 00:00:00 2001 From: Shin'ichiro Kawasaki Date: Fri, 21 Feb 2020 10:37:08 +0900 Subject: block: Fix partition support for host aware zoned block devices Commit b72053072c0b ("block: allow partitions on host aware zone devices") introduced the helper function disk_has_partitions() to check if a given disk has valid partitions. However, since this function result directly depends on the disk partition table length rather than the actual existence of valid partitions in the table, it returns true even after all partitions are removed from the disk. For host aware zoned block devices, this results in zone management support to be kept disabled even after removing all partitions. Fix this by changing disk_has_partitions() to walk through the partition table entries and return true if and only if a valid non-zero size partition is found. Fixes: b72053072c0b ("block: allow partitions on host aware zone devices") Cc: stable@vger.kernel.org # 5.5 Reviewed-by: Damien Le Moal Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Shin'ichiro Kawasaki Signed-off-by: Jens Axboe --- block/genhd.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/genhd.h | 13 +------------ 2 files changed, 37 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index ff6268970ddc..9c2e13ce0d19 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -301,6 +301,42 @@ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) } EXPORT_SYMBOL_GPL(disk_map_sector_rcu); +/** + * disk_has_partitions + * @disk: gendisk of interest + * + * Walk through the partition table and check if valid partition exists. + * + * CONTEXT: + * Don't care. + * + * RETURNS: + * True if the gendisk has at least one valid non-zero size partition. + * Otherwise false. + */ +bool disk_has_partitions(struct gendisk *disk) +{ + struct disk_part_tbl *ptbl; + int i; + bool ret = false; + + rcu_read_lock(); + ptbl = rcu_dereference(disk->part_tbl); + + /* Iterate partitions skipping the whole device at index 0 */ + for (i = 1; i < ptbl->len; i++) { + if (rcu_dereference(ptbl->part[i])) { + ret = true; + break; + } + } + + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL_GPL(disk_has_partitions); + /* * Can be deleted altogether. Later. * diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 6fbe58538ad6..07dc91835b98 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -245,18 +245,6 @@ static inline bool disk_part_scan_enabled(struct gendisk *disk) !(disk->flags & GENHD_FL_NO_PART_SCAN); } -static inline bool disk_has_partitions(struct gendisk *disk) -{ - bool ret = false; - - rcu_read_lock(); - if (rcu_dereference(disk->part_tbl)->len > 1) - ret = true; - rcu_read_unlock(); - - return ret; -} - static inline dev_t disk_devt(struct gendisk *disk) { return MKDEV(disk->major, disk->first_minor); @@ -298,6 +286,7 @@ extern void disk_part_iter_exit(struct disk_part_iter *piter); extern struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector); +bool disk_has_partitions(struct gendisk *disk); /* * Macros to operate on percpu disk statistics: -- cgit v1.2.3 From 58def851063d2178c10622e436897e6285850077 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 3 Mar 2020 10:37:34 +0100 Subject: libata: drop BPRINTK() No users, drop it. Signed-off-by: Hannes Reinecke Reviewed-by: Bartlomiej Zolnierkiewicz Signed-off-by: Jens Axboe --- include/linux/libata.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/libata.h b/include/linux/libata.h index 2ca9b7056a82..710e09dae910 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -57,8 +57,6 @@ #define VPRINTK(fmt, args...) #endif /* ATA_DEBUG */ -#define BPRINTK(fmt, args...) if (ap->flags & ATA_FLAG_DEBUGMSG) printk(KERN_ERR "%s: " fmt, __func__, ## args) - #define ata_print_version_once(dev, version) \ ({ \ static bool __print_once; \ -- cgit v1.2.3 From a5a8cb966a6136ef91b8420ccec6aca1cbb58a4a Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 12 Mar 2020 11:31:37 +0100 Subject: media: dt-bindings: display: add sdtv-standards defines Add defines which can be included to easily describe the supported standard tv norms 'sdtv-standards' within the device tree. Signed-off-by: Marco Felsch Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/dt-bindings/display/sdtv-standards.h | 76 ++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 include/dt-bindings/display/sdtv-standards.h (limited to 'include') diff --git a/include/dt-bindings/display/sdtv-standards.h b/include/dt-bindings/display/sdtv-standards.h new file mode 100644 index 000000000000..fbc1a3db2ea7 --- /dev/null +++ b/include/dt-bindings/display/sdtv-standards.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only or X11 */ +/* + * Copyright 2019 Pengutronix, Marco Felsch + */ + +#ifndef _DT_BINDINGS_DISPLAY_SDTV_STDS_H +#define _DT_BINDINGS_DISPLAY_SDTV_STDS_H + +/* + * Attention: Keep the SDTV_STD_* bit definitions in sync with + * include/uapi/linux/videodev2.h V4L2_STD_* bit definitions. + */ +/* One bit for each standard */ +#define SDTV_STD_PAL_B 0x00000001 +#define SDTV_STD_PAL_B1 0x00000002 +#define SDTV_STD_PAL_G 0x00000004 +#define SDTV_STD_PAL_H 0x00000008 +#define SDTV_STD_PAL_I 0x00000010 +#define SDTV_STD_PAL_D 0x00000020 +#define SDTV_STD_PAL_D1 0x00000040 +#define SDTV_STD_PAL_K 0x00000080 + +#define SDTV_STD_PAL (SDTV_STD_PAL_B | \ + SDTV_STD_PAL_B1 | \ + SDTV_STD_PAL_G | \ + SDTV_STD_PAL_H | \ + SDTV_STD_PAL_I | \ + SDTV_STD_PAL_D | \ + SDTV_STD_PAL_D1 | \ + SDTV_STD_PAL_K) + +#define SDTV_STD_PAL_M 0x00000100 +#define SDTV_STD_PAL_N 0x00000200 +#define SDTV_STD_PAL_Nc 0x00000400 +#define SDTV_STD_PAL_60 0x00000800 + +#define SDTV_STD_NTSC_M 0x00001000 /* BTSC */ +#define SDTV_STD_NTSC_M_JP 0x00002000 /* EIA-J */ +#define SDTV_STD_NTSC_443 0x00004000 +#define SDTV_STD_NTSC_M_KR 0x00008000 /* FM A2 */ + +#define SDTV_STD_NTSC (SDTV_STD_NTSC_M | \ + SDTV_STD_NTSC_M_JP | \ + SDTV_STD_NTSC_M_KR) + +#define SDTV_STD_SECAM_B 0x00010000 +#define SDTV_STD_SECAM_D 0x00020000 +#define SDTV_STD_SECAM_G 0x00040000 +#define SDTV_STD_SECAM_H 0x00080000 +#define SDTV_STD_SECAM_K 0x00100000 +#define SDTV_STD_SECAM_K1 0x00200000 +#define SDTV_STD_SECAM_L 0x00400000 +#define SDTV_STD_SECAM_LC 0x00800000 + +#define SDTV_STD_SECAM (SDTV_STD_SECAM_B | \ + SDTV_STD_SECAM_D | \ + SDTV_STD_SECAM_G | \ + SDTV_STD_SECAM_H | \ + SDTV_STD_SECAM_K | \ + SDTV_STD_SECAM_K1 | \ + SDTV_STD_SECAM_L | \ + SDTV_STD_SECAM_LC) + +/* Standards for Countries with 60Hz Line frequency */ +#define SDTV_STD_525_60 (SDTV_STD_PAL_M | \ + SDTV_STD_PAL_60 | \ + SDTV_STD_NTSC | \ + SDTV_STD_NTSC_443) + +/* Standards for Countries with 50Hz Line frequency */ +#define SDTV_STD_625_50 (SDTV_STD_PAL | \ + SDTV_STD_PAL_N | \ + SDTV_STD_PAL_Nc | \ + SDTV_STD_SECAM) + +#endif /* _DT_BINDINGS_DISPLAY_SDTV_STDS_H */ -- cgit v1.2.3 From f8c8ee61189b8151525e4f10828d53865ef847e4 Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 12 Mar 2020 11:31:38 +0100 Subject: media: v4l: link dt-bindings and uapi Since we expose the definition to the dt-bindings we need to keep those definitions in sync. To address this the patch adds a simple cross reference to the dt-bindings. Signed-off-by: Marco Felsch Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/uapi/linux/videodev2.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 5f9357dcb060..f48e47e230c3 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1242,6 +1242,10 @@ struct v4l2_selection { typedef __u64 v4l2_std_id; +/* + * Attention: Keep the V4L2_STD_* bit definitions in sync with + * include/dt-bindings/display/sdtv-standards.h SDTV_STD_* bit definitions. + */ /* one bit for each */ #define V4L2_STD_PAL_B ((v4l2_std_id)0x00000001) #define V4L2_STD_PAL_B1 ((v4l2_std_id)0x00000002) -- cgit v1.2.3 From 5e316ff52cd717e98dacdb8e006ee40cc6aa1e8b Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 12 Mar 2020 11:31:41 +0100 Subject: media: v4l2-fwnode: add endpoint id field to v4l2_fwnode_link A link is between two endpoints not between two ports to be more precise. Add the local_id/remote_id field which stores the endpoint reg/port property to the link. Now the link holds all necessary information about a link. Signed-off-by: Marco Felsch Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 2 ++ include/media/v4l2-fwnode.h | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 316e19615d88..cd5325b752f8 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -565,6 +565,7 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, memset(link, 0, sizeof(*link)); fwnode_graph_parse_endpoint(fwnode, &fwep); + link->local_id = fwep.id; link->local_port = fwep.port; link->local_node = fwnode_graph_get_port_parent(fwnode); @@ -575,6 +576,7 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, } fwnode_graph_parse_endpoint(fwnode, &fwep); + link->remote_id = fwep.id; link->remote_port = fwep.port; link->remote_node = fwnode_graph_get_port_parent(fwnode); diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index f6a7bcd13197..7bba6dfa3fd6 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -113,14 +113,18 @@ struct v4l2_fwnode_endpoint { * struct v4l2_fwnode_link - a link between two endpoints * @local_node: pointer to device_node of this endpoint * @local_port: identifier of the port this endpoint belongs to + * @local_id: identifier of the id this endpoint belongs to * @remote_node: pointer to device_node of the remote endpoint * @remote_port: identifier of the port the remote endpoint belongs to + * @remote_id: identifier of the id the remote endpoint belongs to */ struct v4l2_fwnode_link { struct fwnode_handle *local_node; unsigned int local_port; + unsigned int local_id; struct fwnode_handle *remote_node; unsigned int remote_port; + unsigned int remote_id; }; /** -- cgit v1.2.3 From a5e1deec4ebbf090226eb821c2295590b509625f Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 12 Mar 2020 11:31:42 +0100 Subject: media: v4l2-fwnode: add v4l2_fwnode_connector Currently every driver needs to parse the connector endpoints by it self. This is the initial work to make this generic. A generic connector has common members and connector specific members. The common members are: - type - label (optional) - links - nr_of_links The specific members are stored within a union, since only one of them can be available at the time. Since this is the initial support the patch adds only the analog-connector specific ones. Signed-off-by: Marco Felsch Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-fwnode.h | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'include') diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 7bba6dfa3fd6..6cf39d447b10 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -127,6 +127,63 @@ struct v4l2_fwnode_link { unsigned int remote_id; }; +/** + * enum v4l2_connector_type - connector type + * @V4L2_CONN_UNKNOWN: unknown connector type, no V4L2 connector configuration + * @V4L2_CONN_COMPOSITE: analog composite connector + * @V4L2_CONN_SVIDEO: analog svideo connector + */ +enum v4l2_connector_type { + V4L2_CONN_UNKNOWN, + V4L2_CONN_COMPOSITE, + V4L2_CONN_SVIDEO, +}; + +/** + * struct v4l2_connector_link - connector link data structure + * @head: structure to be used to add the link to the + * &struct v4l2_fwnode_connector + * @fwnode_link: &struct v4l2_fwnode_link link between the connector and the + * device the connector belongs to. + */ +struct v4l2_connector_link { + struct list_head head; + struct v4l2_fwnode_link fwnode_link; +}; + +/** + * struct v4l2_fwnode_connector_analog - analog connector data structure + * @sdtv_stds: sdtv standards this connector supports, set to V4L2_STD_ALL + * if no restrictions are specified. + */ +struct v4l2_fwnode_connector_analog { + v4l2_std_id sdtv_stds; +}; + +/** + * struct v4l2_fwnode_connector - the connector data structure + * @name: the connector device name + * @label: optional connector label + * @type: connector type + * @links: list of all connector &struct v4l2_connector_link links + * @nr_of_links: total number of links + * @connector: connector configuration + * @connector.analog: analog connector configuration + * &struct v4l2_fwnode_connector_analog + */ +struct v4l2_fwnode_connector { + const char *name; + const char *label; + enum v4l2_connector_type type; + struct list_head links; + unsigned int nr_of_links; + + union { + struct v4l2_fwnode_connector_analog analog; + /* future connectors */ + } connector; +}; + /** * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties * @fwnode: pointer to the endpoint's fwnode handle -- cgit v1.2.3 From dfc22c073b78f5379d6cf38e9819d03451f68f8c Mon Sep 17 00:00:00 2001 From: Marco Felsch Date: Thu, 12 Mar 2020 11:31:43 +0100 Subject: media: v4l2-fwnode: add initial connector parsing support The patch adds the initial connector parsing code, so we can move from a driver specific parsing code to a generic one. Currently only the generic fields and the analog-connector specific fields are parsed. Parsing the other connector specific fields can be added by a simple callbacks. Signed-off-by: Marco Felsch [hverkuil-cisco@xs4all.nl: replace ; with break; in a empty case] Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 165 ++++++++++++++++++++++++++++++++++ include/media/v4l2-fwnode.h | 82 +++++++++++++++++ 2 files changed, 247 insertions(+) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index cd5325b752f8..97f0f8b23b5d 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -591,6 +591,171 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) } EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); +static const struct v4l2_fwnode_connector_conv { + enum v4l2_connector_type type; + const char *compatible; +} connectors[] = { + { + .type = V4L2_CONN_COMPOSITE, + .compatible = "composite-video-connector", + }, { + .type = V4L2_CONN_SVIDEO, + .compatible = "svideo-connector", + }, +}; + +static enum v4l2_connector_type +v4l2_fwnode_string_to_connector_type(const char *con_str) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(connectors); i++) + if (!strcmp(con_str, connectors[i].compatible)) + return connectors[i].type; + + return V4L2_CONN_UNKNOWN; +} + +static void +v4l2_fwnode_connector_parse_analog(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *vc) +{ + u32 stds; + int ret; + + ret = fwnode_property_read_u32(fwnode, "sdtv-standards", &stds); + + /* The property is optional. */ + vc->connector.analog.sdtv_stds = ret ? V4L2_STD_ALL : stds; +} + +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector) +{ + struct v4l2_connector_link *link, *tmp; + + if (IS_ERR_OR_NULL(connector) || connector->type == V4L2_CONN_UNKNOWN) + return; + + list_for_each_entry_safe(link, tmp, &connector->links, head) { + v4l2_fwnode_put_link(&link->fwnode_link); + list_del(&link->head); + kfree(link); + } + + kfree(connector->label); + connector->label = NULL; + connector->type = V4L2_CONN_UNKNOWN; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_free); + +static enum v4l2_connector_type +v4l2_fwnode_get_connector_type(struct fwnode_handle *fwnode) +{ + const char *type_name; + int err; + + if (!fwnode) + return V4L2_CONN_UNKNOWN; + + /* The connector-type is stored within the compatible string. */ + err = fwnode_property_read_string(fwnode, "compatible", &type_name); + if (err) + return V4L2_CONN_UNKNOWN; + + return v4l2_fwnode_string_to_connector_type(type_name); +} + +int v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector) +{ + struct fwnode_handle *connector_node; + enum v4l2_connector_type connector_type; + const char *label; + int err; + + if (!fwnode) + return -EINVAL; + + memset(connector, 0, sizeof(*connector)); + + INIT_LIST_HEAD(&connector->links); + + connector_node = fwnode_graph_get_port_parent(fwnode); + connector_type = v4l2_fwnode_get_connector_type(connector_node); + if (connector_type == V4L2_CONN_UNKNOWN) { + fwnode_handle_put(connector_node); + connector_node = fwnode_graph_get_remote_port_parent(fwnode); + connector_type = v4l2_fwnode_get_connector_type(connector_node); + } + + if (connector_type == V4L2_CONN_UNKNOWN) { + pr_err("Unknown connector type\n"); + err = -ENOTCONN; + goto out; + } + + connector->type = connector_type; + connector->name = fwnode_get_name(connector_node); + err = fwnode_property_read_string(connector_node, "label", &label); + connector->label = err ? NULL : kstrdup_const(label, GFP_KERNEL); + + /* Parse the connector specific properties. */ + switch (connector->type) { + case V4L2_CONN_COMPOSITE: + case V4L2_CONN_SVIDEO: + v4l2_fwnode_connector_parse_analog(connector_node, connector); + break; + /* Avoid compiler warnings */ + case V4L2_CONN_UNKNOWN: + break; + } + +out: + fwnode_handle_put(connector_node); + + return err; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_parse); + +int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector) +{ + struct fwnode_handle *connector_ep; + struct v4l2_connector_link *link; + int err; + + if (!fwnode || !connector || connector->type == V4L2_CONN_UNKNOWN) + return -EINVAL; + + connector_ep = fwnode_graph_get_remote_endpoint(fwnode); + if (!connector_ep) + return -ENOTCONN; + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) { + err = -ENOMEM; + goto err; + } + + err = v4l2_fwnode_parse_link(connector_ep, &link->fwnode_link); + if (err) + goto err; + + fwnode_handle_put(connector_ep); + + list_add(&link->head, &connector->links); + connector->nr_of_links++; + + return 0; + +err: + kfree(link); + fwnode_handle_put(connector_ep); + + return err; +} +EXPORT_SYMBOL_GPL(v4l2_fwnode_connector_add_link); + static int v4l2_async_notifier_fwnode_parse_endpoint(struct device *dev, struct v4l2_async_notifier *notifier, diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 6cf39d447b10..dd82d6d9764e 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -294,6 +294,66 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, */ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); +/** + * v4l2_fwnode_connector_free() - free the V4L2 connector acquired memory + * @connector: the V4L2 connector resources of which are to be released + * + * Free all allocated memory and put all links acquired by + * v4l2_fwnode_connector_parse() and v4l2_fwnode_connector_add_link(). + * + * It is safe to call this function with NULL argument or on a V4L2 connector + * the parsing of which failed. + */ +void v4l2_fwnode_connector_free(struct v4l2_fwnode_connector *connector); + +/** + * v4l2_fwnode_connector_parse() - initialize the 'struct v4l2_fwnode_connector' + * @fwnode: pointer to the subdev endpoint's fwnode handle where the connector + * is connected to or to the connector endpoint fwnode handle. + * @connector: pointer to the V4L2 fwnode connector data structure + * + * Fill the &struct v4l2_fwnode_connector with the connector type, label and + * all &enum v4l2_connector_type specific connector data. The label is optional + * so it is set to %NULL if no one was found. The function initialize the links + * to zero. Adding links to the connector is done by calling + * v4l2_fwnode_connector_add_link(). + * + * The memory allocated for the label must be freed when no longer needed. + * Freeing the memory is done by v4l2_fwnode_connector_free(). + * + * Return: + * * %0 on success or a negative error code on failure: + * * %-EINVAL if @fwnode is invalid + * * %-ENOTCONN if connector type is unknown or connector device can't be found + */ +int v4l2_fwnode_connector_parse(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector); + +/** + * v4l2_fwnode_connector_add_link - add a link between a connector node and + * a v4l2-subdev node. + * @fwnode: pointer to the subdev endpoint's fwnode handle where the connector + * is connected to + * @connector: pointer to the V4L2 fwnode connector data structure + * + * Add a new &struct v4l2_connector_link link to the + * &struct v4l2_fwnode_connector connector links list. The link local_node + * points to the connector node, the remote_node to the host v4l2 (sub)dev. + * + * The taken references to remote_node and local_node must be dropped and the + * allocated memory must be freed when no longer needed. Both is done by calling + * v4l2_fwnode_connector_free(). + * + * Return: + * * %0 on success or a negative error code on failure: + * * %-EINVAL if @fwnode or @connector is invalid or @connector type is unknown + * * %-ENOMEM on link memory allocation failure + * * %-ENOTCONN if remote connector device can't be found + * * %-ENOLINK if link parsing between v4l2 (sub)dev and connector fails + */ +int v4l2_fwnode_connector_add_link(struct fwnode_handle *fwnode, + struct v4l2_fwnode_connector *connector); + /** * typedef parse_endpoint_func - Driver's callback function to be called on * each V4L2 fwnode endpoint. @@ -467,4 +527,26 @@ v4l2_async_register_fwnode_subdev(struct v4l2_subdev *sd, unsigned int num_ports, parse_endpoint_func parse_endpoint); +/* Helper macros to access the connector links. */ + +/** v4l2_connector_last_link - Helper macro to get the first + * &struct v4l2_fwnode_connector link + * @v4l2c: &struct v4l2_fwnode_connector owning the connector links + * + * This marco returns the first added &struct v4l2_connector_link connector + * link or @NULL if the connector has no links. + */ +#define v4l2_connector_first_link(v4l2c) \ + list_first_entry_or_null(&(v4l2c)->links, \ + struct v4l2_connector_link, head) + +/** v4l2_connector_last_link - Helper macro to get the last + * &struct v4l2_fwnode_connector link + * @v4l2c: &struct v4l2_fwnode_connector owning the connector links + * + * This marco returns the last &struct v4l2_connector_link added connector link. + */ +#define v4l2_connector_last_link(v4l2c) \ + list_last_entry(&(v4l2c)->links, struct v4l2_connector_link, head) + #endif /* _V4L2_FWNODE_H */ -- cgit v1.2.3 From a080a92a6f89e716b8a264f6b93123b41a1c004c Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Thu, 12 Mar 2020 11:31:44 +0100 Subject: media: partial revert of "[media] tvp5150: add HW input connectors support" Commit f7b4b54e6364 ("[media] tvp5150: add HW input connectors support") added input signals support for the tvp5150, but the approach was found to be incorrect so the corresponding DT binding commit 82c2ffeb217a ("[media] tvp5150: document input connectors DT bindings") was reverted. This left the driver with an undocumented (and wrong) DT parsing logic, so lets get rid of this code as well until the input connectors support is implemented properly. It's a partial revert due other patches added on top of mentioned commit not allowing the commit to be reverted cleanly anymore. But all the code related to the DT parsing logic and input entities creation are removed. Suggested-by: Laurent Pinchart Signed-off-by: Javier Martinez Canillas Acked-by: Laurent Pinchart [m.felsch@pengutronix.de: rm TVP5150_INPUT_NUM define] Signed-off-by: Marco Felsch Signed-off-by: Mauro Carvalho Chehab Acked-by: Rob Herring Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/i2c/tvp5150.c | 145 ------------------------------------ include/dt-bindings/media/tvp5150.h | 2 - 2 files changed, 147 deletions(-) (limited to 'include') diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index edad49cebcdf..11a5fd7e2f58 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -53,8 +53,6 @@ struct tvp5150 { struct v4l2_subdev sd; #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pads[TVP5150_NUM_PADS]; - struct media_entity input_ent[TVP5150_INPUT_NUM]; - struct media_pad input_pad[TVP5150_INPUT_NUM]; #endif struct v4l2_ctrl_handler hdl; struct v4l2_rect rect; @@ -1169,40 +1167,6 @@ static int tvp5150_enum_frame_size(struct v4l2_subdev *sd, return 0; } -/**************************************************************************** - Media entity ops - ****************************************************************************/ - -#ifdef CONFIG_MEDIA_CONTROLLER -static int tvp5150_link_setup(struct media_entity *entity, - const struct media_pad *local, - const struct media_pad *remote, u32 flags) -{ - struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); - struct tvp5150 *decoder = to_tvp5150(sd); - int i; - - for (i = 0; i < TVP5150_INPUT_NUM; i++) { - if (remote->entity == &decoder->input_ent[i]) - break; - } - - /* Do nothing for entities that are not input connectors */ - if (i == TVP5150_INPUT_NUM) - return 0; - - decoder->input = i; - - tvp5150_selmux(sd); - - return 0; -} - -static const struct media_entity_operations tvp5150_sd_media_ops = { - .link_setup = tvp5150_link_setup, -}; -#endif - /**************************************************************************** I2C Command ****************************************************************************/ @@ -1350,42 +1314,6 @@ static int tvp5150_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int tvp5150_registered(struct v4l2_subdev *sd) -{ -#ifdef CONFIG_MEDIA_CONTROLLER - struct tvp5150 *decoder = to_tvp5150(sd); - int ret = 0; - int i; - - for (i = 0; i < TVP5150_INPUT_NUM; i++) { - struct media_entity *input = &decoder->input_ent[i]; - struct media_pad *pad = &decoder->input_pad[i]; - - if (!input->name) - continue; - - decoder->input_pad[i].flags = MEDIA_PAD_FL_SOURCE; - - ret = media_entity_pads_init(input, 1, pad); - if (ret < 0) - return ret; - - ret = media_device_register_entity(sd->v4l2_dev->mdev, input); - if (ret < 0) - return ret; - - ret = media_create_pad_link(input, 0, &sd->entity, - TVP5150_PAD_IF_INPUT, 0); - if (ret < 0) { - media_device_unregister_entity(input); - return ret; - } - } -#endif - - return 0; -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_ctrl_ops tvp5150_ctrl_ops = { @@ -1439,10 +1367,6 @@ static const struct v4l2_subdev_ops tvp5150_ops = { .pad = &tvp5150_pad_ops, }; -static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { - .registered = tvp5150_registered, -}; - /**************************************************************************** I2C Client & Driver ****************************************************************************/ @@ -1595,12 +1519,6 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) { struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *ep; -#ifdef CONFIG_MEDIA_CONTROLLER - struct device_node *connectors, *child; - struct media_entity *input; - const char *name; - u32 input_type; -#endif unsigned int flags; int ret = 0; @@ -1624,67 +1542,6 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) decoder->mbus_type = bus_cfg.bus_type; -#ifdef CONFIG_MEDIA_CONTROLLER - connectors = of_get_child_by_name(np, "connectors"); - - if (!connectors) - goto err; - - for_each_available_child_of_node(connectors, child) { - ret = of_property_read_u32(child, "input", &input_type); - if (ret) { - dev_err(decoder->sd.dev, - "missing type property in node %pOFn\n", - child); - of_node_put(child); - goto err_connector; - } - - if (input_type >= TVP5150_INPUT_NUM) { - ret = -EINVAL; - of_node_put(child); - goto err_connector; - } - - input = &decoder->input_ent[input_type]; - - /* Each input connector can only be defined once */ - if (input->name) { - dev_err(decoder->sd.dev, - "input %s with same type already exists\n", - input->name); - of_node_put(child); - ret = -EINVAL; - goto err_connector; - } - - switch (input_type) { - case TVP5150_COMPOSITE0: - case TVP5150_COMPOSITE1: - input->function = MEDIA_ENT_F_CONN_COMPOSITE; - break; - case TVP5150_SVIDEO: - input->function = MEDIA_ENT_F_CONN_SVIDEO; - break; - } - - input->flags = MEDIA_ENT_FL_CONNECTOR; - - ret = of_property_read_string(child, "label", &name); - if (ret < 0) { - dev_err(decoder->sd.dev, - "missing label property in node %pOFn\n", - child); - of_node_put(child); - goto err_connector; - } - - input->name = name; - } - -err_connector: - of_node_put(connectors); -#endif err: of_node_put(ep); return ret; @@ -1735,7 +1592,6 @@ static int tvp5150_probe(struct i2c_client *c) } v4l2_i2c_subdev_init(sd, c, &tvp5150_ops); - sd->internal_ops = &tvp5150_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; #if defined(CONFIG_MEDIA_CONTROLLER) @@ -1750,7 +1606,6 @@ static int tvp5150_probe(struct i2c_client *c) if (res < 0) return res; - sd->entity.ops = &tvp5150_sd_media_ops; #endif res = tvp5150_detect_version(core); diff --git a/include/dt-bindings/media/tvp5150.h b/include/dt-bindings/media/tvp5150.h index 01eedf4985b8..dda00c038530 100644 --- a/include/dt-bindings/media/tvp5150.h +++ b/include/dt-bindings/media/tvp5150.h @@ -14,8 +14,6 @@ #define TVP5150_COMPOSITE1 1 #define TVP5150_SVIDEO 2 -#define TVP5150_INPUT_NUM 3 - /* TVP5150 HW outputs */ #define TVP5150_NORMAL 0 #define TVP5150_BLACK_SCREEN 1 -- cgit v1.2.3 From da9a5aa3402db0ff3b57216d8dbf2478e1046cae Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 10 Mar 2020 19:43:37 +0200 Subject: serial: core: Refactor uart_unlock_and_check_sysrq() Refactor uart_unlock_and_check_sysrq() to: - explicitly show that we release a port lock which makes static analyzers happy: CHECK drivers/tty/serial/serial_core.c .../serial_core.c:3290:17: warning: context imbalance in 'uart_unlock_and_check_sysrq' - unexpected unlock - use flags instead of irqflags to avoid confusion with IRQ flags - provide one return point - be more compact Signed-off-by: Andy Shevchenko Reviewed-by: Dmitry Safonov <0x7f454c46@gmail.com> Link: https://lore.kernel.org/r/20200310174337.74109-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 23 ++++++++++------------- include/linux/serial_core.h | 3 +-- 2 files changed, 11 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 752655c80f73..38ef6afddce8 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3187,22 +3187,19 @@ int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch) } EXPORT_SYMBOL_GPL(uart_prepare_sysrq_char); -void uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long irqflags) +void uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long flags) +__releases(&port->lock) { - int sysrq_ch; + if (port->has_sysrq) { + int sysrq_ch = port->sysrq_ch; - if (!port->has_sysrq) { - spin_unlock_irqrestore(&port->lock, irqflags); - return; + port->sysrq_ch = 0; + spin_unlock_irqrestore(&port->lock, flags); + if (sysrq_ch) + handle_sysrq(sysrq_ch); + } else { + spin_unlock_irqrestore(&port->lock, flags); } - - sysrq_ch = port->sysrq_ch; - port->sysrq_ch = 0; - - spin_unlock_irqrestore(&port->lock, irqflags); - - if (sysrq_ch) - handle_sysrq(sysrq_ch); } EXPORT_SYMBOL_GPL(uart_unlock_and_check_sysrq); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 1f4443db5474..92f5eba86052 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -462,8 +462,7 @@ extern void uart_insert_char(struct uart_port *port, unsigned int status, extern int uart_handle_sysrq_char(struct uart_port *port, unsigned int ch); extern int uart_prepare_sysrq_char(struct uart_port *port, unsigned int ch); -extern void uart_unlock_and_check_sysrq(struct uart_port *port, - unsigned long irqflags); +extern void uart_unlock_and_check_sysrq(struct uart_port *port, unsigned long flags); extern int uart_handle_break(struct uart_port *port); /* -- cgit v1.2.3 From 7c4046b1c53bba3a0315f04bb0bb5f36888a747b Mon Sep 17 00:00:00 2001 From: Julian Wiedmann Date: Thu, 12 Mar 2020 18:57:54 +0100 Subject: Revert "net: sched: make newly activated qdiscs visible" This reverts commit 4cda75275f9f89f9485b0ca4d6950c95258a9bce from net-next. Brown bag time. Michal noticed that this change doesn't work at all when netif_set_real_num_tx_queues() gets called prior to an initial dev_activate(), as for instance igb does. Doing so dies with: [ 40.579142] BUG: kernel NULL pointer dereference, address: 0000000000000400 [ 40.586922] #PF: supervisor read access in kernel mode [ 40.592668] #PF: error_code(0x0000) - not-present page [ 40.598405] PGD 0 P4D 0 [ 40.601234] Oops: 0000 [#1] PREEMPT SMP PTI [ 40.605909] CPU: 18 PID: 1681 Comm: wickedd Tainted: G E 5.6.0-rc3-ethnl.50-default #1 [ 40.616205] Hardware name: Intel Corporation S2600CP/S2600CP, BIOS RMLSDP.86I.R3.27.D685.1305151734 05/15/2013 [ 40.627377] RIP: 0010:qdisc_hash_add.part.22+0x2e/0x90 [ 40.633115] Code: 00 55 53 89 f5 48 89 fb e8 2f 9b fb ff 85 c0 74 44 48 8b 43 40 48 8b 08 69 43 38 47 86 c8 61 c1 e8 1c 48 83 e8 80 48 8d 14 c1 <48> 8b 04 c1 48 8d 4b 28 48 89 53 30 48 89 43 28 48 85 c0 48 89 0a [ 40.654080] RSP: 0018:ffffb879864934d8 EFLAGS: 00010203 [ 40.659914] RAX: 0000000000000080 RBX: ffffffffb8328d80 RCX: 0000000000000000 [ 40.667882] RDX: 0000000000000400 RSI: 0000000000000000 RDI: ffffffffb831faa0 [ 40.675849] RBP: 0000000000000000 R08: ffffa0752c8b9088 R09: ffffa0752c8b9208 [ 40.683816] R10: 0000000000000006 R11: 0000000000000000 R12: ffffa0752d734000 [ 40.691783] R13: 0000000000000008 R14: 0000000000000000 R15: ffffa07113c18000 [ 40.699750] FS: 00007f94548e5880(0000) GS:ffffa0752e980000(0000) knlGS:0000000000000000 [ 40.708782] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 40.715189] CR2: 0000000000000400 CR3: 000000082b6ae006 CR4: 00000000001606e0 [ 40.723156] Call Trace: [ 40.725888] dev_qdisc_set_real_num_tx_queues+0x61/0x90 [ 40.731725] netif_set_real_num_tx_queues+0x94/0x1d0 [ 40.737286] __igb_open+0x19a/0x5d0 [igb] [ 40.741767] __dev_open+0xbb/0x150 [ 40.745567] __dev_change_flags+0x157/0x1a0 [ 40.750240] dev_change_flags+0x23/0x60 [...] Fixes: 4cda75275f9f ("net: sched: make newly activated qdiscs visible") Reported-by: Michal Kubecek CC: Michal Kubecek CC: Eric Dumazet CC: Jamal Hadi Salim CC: Cong Wang CC: Jiri Pirko Signed-off-by: Julian Wiedmann Signed-off-by: David S. Miller --- include/net/sch_generic.h | 6 ------ net/core/dev.c | 1 - net/sched/sch_generic.c | 21 --------------------- 3 files changed, 28 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 7bfc45c5b602..151208704ed2 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -153,11 +153,6 @@ static inline bool qdisc_is_empty(const struct Qdisc *qdisc) return !READ_ONCE(qdisc->q.qlen); } -static inline bool qdisc_hashed(struct Qdisc *qdisc) -{ - return hash_hashed(&qdisc->hash); -} - static inline bool qdisc_run_begin(struct Qdisc *qdisc) { if (qdisc->flags & TCQ_F_NOLOCK) { @@ -634,7 +629,6 @@ void qdisc_class_hash_grow(struct Qdisc *, struct Qdisc_class_hash *); void qdisc_class_hash_destroy(struct Qdisc_class_hash *); int dev_qdisc_change_tx_queue_len(struct net_device *dev); -void dev_qdisc_set_real_num_tx_queues(struct net_device *dev); void dev_init_scheduler(struct net_device *dev); void dev_shutdown(struct net_device *dev); void dev_activate(struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index ccc03abeee52..25dab1598803 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2875,7 +2875,6 @@ int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq) netif_setup_tc(dev, txq); dev->real_num_tx_queues = txq; - dev_qdisc_set_real_num_tx_queues(dev); if (disabling) { synchronize_net(); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 36a40ebcf0ee..6c9595f1048a 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1268,27 +1268,6 @@ int dev_qdisc_change_tx_queue_len(struct net_device *dev) return ret; } -void dev_qdisc_set_real_num_tx_queues(struct net_device *dev) -{ -#ifdef CONFIG_NET_SCHED - struct Qdisc *sch = dev->qdisc; - unsigned int ntx; - - if (!sch) - return; - - ASSERT_RTNL(); - - for (ntx = 0; ntx < dev->real_num_tx_queues; ntx++) { - struct netdev_queue *dev_queue = netdev_get_tx_queue(dev, ntx); - struct Qdisc *qdisc = dev_queue->qdisc; - - if (qdisc && !qdisc_hashed(qdisc)) - qdisc_hash_add(qdisc, false); - } -#endif -} - static void dev_init_scheduler_queue(struct net_device *dev, struct netdev_queue *dev_queue, void *_qdisc) -- cgit v1.2.3 From 8213f6c9a275da084dc9363f36f93138547f46f1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 11 Mar 2020 15:32:48 -0700 Subject: net: be2net: reject unsupported coalescing params Set ethtool_ops->supported_coalesce_params to let the core reject unsupported coalescing parameters. This driver did not previously reject unsupported parameters. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/emulex/benet/be_ethtool.c | 3 +++ include/linux/ethtool.h | 3 +++ 2 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 9d9f0545fbfe..d6ed1d943762 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -1408,6 +1408,9 @@ static int be_set_priv_flags(struct net_device *netdev, u32 flags) } const struct ethtool_ops be_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_USE_ADAPTIVE | + ETHTOOL_COALESCE_USECS_LOW_HIGH, .get_drvinfo = be_get_drvinfo, .get_wol = be_get_wol, .set_wol = be_set_wol, diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 9efeebde3514..acfce915a02b 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -211,6 +211,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ) #define ETHTOOL_COALESCE_USE_ADAPTIVE \ (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | ETHTOOL_COALESCE_USE_ADAPTIVE_TX) +#define ETHTOOL_COALESCE_USECS_LOW_HIGH \ + (ETHTOOL_COALESCE_RX_USECS_LOW | ETHTOOL_COALESCE_TX_USECS_LOW | \ + ETHTOOL_COALESCE_RX_USECS_HIGH | ETHTOOL_COALESCE_TX_USECS_HIGH) #define ETHTOOL_COALESCE_PKT_RATE_RX_USECS \ (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | \ ETHTOOL_COALESCE_RX_USECS_LOW | ETHTOOL_COALESCE_RX_USECS_HIGH | \ -- cgit v1.2.3 From 4f9546d24a12e9041eae20574ab244f5410e02c4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 11 Mar 2020 15:32:52 -0700 Subject: net: hns: reject unsupported coalescing params Set ethtool_ops->supported_coalesce_params to let the core reject unsupported coalescing parameters. This driver did not previously reject unsupported parameters. Signed-off-by: Jakub Kicinski Signed-off-by: David S. Miller --- drivers/net/ethernet/hisilicon/hns/hns_ethtool.c | 5 +++++ include/linux/ethtool.h | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index 717fccc2efba..49624acf2473 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -1264,6 +1264,11 @@ static int hns_get_rxnfc(struct net_device *netdev, } static const struct ethtool_ops hns_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_USECS | + ETHTOOL_COALESCE_MAX_FRAMES | + ETHTOOL_COALESCE_USE_ADAPTIVE | + ETHTOOL_COALESCE_USECS_LOW_HIGH | + ETHTOOL_COALESCE_MAX_FRAMES_LOW_HIGH, .get_drvinfo = hns_nic_get_drvinfo, .get_link = hns_nic_get_link, .get_ringparam = hns_get_ringparam, diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index acfce915a02b..be355f37337d 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -214,6 +214,11 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, #define ETHTOOL_COALESCE_USECS_LOW_HIGH \ (ETHTOOL_COALESCE_RX_USECS_LOW | ETHTOOL_COALESCE_TX_USECS_LOW | \ ETHTOOL_COALESCE_RX_USECS_HIGH | ETHTOOL_COALESCE_TX_USECS_HIGH) +#define ETHTOOL_COALESCE_MAX_FRAMES_LOW_HIGH \ + (ETHTOOL_COALESCE_RX_MAX_FRAMES_LOW | \ + ETHTOOL_COALESCE_TX_MAX_FRAMES_LOW | \ + ETHTOOL_COALESCE_RX_MAX_FRAMES_HIGH | \ + ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH) #define ETHTOOL_COALESCE_PKT_RATE_RX_USECS \ (ETHTOOL_COALESCE_USE_ADAPTIVE_RX | \ ETHTOOL_COALESCE_RX_USECS_LOW | ETHTOOL_COALESCE_RX_USECS_HIGH | \ -- cgit v1.2.3 From 4b01a9674231a97553a55456d883f584e948a78d Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Tue, 10 Mar 2020 17:05:25 +0900 Subject: tcp: bind(0) remove the SO_REUSEADDR restriction when ephemeral ports are exhausted. Commit aacd9289af8b82f5fb01bcdd53d0e3406d1333c7 ("tcp: bind() use stronger condition for bind_conflict") introduced a restriction to forbid to bind SO_REUSEADDR enabled sockets to the same (addr, port) tuple in order to assign ports dispersedly so that we can connect to the same remote host. The change results in accelerating port depletion so that we fail to bind sockets to the same local port even if we want to connect to the different remote hosts. You can reproduce this issue by following instructions below. 1. # sysctl -w net.ipv4.ip_local_port_range="32768 32768" 2. set SO_REUSEADDR to two sockets. 3. bind two sockets to (localhost, 0) and the latter fails. Therefore, when ephemeral ports are exhausted, bind(0) should fallback to the legacy behaviour to enable the SO_REUSEADDR option and make it possible to connect to different remote (addr, port) tuples. This patch allows us to bind SO_REUSEADDR enabled sockets to the same (addr, port) only when net.ipv4.ip_autobind_reuse is set 1 and all ephemeral ports are exhausted. This also allows connect() and listen() to share ports in the following way and may break some applications. So the ip_autobind_reuse is 0 by default and disables the feature. 1. setsockopt(sk1, SO_REUSEADDR) 2. setsockopt(sk2, SO_REUSEADDR) 3. bind(sk1, saddr, 0) 4. bind(sk2, saddr, 0) 5. connect(sk1, daddr) 6. listen(sk2) If it is set 1, we can fully utilize the 4-tuples, but we should use IP_BIND_ADDRESS_NO_PORT for bind()+connect() as possible. The notable thing is that if all sockets bound to the same port have both SO_REUSEADDR and SO_REUSEPORT enabled, we can bind sockets to an ephemeral port and also do listen(). Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 9 +++++++++ include/net/netns/ipv4.h | 1 + net/ipv4/inet_connection_sock.c | 10 +++++++++- net/ipv4/sysctl_net_ipv4.c | 9 +++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 5f53faff4e25..ee961d322d93 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -958,6 +958,15 @@ ip_nonlocal_bind - BOOLEAN which can be quite useful - but may break some applications. Default: 0 +ip_autobind_reuse - BOOLEAN + By default, bind() does not select the ports automatically even if + the new socket and all sockets bound to the port have SO_REUSEADDR. + ip_autobind_reuse allows bind() to reuse the port and this is useful + when you use bind()+connect(), but may break some applications. + The preferred solution is to use IP_BIND_ADDRESS_NO_PORT and this + option should only be set by experts. + Default: 0 + ip_dynaddr - BOOLEAN If set non-zero, enables support for dynamic addresses. If set to a non-zero value larger than 1, a kernel log diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 08b98414d94e..154b8f01499b 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -101,6 +101,7 @@ struct netns_ipv4 { int sysctl_ip_fwd_use_pmtu; int sysctl_ip_fwd_update_priority; int sysctl_ip_nonlocal_bind; + int sysctl_ip_autobind_reuse; /* Shall we try to damage output packets if routing dev changes? */ int sysctl_ip_dynaddr; int sysctl_ip_early_demux; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 2e9549f49a82..497366b631f3 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -174,12 +174,14 @@ inet_csk_find_open_port(struct sock *sk, struct inet_bind_bucket **tb_ret, int * int port = 0; struct inet_bind_hashbucket *head; struct net *net = sock_net(sk); + bool relax = false; int i, low, high, attempt_half; struct inet_bind_bucket *tb; u32 remaining, offset; int l3mdev; l3mdev = inet_sk_bound_l3mdev(sk); +ports_exhausted: attempt_half = (sk->sk_reuse == SK_CAN_REUSE) ? 1 : 0; other_half_scan: inet_get_local_port_range(net, &low, &high); @@ -217,7 +219,7 @@ other_parity_scan: inet_bind_bucket_for_each(tb, &head->chain) if (net_eq(ib_net(tb), net) && tb->l3mdev == l3mdev && tb->port == port) { - if (!inet_csk_bind_conflict(sk, tb, false, false)) + if (!inet_csk_bind_conflict(sk, tb, relax, false)) goto success; goto next_port; } @@ -237,6 +239,12 @@ next_port: attempt_half = 2; goto other_half_scan; } + + if (net->ipv4.sysctl_ip_autobind_reuse && !relax) { + /* We still have a chance to connect to different destinations */ + relax = true; + goto ports_exhausted; + } return NULL; success: *port_ret = port; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index d9531b4b33f2..81b267e990a1 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -763,6 +763,15 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "ip_autobind_reuse", + .data = &init_net.ipv4.sysctl_ip_autobind_reuse, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, { .procname = "fwmark_reflect", .data = &init_net.ipv4.sysctl_fwmark_reflect, -- cgit v1.2.3 From 7e934cf5ace1dceeb804f7493fa28bb697ed3c52 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 12 Mar 2020 17:29:11 -0400 Subject: xarray: Fix early termination of xas_for_each_marked xas_for_each_marked() is using entry == NULL as a termination condition of the iteration. When xas_for_each_marked() is used protected only by RCU, this can however race with xas_store(xas, NULL) in the following way: TASK1 TASK2 page_cache_delete() find_get_pages_range_tag() xas_for_each_marked() xas_find_marked() off = xas_find_chunk() xas_store(&xas, NULL) xas_init_marks(&xas); ... rcu_assign_pointer(*slot, NULL); entry = xa_entry(off); And thus xas_for_each_marked() terminates prematurely possibly leading to missed entries in the iteration (translating to missing writeback of some pages or a similar problem). If we find a NULL entry that has been marked, skip it (unless we're trying to allocate an entry). Reported-by: Jan Kara CC: stable@vger.kernel.org Fixes: ef8e5717db01 ("page cache: Convert delete_batch to XArray") Signed-off-by: Matthew Wilcox (Oracle) --- include/linux/xarray.h | 6 +- lib/xarray.c | 2 + tools/testing/radix-tree/Makefile | 4 +- tools/testing/radix-tree/iteration_check_2.c | 87 ++++++++++++++++++++++++++++ tools/testing/radix-tree/main.c | 1 + tools/testing/radix-tree/test.h | 1 + 6 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 tools/testing/radix-tree/iteration_check_2.c (limited to 'include') diff --git a/include/linux/xarray.h b/include/linux/xarray.h index a491653d8882..14c893433139 100644 --- a/include/linux/xarray.h +++ b/include/linux/xarray.h @@ -1648,6 +1648,7 @@ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark) { struct xa_node *node = xas->xa_node; + void *entry; unsigned int offset; if (unlikely(xas_not_node(node) || node->shift)) @@ -1659,7 +1660,10 @@ static inline void *xas_next_marked(struct xa_state *xas, unsigned long max, return NULL; if (offset == XA_CHUNK_SIZE) return xas_find_marked(xas, max, mark); - return xa_entry(xas->xa, node, offset); + entry = xa_entry(xas->xa, node, offset); + if (!entry) + return xas_find_marked(xas, max, mark); + return entry; } /* diff --git a/lib/xarray.c b/lib/xarray.c index f448bcd263ac..e9e641d3c0c3 100644 --- a/lib/xarray.c +++ b/lib/xarray.c @@ -1208,6 +1208,8 @@ void *xas_find_marked(struct xa_state *xas, unsigned long max, xa_mark_t mark) } entry = xa_entry(xas->xa, xas->xa_node, xas->xa_offset); + if (!entry && !(xa_track_free(xas->xa) && mark == XA_FREE_MARK)) + continue; if (!xa_is_node(entry)) return entry; xas->xa_node = xa_to_node(entry); diff --git a/tools/testing/radix-tree/Makefile b/tools/testing/radix-tree/Makefile index 397d6b612502..aa6abfe0749c 100644 --- a/tools/testing/radix-tree/Makefile +++ b/tools/testing/radix-tree/Makefile @@ -7,8 +7,8 @@ LDLIBS+= -lpthread -lurcu TARGETS = main idr-test multiorder xarray CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \ - regression4.o \ - tag_check.o multiorder.o idr-test.o iteration_check.o benchmark.o + regression4.o tag_check.o multiorder.o idr-test.o iteration_check.o \ + iteration_check_2.o benchmark.o ifndef SHIFT SHIFT=3 diff --git a/tools/testing/radix-tree/iteration_check_2.c b/tools/testing/radix-tree/iteration_check_2.c new file mode 100644 index 000000000000..aac5c50a3674 --- /dev/null +++ b/tools/testing/radix-tree/iteration_check_2.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * iteration_check_2.c: Check that deleting a tagged entry doesn't cause + * an RCU walker to finish early. + * Copyright (c) 2020 Oracle + * Author: Matthew Wilcox + */ +#include +#include "test.h" + +static volatile bool test_complete; + +static void *iterator(void *arg) +{ + XA_STATE(xas, arg, 0); + void *entry; + + rcu_register_thread(); + + while (!test_complete) { + xas_set(&xas, 0); + rcu_read_lock(); + xas_for_each_marked(&xas, entry, ULONG_MAX, XA_MARK_0) + ; + rcu_read_unlock(); + assert(xas.xa_index >= 100); + } + + rcu_unregister_thread(); + return NULL; +} + +static void *throbber(void *arg) +{ + struct xarray *xa = arg; + + rcu_register_thread(); + + while (!test_complete) { + int i; + + for (i = 0; i < 100; i++) { + xa_store(xa, i, xa_mk_value(i), GFP_KERNEL); + xa_set_mark(xa, i, XA_MARK_0); + } + for (i = 0; i < 100; i++) + xa_erase(xa, i); + } + + rcu_unregister_thread(); + return NULL; +} + +void iteration_test2(unsigned test_duration) +{ + pthread_t threads[2]; + DEFINE_XARRAY(array); + int i; + + printv(1, "Running iteration test 2 for %d seconds\n", test_duration); + + test_complete = false; + + xa_store(&array, 100, xa_mk_value(100), GFP_KERNEL); + xa_set_mark(&array, 100, XA_MARK_0); + + if (pthread_create(&threads[0], NULL, iterator, &array)) { + perror("create iterator thread"); + exit(1); + } + if (pthread_create(&threads[1], NULL, throbber, &array)) { + perror("create throbber thread"); + exit(1); + } + + sleep(test_duration); + test_complete = true; + + for (i = 0; i < 2; i++) { + if (pthread_join(threads[i], NULL)) { + perror("pthread_join"); + exit(1); + } + } + + xa_destroy(&array); +} diff --git a/tools/testing/radix-tree/main.c b/tools/testing/radix-tree/main.c index 7a22d6e3732e..f2cbc8e5b97c 100644 --- a/tools/testing/radix-tree/main.c +++ b/tools/testing/radix-tree/main.c @@ -311,6 +311,7 @@ int main(int argc, char **argv) regression4_test(); iteration_test(0, 10 + 90 * long_run); iteration_test(7, 10 + 90 * long_run); + iteration_test2(10 + 90 * long_run); single_thread_tests(long_run); /* Free any remaining preallocated nodes */ diff --git a/tools/testing/radix-tree/test.h b/tools/testing/radix-tree/test.h index 1ee4b2c0ad10..34dab4d18744 100644 --- a/tools/testing/radix-tree/test.h +++ b/tools/testing/radix-tree/test.h @@ -34,6 +34,7 @@ void xarray_tests(void); void tag_check(void); void multiorder_checks(void); void iteration_test(unsigned order, unsigned duration); +void iteration_test2(unsigned duration); void benchmark(void); void idr_checks(void); void ida_tests(void); -- cgit v1.2.3 From 5b7cb7451585f83d414512a70b79b2086b8c6ed1 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:03 +0200 Subject: net/mlx5: E-Switch, Enable reg c1 loopback when possible Enable reg c1 loopback if firmware reports it's supported, as this is needed for restoring packet metadata (e.g chain). Also define helper to query if it is enabled. Signed-off-by: Paul Blakey Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/eswitch.h | 1 + .../ethernet/mellanox/mlx5/core/eswitch_offloads.c | 44 ++++++++++++++++------ include/linux/mlx5/eswitch.h | 7 ++++ 3 files changed, 41 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 9b5eaa8a47c4..ee36a8ab8e65 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -236,6 +236,7 @@ struct mlx5_esw_functions { enum { MLX5_ESWITCH_VPORT_MATCH_METADATA = BIT(0), + MLX5_ESWITCH_REG_C1_LOOPBACK_ENABLED = BIT(1), }; struct mlx5_eswitch { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index bdc2e247c053..5e751d7749db 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -763,14 +763,21 @@ void mlx5_eswitch_del_send_to_vport_rule(struct mlx5_flow_handle *rule) mlx5_del_flow_rules(rule); } +static bool mlx5_eswitch_reg_c1_loopback_supported(struct mlx5_eswitch *esw) +{ + return MLX5_CAP_ESW_FLOWTABLE(esw->dev, fdb_to_vport_reg_c_id) & + MLX5_FDB_TO_VPORT_REG_C_1; +} + static int esw_set_passing_vport_metadata(struct mlx5_eswitch *esw, bool enable) { u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {}; u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {}; - u8 fdb_to_vport_reg_c_id; + u8 curr, wanted; int err; - if (!mlx5_eswitch_vport_match_metadata_enabled(esw)) + if (!mlx5_eswitch_reg_c1_loopback_supported(esw) && + !mlx5_eswitch_vport_match_metadata_enabled(esw)) return 0; err = mlx5_eswitch_query_esw_vport_context(esw->dev, 0, false, @@ -778,24 +785,33 @@ static int esw_set_passing_vport_metadata(struct mlx5_eswitch *esw, bool enable) if (err) return err; - fdb_to_vport_reg_c_id = MLX5_GET(query_esw_vport_context_out, out, - esw_vport_context.fdb_to_vport_reg_c_id); + curr = MLX5_GET(query_esw_vport_context_out, out, + esw_vport_context.fdb_to_vport_reg_c_id); + wanted = MLX5_FDB_TO_VPORT_REG_C_0; + if (mlx5_eswitch_reg_c1_loopback_supported(esw)) + wanted |= MLX5_FDB_TO_VPORT_REG_C_1; if (enable) - fdb_to_vport_reg_c_id |= MLX5_FDB_TO_VPORT_REG_C_0 | - MLX5_FDB_TO_VPORT_REG_C_1; + curr |= wanted; else - fdb_to_vport_reg_c_id &= ~(MLX5_FDB_TO_VPORT_REG_C_0 | - MLX5_FDB_TO_VPORT_REG_C_1); + curr &= ~wanted; MLX5_SET(modify_esw_vport_context_in, in, - esw_vport_context.fdb_to_vport_reg_c_id, fdb_to_vport_reg_c_id); + esw_vport_context.fdb_to_vport_reg_c_id, curr); MLX5_SET(modify_esw_vport_context_in, in, field_select.fdb_to_vport_reg_c_id, 1); - return mlx5_eswitch_modify_esw_vport_context(esw->dev, 0, false, - in, sizeof(in)); + err = mlx5_eswitch_modify_esw_vport_context(esw->dev, 0, false, in, + sizeof(in)); + if (!err) { + if (enable && (curr & MLX5_FDB_TO_VPORT_REG_C_1)) + esw->flags |= MLX5_ESWITCH_REG_C1_LOOPBACK_ENABLED; + else + esw->flags &= ~MLX5_ESWITCH_REG_C1_LOOPBACK_ENABLED; + } + + return err; } static void peer_miss_rules_setup(struct mlx5_eswitch *esw, @@ -2831,6 +2847,12 @@ bool mlx5_eswitch_is_vf_vport(const struct mlx5_eswitch *esw, u16 vport_num) vport_num <= esw->dev->priv.sriov.max_vfs; } +bool mlx5_eswitch_reg_c1_loopback_enabled(const struct mlx5_eswitch *esw) +{ + return !!(esw->flags & MLX5_ESWITCH_REG_C1_LOOPBACK_ENABLED); +} +EXPORT_SYMBOL(mlx5_eswitch_reg_c1_loopback_enabled); + bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw) { return !!(esw->flags & MLX5_ESWITCH_VPORT_MATCH_METADATA); diff --git a/include/linux/mlx5/eswitch.h b/include/linux/mlx5/eswitch.h index 61705e74a5bb..c16827eeba9c 100644 --- a/include/linux/mlx5/eswitch.h +++ b/include/linux/mlx5/eswitch.h @@ -70,6 +70,7 @@ u16 mlx5_eswitch_get_total_vports(const struct mlx5_core_dev *dev); enum devlink_eswitch_encap_mode mlx5_eswitch_get_encap_mode(const struct mlx5_core_dev *dev); +bool mlx5_eswitch_reg_c1_loopback_enabled(const struct mlx5_eswitch *esw); bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw); /* Reg C0 usage: @@ -108,6 +109,12 @@ mlx5_eswitch_get_encap_mode(const struct mlx5_core_dev *dev) return DEVLINK_ESWITCH_ENCAP_MODE_NONE; } +static inline bool +mlx5_eswitch_reg_c1_loopback_enabled(const struct mlx5_eswitch *esw) +{ + return false; +}; + static inline bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw) { -- cgit v1.2.3 From 978703f42549ac7d1a354bafbfc346a3ccf15f0d Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:05 +0200 Subject: netfilter: flowtable: Add API for registering to flow table events Let drivers to add their cb allowing them to receive flow offload events of type TC_SETUP_CLSFLOWER (REPLACE/DEL/STATS) for flows managed by the flow table. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/netfilter/nf_flow_table.h | 6 +++++ net/netfilter/nf_flow_table_core.c | 47 +++++++++++++++++++++++++++++++++++ net/netfilter/nf_flow_table_offload.c | 4 +++ 3 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index e0f709d9d547..d9d0945b696e 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -44,6 +44,7 @@ struct nf_flowtable { struct delayed_work gc_work; unsigned int flags; struct flow_block flow_block; + struct mutex flow_block_lock; /* Guards flow_block */ possible_net_t net; }; @@ -129,6 +130,11 @@ struct nf_flow_route { struct flow_offload *flow_offload_alloc(struct nf_conn *ct); void flow_offload_free(struct flow_offload *flow); +int nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table, + flow_setup_cb_t *cb, void *cb_priv); +void nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table, + flow_setup_cb_t *cb, void *cb_priv); + int flow_offload_route_init(struct flow_offload *flow, const struct nf_flow_route *route); diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 8af28e10b4e6..4af0327992cf 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -372,6 +372,50 @@ static void nf_flow_offload_work_gc(struct work_struct *work) queue_delayed_work(system_power_efficient_wq, &flow_table->gc_work, HZ); } +int nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table, + flow_setup_cb_t *cb, void *cb_priv) +{ + struct flow_block *block = &flow_table->flow_block; + struct flow_block_cb *block_cb; + int err = 0; + + mutex_lock(&flow_table->flow_block_lock); + block_cb = flow_block_cb_lookup(block, cb, cb_priv); + if (block_cb) { + err = -EEXIST; + goto unlock; + } + + block_cb = flow_block_cb_alloc(cb, cb_priv, cb_priv, NULL); + if (IS_ERR(block_cb)) { + err = PTR_ERR(block_cb); + goto unlock; + } + + list_add_tail(&block_cb->list, &block->cb_list); + +unlock: + mutex_unlock(&flow_table->flow_block_lock); + return err; +} +EXPORT_SYMBOL_GPL(nf_flow_table_offload_add_cb); + +void nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table, + flow_setup_cb_t *cb, void *cb_priv) +{ + struct flow_block *block = &flow_table->flow_block; + struct flow_block_cb *block_cb; + + mutex_lock(&flow_table->flow_block_lock); + block_cb = flow_block_cb_lookup(block, cb, cb_priv); + if (block_cb) + list_del(&block_cb->list); + else + WARN_ON(true); + mutex_unlock(&flow_table->flow_block_lock); +} +EXPORT_SYMBOL_GPL(nf_flow_table_offload_del_cb); + static int nf_flow_nat_port_tcp(struct sk_buff *skb, unsigned int thoff, __be16 port, __be16 new_port) { @@ -494,6 +538,7 @@ int nf_flow_table_init(struct nf_flowtable *flowtable) INIT_DEFERRABLE_WORK(&flowtable->gc_work, nf_flow_offload_work_gc); flow_block_init(&flowtable->flow_block); + mutex_init(&flowtable->flow_block_lock); err = rhashtable_init(&flowtable->rhashtable, &nf_flow_offload_rhash_params); @@ -550,11 +595,13 @@ void nf_flow_table_free(struct nf_flowtable *flow_table) mutex_lock(&flowtable_lock); list_del(&flow_table->list); mutex_unlock(&flowtable_lock); + cancel_delayed_work_sync(&flow_table->gc_work); nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL); nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table); nf_flow_table_offload_flush(flow_table); rhashtable_destroy(&flow_table->rhashtable); + mutex_destroy(&flow_table->flow_block_lock); } EXPORT_SYMBOL_GPL(nf_flow_table_free); diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index 06f00cdc3891..f5afdf023bb9 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -610,6 +610,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable, if (cmd == FLOW_CLS_REPLACE) cls_flow.rule = flow_rule->rule; + mutex_lock(&flowtable->flow_block_lock); list_for_each_entry(block_cb, block_cb_list, list) { err = block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv); @@ -618,6 +619,7 @@ static int nf_flow_offload_tuple(struct nf_flowtable *flowtable, i++; } + mutex_unlock(&flowtable->flow_block_lock); return i; } @@ -692,8 +694,10 @@ static void flow_offload_tuple_stats(struct flow_offload_work *offload, FLOW_CLS_STATS, &offload->flow->tuplehash[dir].tuple, &extack); + mutex_lock(&flowtable->flow_block_lock); list_for_each_entry(block_cb, &flowtable->flow_block.cb_list, list) block_cb->cb(TC_SETUP_CLSFLOWER, &cls_flow, block_cb->cb_priv); + mutex_unlock(&flowtable->flow_block_lock); memcpy(stats, &cls_flow.stats, sizeof(*stats)); } -- cgit v1.2.3 From 9c26ba9b1f453a0c86b26e9ab5e8efedcb4470d8 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:06 +0200 Subject: net/sched: act_ct: Instantiate flow table entry actions NF flow table API associate 5-tuple rule with an action list by calling the flow table type action() CB to fill the rule's actions. In action CB of act_ct, populate the ct offload entry actions with a new ct_metadata action. Initialize the ct_metadata with the ct mark, label and zone information. If ct nat was performed, then also append the relevant packet mangle actions (e.g. ipv4/ipv6/tcp/udp header rewrites). Drivers that offload the ft entries may match on the 5-tuple and perform the action list. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Reviewed-by: Edward Cree Signed-off-by: David S. Miller --- include/net/flow_offload.h | 5 + include/net/netfilter/nf_flow_table.h | 23 ++++ net/netfilter/nf_flow_table_offload.c | 23 ---- net/sched/act_ct.c | 207 ++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index d1b1e4aa310a..ba433497789b 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -136,6 +136,7 @@ enum flow_action_id { FLOW_ACTION_SAMPLE, FLOW_ACTION_POLICE, FLOW_ACTION_CT, + FLOW_ACTION_CT_METADATA, FLOW_ACTION_MPLS_PUSH, FLOW_ACTION_MPLS_POP, FLOW_ACTION_MPLS_MANGLE, @@ -225,6 +226,10 @@ struct flow_action_entry { int action; u16 zone; } ct; + struct { + u32 mark; + u32 labels[4]; + } ct_metadata; struct { /* FLOW_ACTION_MPLS_PUSH */ u32 label; __be16 proto; diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index d9d0945b696e..c2d5cdd9904d 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -16,6 +16,29 @@ struct nf_flow_rule; struct flow_offload; enum flow_offload_tuple_dir; +struct nf_flow_key { + struct flow_dissector_key_meta meta; + struct flow_dissector_key_control control; + struct flow_dissector_key_basic basic; + union { + struct flow_dissector_key_ipv4_addrs ipv4; + struct flow_dissector_key_ipv6_addrs ipv6; + }; + struct flow_dissector_key_tcp tcp; + struct flow_dissector_key_ports tp; +} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ + +struct nf_flow_match { + struct flow_dissector dissector; + struct nf_flow_key key; + struct nf_flow_key mask; +}; + +struct nf_flow_rule { + struct nf_flow_match match; + struct flow_rule *rule; +}; + struct nf_flowtable_type { struct list_head list; int family; diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c index f5afdf023bb9..42b73a084a63 100644 --- a/net/netfilter/nf_flow_table_offload.c +++ b/net/netfilter/nf_flow_table_offload.c @@ -23,29 +23,6 @@ struct flow_offload_work { struct flow_offload *flow; }; -struct nf_flow_key { - struct flow_dissector_key_meta meta; - struct flow_dissector_key_control control; - struct flow_dissector_key_basic basic; - union { - struct flow_dissector_key_ipv4_addrs ipv4; - struct flow_dissector_key_ipv6_addrs ipv6; - }; - struct flow_dissector_key_tcp tcp; - struct flow_dissector_key_ports tp; -} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */ - -struct nf_flow_match { - struct flow_dissector dissector; - struct nf_flow_key key; - struct nf_flow_key mask; -}; - -struct nf_flow_rule { - struct nf_flow_match match; - struct flow_rule *rule; -}; - #define NF_FLOW_DISSECTOR(__match, __type, __field) \ (__match)->dissector.offset[__type] = \ offsetof(struct nf_flow_key, __field) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 3d9e678d7d53..9c522bc51f68 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -55,7 +55,214 @@ static const struct rhashtable_params zones_params = { .automatic_shrinking = true, }; +static struct flow_action_entry * +tcf_ct_flow_table_flow_action_get_next(struct flow_action *flow_action) +{ + int i = flow_action->num_entries++; + + return &flow_action->entries[i]; +} + +static void tcf_ct_add_mangle_action(struct flow_action *action, + enum flow_action_mangle_base htype, + u32 offset, + u32 mask, + u32 val) +{ + struct flow_action_entry *entry; + + entry = tcf_ct_flow_table_flow_action_get_next(action); + entry->id = FLOW_ACTION_MANGLE; + entry->mangle.htype = htype; + entry->mangle.mask = ~mask; + entry->mangle.offset = offset; + entry->mangle.val = val; +} + +/* The following nat helper functions check if the inverted reverse tuple + * (target) is different then the current dir tuple - meaning nat for ports + * and/or ip is needed, and add the relevant mangle actions. + */ +static void +tcf_ct_flow_table_add_action_nat_ipv4(const struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple target, + struct flow_action *action) +{ + if (memcmp(&target.src.u3, &tuple->src.u3, sizeof(target.src.u3))) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_IP4, + offsetof(struct iphdr, saddr), + 0xFFFFFFFF, + be32_to_cpu(target.src.u3.ip)); + if (memcmp(&target.dst.u3, &tuple->dst.u3, sizeof(target.dst.u3))) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_IP4, + offsetof(struct iphdr, daddr), + 0xFFFFFFFF, + be32_to_cpu(target.dst.u3.ip)); +} + +static void +tcf_ct_add_ipv6_addr_mangle_action(struct flow_action *action, + union nf_inet_addr *addr, + u32 offset) +{ + int i; + + for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_IP6, + i * sizeof(u32) + offset, + 0xFFFFFFFF, be32_to_cpu(addr->ip6[i])); +} + +static void +tcf_ct_flow_table_add_action_nat_ipv6(const struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple target, + struct flow_action *action) +{ + if (memcmp(&target.src.u3, &tuple->src.u3, sizeof(target.src.u3))) + tcf_ct_add_ipv6_addr_mangle_action(action, &target.src.u3, + offsetof(struct ipv6hdr, + saddr)); + if (memcmp(&target.dst.u3, &tuple->dst.u3, sizeof(target.dst.u3))) + tcf_ct_add_ipv6_addr_mangle_action(action, &target.dst.u3, + offsetof(struct ipv6hdr, + daddr)); +} + +static void +tcf_ct_flow_table_add_action_nat_tcp(const struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple target, + struct flow_action *action) +{ + __be16 target_src = target.src.u.tcp.port; + __be16 target_dst = target.dst.u.tcp.port; + + if (target_src != tuple->src.u.tcp.port) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP, + offsetof(struct tcphdr, source), + 0xFFFF, be16_to_cpu(target_src)); + if (target_dst != tuple->dst.u.tcp.port) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP, + offsetof(struct tcphdr, dest), + 0xFFFF, be16_to_cpu(target_dst)); +} + +static void +tcf_ct_flow_table_add_action_nat_udp(const struct nf_conntrack_tuple *tuple, + struct nf_conntrack_tuple target, + struct flow_action *action) +{ + __be16 target_src = target.src.u.udp.port; + __be16 target_dst = target.dst.u.udp.port; + + if (target_src != tuple->src.u.udp.port) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP, + offsetof(struct udphdr, source), + 0xFFFF, be16_to_cpu(target_src)); + if (target_dst != tuple->dst.u.udp.port) + tcf_ct_add_mangle_action(action, FLOW_ACT_MANGLE_HDR_TYPE_TCP, + offsetof(struct udphdr, dest), + 0xFFFF, be16_to_cpu(target_dst)); +} + +static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct, + enum ip_conntrack_dir dir, + struct flow_action *action) +{ + struct nf_conn_labels *ct_labels; + struct flow_action_entry *entry; + u32 *act_ct_labels; + + entry = tcf_ct_flow_table_flow_action_get_next(action); + entry->id = FLOW_ACTION_CT_METADATA; +#if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) + entry->ct_metadata.mark = ct->mark; +#endif + + act_ct_labels = entry->ct_metadata.labels; + ct_labels = nf_ct_labels_find(ct); + if (ct_labels) + memcpy(act_ct_labels, ct_labels->bits, NF_CT_LABELS_MAX_SIZE); + else + memset(act_ct_labels, 0, NF_CT_LABELS_MAX_SIZE); +} + +static int tcf_ct_flow_table_add_action_nat(struct net *net, + struct nf_conn *ct, + enum ip_conntrack_dir dir, + struct flow_action *action) +{ + const struct nf_conntrack_tuple *tuple = &ct->tuplehash[dir].tuple; + struct nf_conntrack_tuple target; + + nf_ct_invert_tuple(&target, &ct->tuplehash[!dir].tuple); + + switch (tuple->src.l3num) { + case NFPROTO_IPV4: + tcf_ct_flow_table_add_action_nat_ipv4(tuple, target, + action); + break; + case NFPROTO_IPV6: + tcf_ct_flow_table_add_action_nat_ipv6(tuple, target, + action); + break; + default: + return -EOPNOTSUPP; + } + + switch (nf_ct_protonum(ct)) { + case IPPROTO_TCP: + tcf_ct_flow_table_add_action_nat_tcp(tuple, target, action); + break; + case IPPROTO_UDP: + tcf_ct_flow_table_add_action_nat_udp(tuple, target, action); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int tcf_ct_flow_table_fill_actions(struct net *net, + const struct flow_offload *flow, + enum flow_offload_tuple_dir tdir, + struct nf_flow_rule *flow_rule) +{ + struct flow_action *action = &flow_rule->rule->action; + int num_entries = action->num_entries; + struct nf_conn *ct = flow->ct; + enum ip_conntrack_dir dir; + int i, err; + + switch (tdir) { + case FLOW_OFFLOAD_DIR_ORIGINAL: + dir = IP_CT_DIR_ORIGINAL; + break; + case FLOW_OFFLOAD_DIR_REPLY: + dir = IP_CT_DIR_REPLY; + break; + default: + return -EOPNOTSUPP; + } + + err = tcf_ct_flow_table_add_action_nat(net, ct, dir, action); + if (err) + goto err_nat; + + tcf_ct_flow_table_add_action_meta(ct, dir, action); + return 0; + +err_nat: + /* Clear filled actions */ + for (i = num_entries; i < action->num_entries; i++) + memset(&action->entries[i], 0, sizeof(action->entries[i])); + action->num_entries = num_entries; + + return err; +} + static struct nf_flowtable_type flowtable_ct = { + .action = tcf_ct_flow_table_fill_actions, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 30b0cf90c6dd82e7ebb3fcb5ba8447f1baeb80be Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:07 +0200 Subject: net/sched: act_ct: Support restoring conntrack info on skbs Provide an API to restore the ct state pointer. This may be used by drivers to restore the ct state if they miss in tc chain after they already did the hardware connection tracking action (ct_metadata action). For example, consider the following rule on chain 0 that is in_hw, however chain 1 is not_in_hw: $ tc filter add dev ... chain 0 ... \ flower ... action ct pipe action goto chain 1 Packets of a flow offloaded (via nf flow table offload) by the driver hit this rule in hardware, will be marked with the ct metadata action (mark, label, zone) that does the equivalent of the software ct action, and when the packet jumps to hardware chain 1, there would be a miss. CT was already processed in hardware. Therefore, the driver's miss handling should restore the ct state on the skb, using the provided API, and continue the packet processing in chain 1. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_offload.h | 1 + include/net/tc_act/tc_ct.h | 7 +++++++ net/sched/act_ct.c | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index ba433497789b..a039c900c384 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -227,6 +227,7 @@ struct flow_action_entry { u16 zone; } ct; struct { + unsigned long cookie; u32 mark; u32 labels[4]; } ct_metadata; diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h index cf3492e2a6a4..735da5912974 100644 --- a/include/net/tc_act/tc_ct.h +++ b/include/net/tc_act/tc_ct.h @@ -55,6 +55,13 @@ static inline uint16_t tcf_ct_zone(const struct tc_action *a) { return 0; } static inline int tcf_ct_action(const struct tc_action *a) { return 0; } #endif /* CONFIG_NF_CONNTRACK */ +#if IS_ENABLED(CONFIG_NET_ACT_CT) +void tcf_ct_flow_table_restore_skb(struct sk_buff *skb, unsigned long cookie); +#else +static inline void +tcf_ct_flow_table_restore_skb(struct sk_buff *skb, unsigned long cookie) { } +#endif + static inline bool is_tcf_ct(const struct tc_action *a) { #if defined(CONFIG_NET_CLS_ACT) && IS_ENABLED(CONFIG_NF_CONNTRACK) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 9c522bc51f68..31eef8a847d2 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -170,6 +170,7 @@ static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct, { struct nf_conn_labels *ct_labels; struct flow_action_entry *entry; + enum ip_conntrack_info ctinfo; u32 *act_ct_labels; entry = tcf_ct_flow_table_flow_action_get_next(action); @@ -177,6 +178,10 @@ static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct, #if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK) entry->ct_metadata.mark = ct->mark; #endif + ctinfo = dir == IP_CT_DIR_ORIGINAL ? IP_CT_ESTABLISHED : + IP_CT_ESTABLISHED_REPLY; + /* aligns with the CT reference on the SKB nf_ct_set */ + entry->ct_metadata.cookie = (unsigned long)ct | ctinfo; act_ct_labels = entry->ct_metadata.labels; ct_labels = nf_ct_labels_find(ct); @@ -1530,6 +1535,17 @@ static void __exit ct_cleanup_module(void) destroy_workqueue(act_ct_wq); } +void tcf_ct_flow_table_restore_skb(struct sk_buff *skb, unsigned long cookie) +{ + enum ip_conntrack_info ctinfo = cookie & NFCT_INFOMASK; + struct nf_conn *ct; + + ct = (struct nf_conn *)(cookie & NFCT_PTRMASK); + nf_conntrack_get(&ct->ct_general); + nf_ct_set(skb, ct, ctinfo); +} +EXPORT_SYMBOL_GPL(tcf_ct_flow_table_restore_skb); + module_init(ct_init_module); module_exit(ct_cleanup_module); MODULE_AUTHOR("Paul Blakey "); -- cgit v1.2.3 From 8b3646d6e0c4ca4ba5615facaef1312d6d40d123 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:08 +0200 Subject: net/sched: act_ct: Support refreshing the flow table entries If driver deleted an FT entry, a FT failed to offload, or registered to the flow table after flows were already added, we still get packets in software. For those packets, while restoring the ct state from the flow table entry, refresh it's hardware offload. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/netfilter/nf_flow_table.h | 3 +++ net/netfilter/nf_flow_table_core.c | 13 +++++++++++++ net/netfilter/nf_flow_table_ip.c | 15 ++------------- net/sched/act_ct.c | 1 + 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h index c2d5cdd9904d..6890f1ca3e31 100644 --- a/include/net/netfilter/nf_flow_table.h +++ b/include/net/netfilter/nf_flow_table.h @@ -162,6 +162,9 @@ int flow_offload_route_init(struct flow_offload *flow, const struct nf_flow_route *route); int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow); +void flow_offload_refresh(struct nf_flowtable *flow_table, + struct flow_offload *flow); + struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table, struct flow_offload_tuple *tuple); void nf_flow_table_cleanup(struct net_device *dev); diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c index 4af0327992cf..9a477bd563b7 100644 --- a/net/netfilter/nf_flow_table_core.c +++ b/net/netfilter/nf_flow_table_core.c @@ -252,6 +252,19 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow) } EXPORT_SYMBOL_GPL(flow_offload_add); +void flow_offload_refresh(struct nf_flowtable *flow_table, + struct flow_offload *flow) +{ + flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT; + + if (likely(!nf_flowtable_hw_offload(flow_table) || + !test_and_clear_bit(NF_FLOW_HW_REFRESH, &flow->flags))) + return; + + nf_flow_offload_add(flow_table, flow); +} +EXPORT_SYMBOL_GPL(flow_offload_refresh); + static inline bool nf_flow_has_expired(const struct flow_offload *flow) { return nf_flow_timeout_delta(flow->timeout) <= 0; diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 9e563fd3da0f..5272721080f8 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -232,13 +232,6 @@ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb, return NF_STOLEN; } -static bool nf_flow_offload_refresh(struct nf_flowtable *flow_table, - struct flow_offload *flow) -{ - return nf_flowtable_hw_offload(flow_table) && - test_and_clear_bit(NF_FLOW_HW_REFRESH, &flow->flags); -} - unsigned int nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, const struct nf_hook_state *state) @@ -279,8 +272,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, if (nf_flow_state_check(flow, ip_hdr(skb)->protocol, skb, thoff)) return NF_ACCEPT; - if (unlikely(nf_flow_offload_refresh(flow_table, flow))) - nf_flow_offload_add(flow_table, flow); + flow_offload_refresh(flow_table, flow); if (nf_flow_offload_dst_check(&rt->dst)) { flow_offload_teardown(flow); @@ -290,7 +282,6 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, if (nf_flow_nat_ip(flow, skb, thoff, dir) < 0) return NF_DROP; - flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT; iph = ip_hdr(skb); ip_decrease_ttl(iph); skb->tstamp = 0; @@ -508,8 +499,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, sizeof(*ip6h))) return NF_ACCEPT; - if (unlikely(nf_flow_offload_refresh(flow_table, flow))) - nf_flow_offload_add(flow_table, flow); + flow_offload_refresh(flow_table, flow); if (nf_flow_offload_dst_check(&rt->dst)) { flow_offload_teardown(flow); @@ -522,7 +512,6 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, if (nf_flow_nat_ipv6(flow, skb, dir) < 0) return NF_DROP; - flow->timeout = nf_flowtable_time_stamp + NF_FLOW_TIMEOUT; ip6h = ipv6_hdr(skb); ip6h->hop_limit--; skb->tstamp = 0; diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 31eef8a847d2..16fc731e5b03 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -531,6 +531,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p, ctinfo = dir == FLOW_OFFLOAD_DIR_ORIGINAL ? IP_CT_ESTABLISHED : IP_CT_ESTABLISHED_REPLY; + flow_offload_refresh(nf_ft, flow); nf_conntrack_get(&ct->ct_general); nf_ct_set(skb, ct, ctinfo); -- cgit v1.2.3 From edd5861e597b7ec2fae2fa3bc8180164045b5075 Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:09 +0200 Subject: net/sched: act_ct: Enable hardware offload of flow table entires Pass the zone's flow table instance on the flow action to the drivers. Thus, allowing drivers to register FT add/del/stats callbacks. Finally, enable hardware offload on the flow table instance. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_offload.h | 1 + include/net/tc_act/tc_ct.h | 10 ++++++++++ net/sched/act_ct.c | 2 ++ net/sched/cls_api.c | 1 + 4 files changed, 14 insertions(+) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index a039c900c384..ceaa3628796d 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -225,6 +225,7 @@ struct flow_action_entry { struct { /* FLOW_ACTION_CT */ int action; u16 zone; + struct nf_flowtable *flow_table; } ct; struct { unsigned long cookie; diff --git a/include/net/tc_act/tc_ct.h b/include/net/tc_act/tc_ct.h index 735da5912974..79654bcb9a29 100644 --- a/include/net/tc_act/tc_ct.h +++ b/include/net/tc_act/tc_ct.h @@ -27,6 +27,7 @@ struct tcf_ct_params { struct rcu_head rcu; struct tcf_ct_flow_table *ct_ft; + struct nf_flowtable *nf_ft; }; struct tcf_ct { @@ -50,9 +51,18 @@ static inline int tcf_ct_action(const struct tc_action *a) return to_ct_params(a)->ct_action; } +static inline struct nf_flowtable *tcf_ct_ft(const struct tc_action *a) +{ + return to_ct_params(a)->nf_ft; +} + #else static inline uint16_t tcf_ct_zone(const struct tc_action *a) { return 0; } static inline int tcf_ct_action(const struct tc_action *a) { return 0; } +static inline struct nf_flowtable *tcf_ct_ft(const struct tc_action *a) +{ + return NULL; +} #endif /* CONFIG_NF_CONNTRACK */ #if IS_ENABLED(CONFIG_NET_ACT_CT) diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c index 16fc731e5b03..56b66d215a89 100644 --- a/net/sched/act_ct.c +++ b/net/sched/act_ct.c @@ -292,6 +292,7 @@ static int tcf_ct_flow_table_get(struct tcf_ct_params *params) goto err_insert; ct_ft->nf_ft.type = &flowtable_ct; + ct_ft->nf_ft.flags |= NF_FLOWTABLE_HW_OFFLOAD; err = nf_flow_table_init(&ct_ft->nf_ft); if (err) goto err_init; @@ -299,6 +300,7 @@ static int tcf_ct_flow_table_get(struct tcf_ct_params *params) __module_get(THIS_MODULE); out_unlock: params->ct_ft = ct_ft; + params->nf_ft = &ct_ft->nf_ft; mutex_unlock(&zones_mutex); return 0; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 2b5b4ebda842..2046102a763e 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -3636,6 +3636,7 @@ int tc_setup_flow_action(struct flow_action *flow_action, entry->id = FLOW_ACTION_CT; entry->ct.action = tcf_ct_action(act); entry->ct.zone = tcf_ct_zone(act); + entry->ct.flow_table = tcf_ct_ft(act); } else if (is_tcf_mpls(act)) { switch (tcf_mpls_action(act)) { case TCA_MPLS_ACT_PUSH: -- cgit v1.2.3 From ee1c45e87595a1c85d8e348aa896b24f24a356ce Mon Sep 17 00:00:00 2001 From: Paul Blakey Date: Thu, 12 Mar 2020 12:23:13 +0200 Subject: flow_offload: Add flow_match_ct to get rule ct match Add relevant getter for ct info dissector. Signed-off-by: Paul Blakey Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/flow_offload.h | 6 ++++++ net/core/flow_offload.c | 7 +++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h index ceaa3628796d..efd8d47f6997 100644 --- a/include/net/flow_offload.h +++ b/include/net/flow_offload.h @@ -69,6 +69,10 @@ struct flow_match_enc_opts { struct flow_dissector_key_enc_opts *key, *mask; }; +struct flow_match_ct { + struct flow_dissector_key_ct *key, *mask; +}; + struct flow_rule; void flow_rule_match_meta(const struct flow_rule *rule, @@ -111,6 +115,8 @@ void flow_rule_match_enc_keyid(const struct flow_rule *rule, struct flow_match_enc_keyid *out); void flow_rule_match_enc_opts(const struct flow_rule *rule, struct flow_match_enc_opts *out); +void flow_rule_match_ct(const struct flow_rule *rule, + struct flow_match_ct *out); enum flow_action_id { FLOW_ACTION_ACCEPT = 0, diff --git a/net/core/flow_offload.c b/net/core/flow_offload.c index d21348202ba6..7440e6117c81 100644 --- a/net/core/flow_offload.c +++ b/net/core/flow_offload.c @@ -188,6 +188,13 @@ void flow_action_cookie_destroy(struct flow_action_cookie *cookie) } EXPORT_SYMBOL(flow_action_cookie_destroy); +void flow_rule_match_ct(const struct flow_rule *rule, + struct flow_match_ct *out) +{ + FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CT, out); +} +EXPORT_SYMBOL(flow_rule_match_ct); + struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb, void *cb_ident, void *cb_priv, void (*release)(void *cb_priv)) -- cgit v1.2.3 From 0524399d4612f5af38b8383680dde4df4bc4eea2 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:07:48 +0100 Subject: ethtool: provide netdev features with FEATURES_GET request Implement FEATURES_GET request to get network device features. These are traditionally available via ETHTOOL_GFEATURES ioctl request. v2: - style cleanup suggested by Jakub Kicinski Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 51 +++++++++-- include/uapi/linux/ethtool_netlink.h | 17 ++++ net/ethtool/Makefile | 2 +- net/ethtool/common.h | 2 + net/ethtool/features.c | 131 +++++++++++++++++++++++++++ net/ethtool/ioctl.c | 2 - net/ethtool/netlink.c | 8 ++ net/ethtool/netlink.h | 1 + 8 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 net/ethtool/features.c (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index f1f868479ceb..5713abf98534 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -189,6 +189,7 @@ Userspace to kernel: ``ETHTOOL_MSG_DEBUG_SET`` set debugging settings ``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings ``ETHTOOL_MSG_WOL_SET`` set wake-on-lan settings + ``ETHTOOL_MSG_FEATURES_GET`` get device features ===================================== ================================ Kernel to userspace: @@ -204,6 +205,7 @@ Kernel to userspace: ``ETHTOOL_MSG_DEBUG_NTF`` debugging settings notification ``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings ``ETHTOOL_MSG_WOL_NTF`` wake-on-lan settings notification + ``ETHTOOL_MSG_FEATURES_GET_REPLY`` device features ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -521,6 +523,37 @@ Request contents: ``WAKE_MAGICSECURE`` mode. +FEATURES_GET +============ + +Gets netdev features like ``ETHTOOL_GFEATURES`` ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_FEATURES_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_FEATURES_HEADER`` nested reply header + ``ETHTOOL_A_FEATURES_HW`` bitset dev->hw_features + ``ETHTOOL_A_FEATURES_WANTED`` bitset dev->wanted_features + ``ETHTOOL_A_FEATURES_ACTIVE`` bitset dev->features + ``ETHTOOL_A_FEATURES_NOCHANGE`` bitset NETIF_F_NEVER_CHANGE + ==================================== ====== ========================== + +Bitmaps in kernel response have the same meaning as bitmaps used in ioctl +interference but attribute names are different (they are based on +corresponding members of struct net_device). Legacy "flags" are not provided, +if userspace needs them (most likely only ethtool for backward compatibility), +it can calculate their values from related feature bits itself. +ETHA_FEATURES_HW uses mask consisting of all features recognized by kernel (to +provide all names when using verbose bitmap format), the other three use no +mask (simple bit lists). + + Request translation =================== @@ -551,30 +584,30 @@ have their netlink replacement yet. ``ETHTOOL_SRINGPARAM`` n/a ``ETHTOOL_GPAUSEPARAM`` n/a ``ETHTOOL_SPAUSEPARAM`` n/a - ``ETHTOOL_GRXCSUM`` n/a + ``ETHTOOL_GRXCSUM`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SRXCSUM`` n/a - ``ETHTOOL_GTXCSUM`` n/a + ``ETHTOOL_GTXCSUM`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_STXCSUM`` n/a - ``ETHTOOL_GSG`` n/a + ``ETHTOOL_GSG`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SSG`` n/a ``ETHTOOL_TEST`` n/a ``ETHTOOL_GSTRINGS`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_PHYS_ID`` n/a ``ETHTOOL_GSTATS`` n/a - ``ETHTOOL_GTSO`` n/a + ``ETHTOOL_GTSO`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_STSO`` n/a ``ETHTOOL_GPERMADDR`` rtnetlink ``RTM_GETLINK`` - ``ETHTOOL_GUFO`` n/a + ``ETHTOOL_GUFO`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SUFO`` n/a - ``ETHTOOL_GGSO`` n/a + ``ETHTOOL_GGSO`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SGSO`` n/a - ``ETHTOOL_GFLAGS`` n/a + ``ETHTOOL_GFLAGS`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFLAGS`` n/a ``ETHTOOL_GPFLAGS`` n/a ``ETHTOOL_SPFLAGS`` n/a ``ETHTOOL_GRXFH`` n/a ``ETHTOOL_SRXFH`` n/a - ``ETHTOOL_GGRO`` n/a + ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SGRO`` n/a ``ETHTOOL_GRXRINGS`` n/a ``ETHTOOL_GRXCLSRLCNT`` n/a @@ -589,7 +622,7 @@ have their netlink replacement yet. ``ETHTOOL_GSSET_INFO`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_GRXFHINDIR`` n/a ``ETHTOOL_SRXFHINDIR`` n/a - ``ETHTOOL_GFEATURES`` n/a + ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFEATURES`` n/a ``ETHTOOL_GCHANNELS`` n/a ``ETHTOOL_SCHANNELS`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 7e0b460f872c..d0cc7a0334c8 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -24,6 +24,7 @@ enum { ETHTOOL_MSG_DEBUG_SET, ETHTOOL_MSG_WOL_GET, ETHTOOL_MSG_WOL_SET, + ETHTOOL_MSG_FEATURES_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -43,6 +44,7 @@ enum { ETHTOOL_MSG_DEBUG_NTF, ETHTOOL_MSG_WOL_GET_REPLY, ETHTOOL_MSG_WOL_NTF, + ETHTOOL_MSG_FEATURES_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -228,6 +230,21 @@ enum { ETHTOOL_A_WOL_MAX = __ETHTOOL_A_WOL_CNT - 1 }; +/* FEATURES */ + +enum { + ETHTOOL_A_FEATURES_UNSPEC, + ETHTOOL_A_FEATURES_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_FEATURES_HW, /* bitset */ + ETHTOOL_A_FEATURES_WANTED, /* bitset */ + ETHTOOL_A_FEATURES_ACTIVE, /* bitset */ + ETHTOOL_A_FEATURES_NOCHANGE, /* bitset */ + + /* add new constants above here */ + __ETHTOOL_A_FEATURES_CNT, + ETHTOOL_A_FEATURES_MAX = __ETHTOOL_A_FEATURES_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 424545a4aaec..5be8c9ab26d1 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -5,4 +5,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ - linkstate.o debug.o wol.o + linkstate.o debug.o wol.o features.o diff --git a/net/ethtool/common.h b/net/ethtool/common.h index 40ba74e0b9bb..7dc1163800a7 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -6,6 +6,8 @@ #include #include +#define ETHTOOL_DEV_FEATURE_WORDS DIV_ROUND_UP(NETDEV_FEATURE_COUNT, 32) + /* compose link mode index from speed, type and duplex */ #define ETHTOOL_LINK_MODE(speed, type, duplex) \ ETHTOOL_LINK_MODE_ ## speed ## base ## type ## _ ## duplex ## _BIT diff --git a/net/ethtool/features.c b/net/ethtool/features.c new file mode 100644 index 000000000000..a0cc2b969053 --- /dev/null +++ b/net/ethtool/features.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" +#include "bitset.h" + +struct features_req_info { + struct ethnl_req_info base; +}; + +struct features_reply_data { + struct ethnl_reply_data base; + u32 hw[ETHTOOL_DEV_FEATURE_WORDS]; + u32 wanted[ETHTOOL_DEV_FEATURE_WORDS]; + u32 active[ETHTOOL_DEV_FEATURE_WORDS]; + u32 nochange[ETHTOOL_DEV_FEATURE_WORDS]; + u32 all[ETHTOOL_DEV_FEATURE_WORDS]; +}; + +#define FEATURES_REPDATA(__reply_base) \ + container_of(__reply_base, struct features_reply_data, base) + +static const struct nla_policy +features_get_policy[ETHTOOL_A_FEATURES_MAX + 1] = { + [ETHTOOL_A_FEATURES_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_FEATURES_HW] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_WANTED] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_ACTIVE] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_NOCHANGE] = { .type = NLA_REJECT }, +}; + +static void ethnl_features_to_bitmap32(u32 *dest, netdev_features_t src) +{ + unsigned int i; + + for (i = 0; i < ETHTOOL_DEV_FEATURE_WORDS; i++) + dest[i] = src >> (32 * i); +} + +static int features_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct features_reply_data *data = FEATURES_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + netdev_features_t all_features; + + ethnl_features_to_bitmap32(data->hw, dev->hw_features); + ethnl_features_to_bitmap32(data->wanted, dev->wanted_features); + ethnl_features_to_bitmap32(data->active, dev->features); + ethnl_features_to_bitmap32(data->nochange, NETIF_F_NEVER_CHANGE); + all_features = GENMASK_ULL(NETDEV_FEATURE_COUNT - 1, 0); + ethnl_features_to_bitmap32(data->all, all_features); + + return 0; +} + +static int features_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct features_reply_data *data = FEATURES_REPDATA(reply_base); + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + unsigned int len = 0; + int ret; + + ret = ethnl_bitset32_size(data->hw, data->all, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + len += ret; + ret = ethnl_bitset32_size(data->wanted, NULL, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + len += ret; + ret = ethnl_bitset32_size(data->active, NULL, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + len += ret; + ret = ethnl_bitset32_size(data->nochange, NULL, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + len += ret; + + return len; +} + +static int features_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct features_reply_data *data = FEATURES_REPDATA(reply_base); + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + int ret; + + ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_HW, data->hw, + data->all, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_WANTED, data->wanted, + NULL, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + ret = ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_ACTIVE, data->active, + NULL, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + return ret; + return ethnl_put_bitset32(skb, ETHTOOL_A_FEATURES_NOCHANGE, + data->nochange, NULL, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); +} + +const struct ethnl_request_ops ethnl_features_request_ops = { + .request_cmd = ETHTOOL_MSG_FEATURES_GET, + .reply_cmd = ETHTOOL_MSG_FEATURES_GET_REPLY, + .hdr_attr = ETHTOOL_A_FEATURES_HEADER, + .max_attr = ETHTOOL_A_FEATURES_MAX, + .req_info_size = sizeof(struct features_req_info), + .reply_data_size = sizeof(struct features_reply_data), + .request_policy = features_get_policy, + + .prepare_data = features_prepare_data, + .reply_size = features_reply_size, + .fill_reply = features_fill_reply, +}; diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index ae97c82c7052..45d1bf1764b7 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -56,8 +56,6 @@ EXPORT_SYMBOL(ethtool_op_get_ts_info); /* Handlers for each ethtool command */ -#define ETHTOOL_DEV_FEATURE_WORDS ((NETDEV_FEATURE_COUNT + 31) / 32) - static int ethtool_get_features(struct net_device *dev, void __user *useraddr) { struct ethtool_gfeatures cmd = { diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 8eca55122ef3..e451a75e9577 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -215,6 +215,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_LINKSTATE_GET] = ðnl_linkstate_request_ops, [ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops, [ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops, + [ETHTOOL_MSG_FEATURES_GET] = ðnl_features_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -695,6 +696,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_wol, }, + { + .cmd = ETHTOOL_MSG_FEATURES_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 961708290074..be2325ea8493 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -337,6 +337,7 @@ extern const struct ethnl_request_ops ethnl_linkmodes_request_ops; extern const struct ethnl_request_ops ethnl_linkstate_request_ops; extern const struct ethnl_request_ops ethnl_debug_request_ops; extern const struct ethnl_request_ops ethnl_wol_request_ops; +extern const struct ethnl_request_ops ethnl_features_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); -- cgit v1.2.3 From 0980bfcd6954f124e40a000b85335c197764de14 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:07:58 +0100 Subject: ethtool: set netdev features with FEATURES_SET request Implement FEATURES_SET netlink request to set network device features. These are traditionally set using ETHTOOL_SFEATURES ioctl request. Actual change is subject to netdev_change_features() sanity checks so that it can differ from what was requested. Unlike with most other SET requests, in addition to error code and optional extack, kernel provides an optional reply message (ETHTOOL_MSG_FEATURES_SET_REPLY) in the same format but with different semantics: information about difference between user request and actual result and difference between old and new state of dev->features. This reply message can be suppressed by setting ETHTOOL_FLAG_OMIT_REPLY flag in request header. Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 56 +++++++-- include/uapi/linux/ethtool_netlink.h | 2 + net/ethtool/features.c | 169 +++++++++++++++++++++++++++ net/ethtool/netlink.c | 5 + net/ethtool/netlink.h | 1 + 5 files changed, 224 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 5713abf98534..d6706c4aa972 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -190,6 +190,7 @@ Userspace to kernel: ``ETHTOOL_MSG_WOL_GET`` get wake-on-lan settings ``ETHTOOL_MSG_WOL_SET`` set wake-on-lan settings ``ETHTOOL_MSG_FEATURES_GET`` get device features + ``ETHTOOL_MSG_FEATURES_SET`` set device features ===================================== ================================ Kernel to userspace: @@ -206,6 +207,7 @@ Kernel to userspace: ``ETHTOOL_MSG_WOL_GET_REPLY`` wake-on-lan settings ``ETHTOOL_MSG_WOL_NTF`` wake-on-lan settings notification ``ETHTOOL_MSG_FEATURES_GET_REPLY`` device features + ``ETHTOOL_MSG_FEATURES_SET_REPLY`` optional reply to FEATURES_SET ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -554,6 +556,42 @@ provide all names when using verbose bitmap format), the other three use no mask (simple bit lists). +FEATURES_SET +============ + +Request to set netdev features like ``ETHTOOL_SFEATURES`` ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_FEATURES_HEADER`` nested request header + ``ETHTOOL_A_FEATURES_WANTED`` bitset requested features + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_FEATURES_HEADER`` nested reply header + ``ETHTOOL_A_FEATURES_WANTED`` bitset diff wanted vs. result + ``ETHTOOL_A_FEATURES_ACTIVE`` bitset diff old vs. new active + ==================================== ====== ========================== + +Request constains only one bitset which can be either value/mask pair (request +to change specific feature bits and leave the rest) or only a value (request +to set all features to specified set). + +As request is subject to netdev_change_features() sanity checks, optional +kernel reply (can be suppressed by ``ETHTOOL_FLAG_OMIT_REPLY`` flag in request +header) informs client about the actual result. ``ETHTOOL_A_FEATURES_WANTED`` +reports the difference between client request and actual result: mask consists +of bits which differ between requested features and result (dev->features +after the operation), value consists of values of these bits in the request +(i.e. negated values from resulting features). ``ETHTOOL_A_FEATURES_ACTIVE`` +reports the difference between old and new dev->features: mask consists of +bits which have changed, values are their values in new dev->features (after +the operation). + + Request translation =================== @@ -585,30 +623,30 @@ have their netlink replacement yet. ``ETHTOOL_GPAUSEPARAM`` n/a ``ETHTOOL_SPAUSEPARAM`` n/a ``ETHTOOL_GRXCSUM`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SRXCSUM`` n/a + ``ETHTOOL_SRXCSUM`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GTXCSUM`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_STXCSUM`` n/a + ``ETHTOOL_STXCSUM`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GSG`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SSG`` n/a + ``ETHTOOL_SSG`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_TEST`` n/a ``ETHTOOL_GSTRINGS`` ``ETHTOOL_MSG_STRSET_GET`` ``ETHTOOL_PHYS_ID`` n/a ``ETHTOOL_GSTATS`` n/a ``ETHTOOL_GTSO`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_STSO`` n/a + ``ETHTOOL_STSO`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GPERMADDR`` rtnetlink ``RTM_GETLINK`` ``ETHTOOL_GUFO`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SUFO`` n/a + ``ETHTOOL_SUFO`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GGSO`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SGSO`` n/a + ``ETHTOOL_SGSO`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GFLAGS`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SFLAGS`` n/a + ``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GPFLAGS`` n/a ``ETHTOOL_SPFLAGS`` n/a ``ETHTOOL_GRXFH`` n/a ``ETHTOOL_SRXFH`` n/a ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SGRO`` n/a + ``ETHTOOL_SGRO`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GRXRINGS`` n/a ``ETHTOOL_GRXCLSRLCNT`` n/a ``ETHTOOL_GRXCLSRULE`` n/a @@ -623,7 +661,7 @@ have their netlink replacement yet. ``ETHTOOL_GRXFHINDIR`` n/a ``ETHTOOL_SRXFHINDIR`` n/a ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` - ``ETHTOOL_SFEATURES`` n/a + ``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GCHANNELS`` n/a ``ETHTOOL_SCHANNELS`` n/a ``ETHTOOL_SET_DUMP`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index d0cc7a0334c8..6f7aaa6b7f42 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -25,6 +25,7 @@ enum { ETHTOOL_MSG_WOL_GET, ETHTOOL_MSG_WOL_SET, ETHTOOL_MSG_FEATURES_GET, + ETHTOOL_MSG_FEATURES_SET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -45,6 +46,7 @@ enum { ETHTOOL_MSG_WOL_GET_REPLY, ETHTOOL_MSG_WOL_NTF, ETHTOOL_MSG_FEATURES_GET_REPLY, + ETHTOOL_MSG_FEATURES_SET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/features.c b/net/ethtool/features.c index a0cc2b969053..4ac1e05684ce 100644 --- a/net/ethtool/features.c +++ b/net/ethtool/features.c @@ -129,3 +129,172 @@ const struct ethnl_request_ops ethnl_features_request_ops = { .reply_size = features_reply_size, .fill_reply = features_fill_reply, }; + +/* FEATURES_SET */ + +static const struct nla_policy +features_set_policy[ETHTOOL_A_FEATURES_MAX + 1] = { + [ETHTOOL_A_FEATURES_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_FEATURES_HW] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_WANTED] = { .type = NLA_NESTED }, + [ETHTOOL_A_FEATURES_ACTIVE] = { .type = NLA_REJECT }, + [ETHTOOL_A_FEATURES_NOCHANGE] = { .type = NLA_REJECT }, +}; + +static void ethnl_features_to_bitmap(unsigned long *dest, netdev_features_t val) +{ + const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT); + unsigned int i; + + bitmap_zero(dest, NETDEV_FEATURE_COUNT); + for (i = 0; i < words; i++) + dest[i] = (unsigned long)(val >> (i * BITS_PER_LONG)); +} + +static netdev_features_t ethnl_bitmap_to_features(unsigned long *src) +{ + const unsigned int nft_bits = sizeof(netdev_features_t) * BITS_PER_BYTE; + const unsigned int words = BITS_TO_LONGS(NETDEV_FEATURE_COUNT); + netdev_features_t ret = 0; + unsigned int i; + + for (i = 0; i < words; i++) + ret |= (netdev_features_t)(src[i]) << (i * BITS_PER_LONG); + ret &= ~(netdev_features_t)0 >> (nft_bits - NETDEV_FEATURE_COUNT); + return ret; +} + +static int features_send_reply(struct net_device *dev, struct genl_info *info, + const unsigned long *wanted, + const unsigned long *wanted_mask, + const unsigned long *active, + const unsigned long *active_mask, bool compact) +{ + struct sk_buff *rskb; + void *reply_payload; + int reply_len = 0; + int ret; + + reply_len = ethnl_reply_header_size(); + ret = ethnl_bitset_size(wanted, wanted_mask, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + goto err; + reply_len += ret; + ret = ethnl_bitset_size(active, active_mask, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + goto err; + reply_len += ret; + + ret = -ENOMEM; + rskb = ethnl_reply_init(reply_len, dev, ETHTOOL_MSG_FEATURES_SET_REPLY, + ETHTOOL_A_FEATURES_HEADER, info, + &reply_payload); + if (!rskb) + goto err; + + ret = ethnl_put_bitset(rskb, ETHTOOL_A_FEATURES_WANTED, wanted, + wanted_mask, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + goto nla_put_failure; + ret = ethnl_put_bitset(rskb, ETHTOOL_A_FEATURES_ACTIVE, active, + active_mask, NETDEV_FEATURE_COUNT, + netdev_features_strings, compact); + if (ret < 0) + goto nla_put_failure; + + genlmsg_end(rskb, reply_payload); + ret = genlmsg_reply(rskb, info); + return ret; + +nla_put_failure: + nlmsg_free(rskb); + WARN_ONCE(1, "calculated message payload length (%d) not sufficient\n", + reply_len); +err: + GENL_SET_ERR_MSG(info, "failed to send reply message"); + return ret; +} + +int ethnl_set_features(struct sk_buff *skb, struct genl_info *info) +{ + DECLARE_BITMAP(wanted_diff_mask, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(active_diff_mask, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(old_active, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(new_active, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(req_wanted, NETDEV_FEATURE_COUNT); + DECLARE_BITMAP(req_mask, NETDEV_FEATURE_COUNT); + struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1]; + struct ethnl_req_info req_info = {}; + struct net_device *dev; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_FEATURES_MAX, features_set_policy, + info->extack); + if (ret < 0) + return ret; + if (!tb[ETHTOOL_A_FEATURES_WANTED]) + return -EINVAL; + ret = ethnl_parse_header_dev_get(&req_info, + tb[ETHTOOL_A_FEATURES_HEADER], + genl_info_net(info), info->extack, + true); + if (ret < 0) + return ret; + dev = req_info.dev; + + rtnl_lock(); + ethnl_features_to_bitmap(old_active, dev->features); + ret = ethnl_parse_bitset(req_wanted, req_mask, NETDEV_FEATURE_COUNT, + tb[ETHTOOL_A_FEATURES_WANTED], + netdev_features_strings, info->extack); + if (ret < 0) + goto out_rtnl; + if (ethnl_bitmap_to_features(req_mask) & ~NETIF_F_ETHTOOL_BITS) { + GENL_SET_ERR_MSG(info, "attempt to change non-ethtool features"); + ret = -EINVAL; + goto out_rtnl; + } + + /* set req_wanted bits not in req_mask from old_active */ + bitmap_and(req_wanted, req_wanted, req_mask, NETDEV_FEATURE_COUNT); + bitmap_andnot(new_active, old_active, req_mask, NETDEV_FEATURE_COUNT); + bitmap_or(req_wanted, new_active, req_wanted, NETDEV_FEATURE_COUNT); + if (bitmap_equal(req_wanted, old_active, NETDEV_FEATURE_COUNT)) { + ret = 0; + goto out_rtnl; + } + + dev->wanted_features = ethnl_bitmap_to_features(req_wanted); + __netdev_update_features(dev); + ethnl_features_to_bitmap(new_active, dev->features); + + ret = 0; + if (!(req_info.flags & ETHTOOL_FLAG_OMIT_REPLY)) { + bool compact = req_info.flags & ETHTOOL_FLAG_COMPACT_BITSETS; + + bitmap_xor(wanted_diff_mask, req_wanted, new_active, + NETDEV_FEATURE_COUNT); + bitmap_xor(active_diff_mask, old_active, new_active, + NETDEV_FEATURE_COUNT); + bitmap_and(wanted_diff_mask, wanted_diff_mask, req_mask, + NETDEV_FEATURE_COUNT); + bitmap_and(req_wanted, req_wanted, wanted_diff_mask, + NETDEV_FEATURE_COUNT); + bitmap_and(new_active, new_active, active_diff_mask, + NETDEV_FEATURE_COUNT); + + ret = features_send_reply(dev, info, req_wanted, + wanted_diff_mask, new_active, + active_diff_mask, compact); + } + +out_rtnl: + rtnl_unlock(); + dev_put(dev); + return ret; +} diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index e451a75e9577..757ea3fc98a0 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -703,6 +703,11 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_FEATURES_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_features, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index be2325ea8493..135836201e89 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -343,5 +343,6 @@ int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); int ethnl_set_debug(struct sk_buff *skb, struct genl_info *info); int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info); +int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); #endif /* _NET_ETHTOOL_NETLINK_H */ -- cgit v1.2.3 From 9c6451ef4881c2f528c625766e8bb9af5e40941a Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:03 +0100 Subject: ethtool: add FEATURES_NTF notification Send ETHTOOL_MSG_FEATURES_NTF notification whenever network device features are modified using ETHTOOL_MSG_FEATURES_SET netlink message, ethtool ioctl request or any other way resulting in call to netdev_update_features() or netdev_change_features() Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 6 ++++++ include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/features.c | 4 ++++ net/ethtool/netlink.c | 29 +++++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index d6706c4aa972..47542f042e9d 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -208,6 +208,7 @@ Kernel to userspace: ``ETHTOOL_MSG_WOL_NTF`` wake-on-lan settings notification ``ETHTOOL_MSG_FEATURES_GET_REPLY`` device features ``ETHTOOL_MSG_FEATURES_SET_REPLY`` optional reply to FEATURES_SET + ``ETHTOOL_MSG_FEATURES_NTF`` netdev features notification ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -591,6 +592,11 @@ reports the difference between old and new dev->features: mask consists of bits which have changed, values are their values in new dev->features (after the operation). +``ETHTOOL_MSG_FEATURES_NTF`` notification is sent not only if device features +are modified using ``ETHTOOL_MSG_FEATURES_SET`` request or on of ethtool ioctl +request but also each time features are modified with netdev_update_features() +or netdev_change_features(). + Request translation =================== diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 6f7aaa6b7f42..3d0204cf96a6 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -47,6 +47,7 @@ enum { ETHTOOL_MSG_WOL_NTF, ETHTOOL_MSG_FEATURES_GET_REPLY, ETHTOOL_MSG_FEATURES_SET_REPLY, + ETHTOOL_MSG_FEATURES_NTF, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/features.c b/net/ethtool/features.c index 4ac1e05684ce..4e632dc987d8 100644 --- a/net/ethtool/features.c +++ b/net/ethtool/features.c @@ -230,6 +230,7 @@ int ethnl_set_features(struct sk_buff *skb, struct genl_info *info) struct nlattr *tb[ETHTOOL_A_FEATURES_MAX + 1]; struct ethnl_req_info req_info = {}; struct net_device *dev; + bool mod; int ret; ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, @@ -272,6 +273,7 @@ int ethnl_set_features(struct sk_buff *skb, struct genl_info *info) dev->wanted_features = ethnl_bitmap_to_features(req_wanted); __netdev_update_features(dev); ethnl_features_to_bitmap(new_active, dev->features); + mod = !bitmap_equal(old_active, new_active, NETDEV_FEATURE_COUNT); ret = 0; if (!(req_info.flags & ETHTOOL_FLAG_OMIT_REPLY)) { @@ -292,6 +294,8 @@ int ethnl_set_features(struct sk_buff *skb, struct genl_info *info) wanted_diff_mask, new_active, active_diff_mask, compact); } + if (mod) + ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF, NULL); out_rtnl: rtnl_unlock(); diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 757ea3fc98a0..5c0e361bfd66 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -528,6 +528,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { [ETHTOOL_MSG_LINKMODES_NTF] = ðnl_linkmodes_request_ops, [ETHTOOL_MSG_DEBUG_NTF] = ðnl_debug_request_ops, [ETHTOOL_MSG_WOL_NTF] = ðnl_wol_request_ops, + [ETHTOOL_MSG_FEATURES_NTF] = ðnl_features_request_ops, }; /* default notification handler */ @@ -613,6 +614,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { [ETHTOOL_MSG_LINKMODES_NTF] = ethnl_default_notify, [ETHTOOL_MSG_DEBUG_NTF] = ethnl_default_notify, [ETHTOOL_MSG_WOL_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_FEATURES_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) @@ -630,6 +632,29 @@ void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) } EXPORT_SYMBOL(ethtool_notify); +static void ethnl_notify_features(struct netdev_notifier_info *info) +{ + struct net_device *dev = netdev_notifier_info_to_dev(info); + + ethtool_notify(dev, ETHTOOL_MSG_FEATURES_NTF, NULL); +} + +static int ethnl_netdev_event(struct notifier_block *this, unsigned long event, + void *ptr) +{ + switch (event) { + case NETDEV_FEAT_CHANGE: + ethnl_notify_features(ptr); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block ethnl_netdev_notifier = { + .notifier_call = ethnl_netdev_event, +}; + /* genetlink setup */ static const struct genl_ops ethtool_genl_ops[] = { @@ -736,7 +761,9 @@ static int __init ethnl_init(void) return ret; ethnl_ok = true; - return 0; + ret = register_netdevice_notifier(ðnl_netdev_notifier); + WARN(ret < 0, "ethtool: net device notifier registration failed"); + return ret; } subsys_initcall(ethnl_init); -- cgit v1.2.3 From e16c3386fc4d9611b6a2abaa639318c7ae793370 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:08 +0100 Subject: ethtool: provide private flags with PRIVFLAGS_GET request Implement PRIVFLAGS_GET request to get private flags for a network device. These are traditionally available via ETHTOOL_GPFLAGS ioctl request. Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 30 +++++- include/uapi/linux/ethtool_netlink.h | 14 +++ net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 8 ++ net/ethtool/netlink.h | 1 + net/ethtool/privflags.c | 136 +++++++++++++++++++++++++++ 6 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 net/ethtool/privflags.c (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 47542f042e9d..7bba4c940ef7 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -191,6 +191,7 @@ Userspace to kernel: ``ETHTOOL_MSG_WOL_SET`` set wake-on-lan settings ``ETHTOOL_MSG_FEATURES_GET`` get device features ``ETHTOOL_MSG_FEATURES_SET`` set device features + ``ETHTOOL_MSG_PRIVFLAGS_GET`` get private flags ===================================== ================================ Kernel to userspace: @@ -209,6 +210,7 @@ Kernel to userspace: ``ETHTOOL_MSG_FEATURES_GET_REPLY`` device features ``ETHTOOL_MSG_FEATURES_SET_REPLY`` optional reply to FEATURES_SET ``ETHTOOL_MSG_FEATURES_NTF`` netdev features notification + ``ETHTOOL_MSG_PRIVFLAGS_GET_REPLY`` private flags ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -598,6 +600,32 @@ request but also each time features are modified with netdev_update_features() or netdev_change_features(). +PRIVFLAGS_GET +============= + +Gets private flags like ``ETHTOOL_GPFLAGS`` ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_PRIVFLAGS_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_PRIVFLAGS_HEADER`` nested reply header + ``ETHTOOL_A_PRIVFLAGS_FLAGS`` bitset private flags + ==================================== ====== ========================== + +``ETHTOOL_A_PRIVFLAGS_FLAGS`` is a bitset with values of device private flags. +These flags are defined by driver, their number and names (and also meaning) +are device dependent. For compact bitset format, names can be retrieved as +``ETH_SS_PRIV_FLAGS`` string set. If verbose bitset format is requested, +response uses all private flags supported by the device as mask so that client +gets the full information without having to fetch the string set with names. + + Request translation =================== @@ -647,7 +675,7 @@ have their netlink replacement yet. ``ETHTOOL_SGSO`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GFLAGS`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET`` - ``ETHTOOL_GPFLAGS`` n/a + ``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET`` ``ETHTOOL_SPFLAGS`` n/a ``ETHTOOL_GRXFH`` n/a ``ETHTOOL_SRXFH`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 3d0204cf96a6..d94bbf5e4d1c 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -26,6 +26,7 @@ enum { ETHTOOL_MSG_WOL_SET, ETHTOOL_MSG_FEATURES_GET, ETHTOOL_MSG_FEATURES_SET, + ETHTOOL_MSG_PRIVFLAGS_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -48,6 +49,7 @@ enum { ETHTOOL_MSG_FEATURES_GET_REPLY, ETHTOOL_MSG_FEATURES_SET_REPLY, ETHTOOL_MSG_FEATURES_NTF, + ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -248,6 +250,18 @@ enum { ETHTOOL_A_FEATURES_MAX = __ETHTOOL_A_FEATURES_CNT - 1 }; +/* PRIVFLAGS */ + +enum { + ETHTOOL_A_PRIVFLAGS_UNSPEC, + ETHTOOL_A_PRIVFLAGS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_PRIVFLAGS_FLAGS, /* bitset */ + + /* add new constants above here */ + __ETHTOOL_A_PRIVFLAGS_CNT, + ETHTOOL_A_PRIVFLAGS_MAX = __ETHTOOL_A_PRIVFLAGS_CNT - 1 +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 5be8c9ab26d1..58708bcc2968 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -5,4 +5,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ - linkstate.o debug.o wol.o features.o + linkstate.o debug.o wol.o features.o privflags.o diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 5c0e361bfd66..9cbb1d8b4d23 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -216,6 +216,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_DEBUG_GET] = ðnl_debug_request_ops, [ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops, [ETHTOOL_MSG_FEATURES_GET] = ðnl_features_request_ops, + [ETHTOOL_MSG_PRIVFLAGS_GET] = ðnl_privflags_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -733,6 +734,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_features, }, + { + .cmd = ETHTOOL_MSG_PRIVFLAGS_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 135836201e89..36cf077c3085 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -338,6 +338,7 @@ extern const struct ethnl_request_ops ethnl_linkstate_request_ops; extern const struct ethnl_request_ops ethnl_debug_request_ops; extern const struct ethnl_request_ops ethnl_wol_request_ops; extern const struct ethnl_request_ops ethnl_features_request_ops; +extern const struct ethnl_request_ops ethnl_privflags_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); diff --git a/net/ethtool/privflags.c b/net/ethtool/privflags.c new file mode 100644 index 000000000000..169dd4a832f6 --- /dev/null +++ b/net/ethtool/privflags.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" +#include "bitset.h" + +struct privflags_req_info { + struct ethnl_req_info base; +}; + +struct privflags_reply_data { + struct ethnl_reply_data base; + const char (*priv_flag_names)[ETH_GSTRING_LEN]; + unsigned int n_priv_flags; + u32 priv_flags; +}; + +#define PRIVFLAGS_REPDATA(__reply_base) \ + container_of(__reply_base, struct privflags_reply_data, base) + +static const struct nla_policy +privflags_get_policy[ETHTOOL_A_PRIVFLAGS_MAX + 1] = { + [ETHTOOL_A_PRIVFLAGS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_PRIVFLAGS_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_REJECT }, +}; + +static int ethnl_get_priv_flags_info(struct net_device *dev, + unsigned int *count, + const char (**names)[ETH_GSTRING_LEN]) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + int nflags; + + nflags = ops->get_sset_count(dev, ETH_SS_PRIV_FLAGS); + if (nflags < 0) + return nflags; + + if (names) { + *names = kcalloc(nflags, ETH_GSTRING_LEN, GFP_KERNEL); + if (!*names) + return -ENOMEM; + ops->get_strings(dev, ETH_SS_PRIV_FLAGS, (u8 *)*names); + } + + /* We can pass more than 32 private flags to userspace via netlink but + * we cannot get more with ethtool_ops::get_priv_flags(). Note that we + * must not adjust nflags before allocating the space for flag names + * as the buffer must be large enough for all flags. + */ + if (WARN_ONCE(nflags > 32, + "device %s reports more than 32 private flags (%d)\n", + netdev_name(dev), nflags)) + nflags = 32; + *count = nflags; + + return 0; +} + +static int privflags_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + const char (*names)[ETH_GSTRING_LEN]; + const struct ethtool_ops *ops; + unsigned int nflags; + int ret; + + ops = dev->ethtool_ops; + if (!ops->get_priv_flags || !ops->get_sset_count || !ops->get_strings) + return -EOPNOTSUPP; + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + + ret = ethnl_get_priv_flags_info(dev, &nflags, &names); + if (ret < 0) + goto out_ops; + data->priv_flags = ops->get_priv_flags(dev); + data->priv_flag_names = names; + data->n_priv_flags = nflags; + +out_ops: + ethnl_ops_complete(dev); + return ret; +} + +static int privflags_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base); + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags); + + return ethnl_bitset32_size(&data->priv_flags, &all_flags, + data->n_priv_flags, + data->priv_flag_names, compact); +} + +static int privflags_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_base); + bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; + const u32 all_flags = ~(u32)0 >> (32 - data->n_priv_flags); + + return ethnl_put_bitset32(skb, ETHTOOL_A_PRIVFLAGS_FLAGS, + &data->priv_flags, &all_flags, + data->n_priv_flags, data->priv_flag_names, + compact); +} + +static void privflags_cleanup_data(struct ethnl_reply_data *reply_data) +{ + struct privflags_reply_data *data = PRIVFLAGS_REPDATA(reply_data); + + kfree(data->priv_flag_names); +} + +const struct ethnl_request_ops ethnl_privflags_request_ops = { + .request_cmd = ETHTOOL_MSG_PRIVFLAGS_GET, + .reply_cmd = ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, + .hdr_attr = ETHTOOL_A_PRIVFLAGS_HEADER, + .max_attr = ETHTOOL_A_PRIVFLAGS_MAX, + .req_info_size = sizeof(struct privflags_req_info), + .reply_data_size = sizeof(struct privflags_reply_data), + .request_policy = privflags_get_policy, + + .prepare_data = privflags_prepare_data, + .reply_size = privflags_reply_size, + .fill_reply = privflags_fill_reply, + .cleanup_data = privflags_cleanup_data, +}; -- cgit v1.2.3 From f265d799596aff80fe19b0b3896a4abe13cdb534 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:13 +0100 Subject: ethtool: set device private flags with PRIVFLAGS_SET request Implement PRIVFLAGS_SET netlink request to set private flags of a network device. These are traditionally set with ETHTOOL_SPFLAGS ioctl request. Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 21 ++++++++- include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/netlink.c | 5 ++ net/ethtool/netlink.h | 1 + net/ethtool/privflags.c | 70 ++++++++++++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 7bba4c940ef7..e553112fa2d7 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -192,6 +192,7 @@ Userspace to kernel: ``ETHTOOL_MSG_FEATURES_GET`` get device features ``ETHTOOL_MSG_FEATURES_SET`` set device features ``ETHTOOL_MSG_PRIVFLAGS_GET`` get private flags + ``ETHTOOL_MSG_PRIVFLAGS_SET`` set private flags ===================================== ================================ Kernel to userspace: @@ -211,6 +212,7 @@ Kernel to userspace: ``ETHTOOL_MSG_FEATURES_SET_REPLY`` optional reply to FEATURES_SET ``ETHTOOL_MSG_FEATURES_NTF`` netdev features notification ``ETHTOOL_MSG_PRIVFLAGS_GET_REPLY`` private flags + ``ETHTOOL_MSG_PRIVFLAGS_NTF`` private flags ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -626,6 +628,23 @@ response uses all private flags supported by the device as mask so that client gets the full information without having to fetch the string set with names. +PRIVFLAGS_SET +============= + +Sets or modifies values of device private flags like ``ETHTOOL_SPFLAGS`` +ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_PRIVFLAGS_HEADER`` nested request header + ``ETHTOOL_A_PRIVFLAGS_FLAGS`` bitset private flags + ==================================== ====== ========================== + +``ETHTOOL_A_PRIVFLAGS_FLAGS`` can either set the whole set of private flags or +modify only values of some of them. + + Request translation =================== @@ -676,7 +695,7 @@ have their netlink replacement yet. ``ETHTOOL_GFLAGS`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFLAGS`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_GET`` - ``ETHTOOL_SPFLAGS`` n/a + ``ETHTOOL_SPFLAGS`` ``ETHTOOL_MSG_PRIVFLAGS_SET`` ``ETHTOOL_GRXFH`` n/a ``ETHTOOL_SRXFH`` n/a ``ETHTOOL_GGRO`` ``ETHTOOL_MSG_FEATURES_GET`` diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index d94bbf5e4d1c..13e631c86825 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -27,6 +27,7 @@ enum { ETHTOOL_MSG_FEATURES_GET, ETHTOOL_MSG_FEATURES_SET, ETHTOOL_MSG_PRIVFLAGS_GET, + ETHTOOL_MSG_PRIVFLAGS_SET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 9cbb1d8b4d23..b6795aad7ccb 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -741,6 +741,11 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_PRIVFLAGS_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_privflags, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 36cf077c3085..ca789f587ef3 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -345,5 +345,6 @@ int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); int ethnl_set_debug(struct sk_buff *skb, struct genl_info *info); int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info); int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); +int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info); #endif /* _NET_ETHTOOL_NETLINK_H */ diff --git a/net/ethtool/privflags.c b/net/ethtool/privflags.c index 169dd4a832f6..dfa76d552277 100644 --- a/net/ethtool/privflags.c +++ b/net/ethtool/privflags.c @@ -134,3 +134,73 @@ const struct ethnl_request_ops ethnl_privflags_request_ops = { .fill_reply = privflags_fill_reply, .cleanup_data = privflags_cleanup_data, }; + +/* PRIVFLAGS_SET */ + +static const struct nla_policy +privflags_set_policy[ETHTOOL_A_PRIVFLAGS_MAX + 1] = { + [ETHTOOL_A_PRIVFLAGS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_PRIVFLAGS_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_PRIVFLAGS_FLAGS] = { .type = NLA_NESTED }, +}; + +int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[ETHTOOL_A_PRIVFLAGS_MAX + 1]; + const char (*names)[ETH_GSTRING_LEN] = NULL; + struct ethnl_req_info req_info = {}; + const struct ethtool_ops *ops; + struct net_device *dev; + unsigned int nflags; + bool mod = false; + bool compact; + u32 flags; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_PRIVFLAGS_MAX, privflags_set_policy, + info->extack); + if (ret < 0) + return ret; + if (!tb[ETHTOOL_A_PRIVFLAGS_FLAGS]) + return -EINVAL; + ret = ethnl_bitset_is_compact(tb[ETHTOOL_A_PRIVFLAGS_FLAGS], &compact); + if (ret < 0) + return ret; + ret = ethnl_parse_header_dev_get(&req_info, + tb[ETHTOOL_A_PRIVFLAGS_HEADER], + genl_info_net(info), info->extack, + true); + if (ret < 0) + return ret; + dev = req_info.dev; + ops = dev->ethtool_ops; + if (!ops->get_priv_flags || !ops->set_priv_flags || + !ops->get_sset_count || !ops->get_strings) + return -EOPNOTSUPP; + + rtnl_lock(); + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto out_rtnl; + ret = ethnl_get_priv_flags_info(dev, &nflags, compact ? NULL : &names); + if (ret < 0) + goto out_ops; + flags = ops->get_priv_flags(dev); + + ret = ethnl_update_bitset32(&flags, nflags, + tb[ETHTOOL_A_PRIVFLAGS_FLAGS], names, + info->extack, &mod); + if (ret < 0 || !mod) + goto out_free; + ret = ops->set_priv_flags(dev, flags); + +out_free: + kfree(names); +out_ops: + ethnl_ops_complete(dev); +out_rtnl: + rtnl_unlock(); + dev_put(dev); + return ret; +} -- cgit v1.2.3 From 111dcba3c694b4ca7dc8b6abfd575745dd51be3d Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:18 +0100 Subject: ethtool: add PRIVFLAGS_NTF notification Send ETHTOOL_MSG_PRIVFLAGS_NTF notification whenever private flags of a network device are modified using ETHTOOL_MSG_PRIVFLAGS_SET netlink message or ETHTOOL_SPFLAGS ioctl request. Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller --- include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/ioctl.c | 2 ++ net/ethtool/netlink.c | 2 ++ net/ethtool/privflags.c | 3 +++ 4 files changed, 8 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 13e631c86825..7f23a7f0fca1 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -51,6 +51,7 @@ enum { ETHTOOL_MSG_FEATURES_SET_REPLY, ETHTOOL_MSG_FEATURES_NTF, ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, + ETHTOOL_MSG_PRIVFLAGS_NTF, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 45d1bf1764b7..298822289496 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -2716,6 +2716,8 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GPFLAGS: rc = ethtool_get_value(dev, useraddr, ethcmd, dev->ethtool_ops->get_priv_flags); + if (!rc) + ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL); break; case ETHTOOL_SPFLAGS: rc = ethtool_set_value(dev, useraddr, diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index b6795aad7ccb..dec82b0b80bd 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -530,6 +530,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { [ETHTOOL_MSG_DEBUG_NTF] = ðnl_debug_request_ops, [ETHTOOL_MSG_WOL_NTF] = ðnl_wol_request_ops, [ETHTOOL_MSG_FEATURES_NTF] = ðnl_features_request_ops, + [ETHTOOL_MSG_PRIVFLAGS_NTF] = ðnl_privflags_request_ops, }; /* default notification handler */ @@ -616,6 +617,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { [ETHTOOL_MSG_DEBUG_NTF] = ethnl_default_notify, [ETHTOOL_MSG_WOL_NTF] = ethnl_default_notify, [ETHTOOL_MSG_FEATURES_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_PRIVFLAGS_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) diff --git a/net/ethtool/privflags.c b/net/ethtool/privflags.c index dfa76d552277..e8f03b33db9b 100644 --- a/net/ethtool/privflags.c +++ b/net/ethtool/privflags.c @@ -194,6 +194,9 @@ int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info) if (ret < 0 || !mod) goto out_free; ret = ops->set_priv_flags(dev, flags); + if (ret < 0) + goto out_free; + ethtool_notify(dev, ETHTOOL_MSG_PRIVFLAGS_NTF, NULL); out_free: kfree(names); -- cgit v1.2.3 From e4a1717b677c5cb285e9b425c569e261084a484c Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:23 +0100 Subject: ethtool: provide ring sizes with RINGS_GET request Implement RINGS_GET request to get ring sizes of a network device. These are traditionally available via ETHTOOL_GRINGPARAM ioctl request. Omit attributes for ring types which are not supported by driver or device (zero reported for maximum). v2: (all suggested by Jakub Kicinski) - minor cleanup in rings_prepare_data() - more descriptive rings_reply_size() - omit attributes with zero max size Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 30 +++++++- include/uapi/linux/ethtool_netlink.h | 21 ++++++ net/ethtool/Makefile | 2 +- net/ethtool/netlink.c | 8 ++ net/ethtool/netlink.h | 1 + net/ethtool/rings.c | 108 +++++++++++++++++++++++++++ 6 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 net/ethtool/rings.c (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index e553112fa2d7..798c2f97d89b 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -193,6 +193,7 @@ Userspace to kernel: ``ETHTOOL_MSG_FEATURES_SET`` set device features ``ETHTOOL_MSG_PRIVFLAGS_GET`` get private flags ``ETHTOOL_MSG_PRIVFLAGS_SET`` set private flags + ``ETHTOOL_MSG_RINGS_GET`` get ring sizes ===================================== ================================ Kernel to userspace: @@ -213,6 +214,7 @@ Kernel to userspace: ``ETHTOOL_MSG_FEATURES_NTF`` netdev features notification ``ETHTOOL_MSG_PRIVFLAGS_GET_REPLY`` private flags ``ETHTOOL_MSG_PRIVFLAGS_NTF`` private flags + ``ETHTOOL_MSG_RINGS_GET_REPLY`` ring sizes ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -645,6 +647,32 @@ Request contents: modify only values of some of them. +RINGS_GET +========= + +Gets ring sizes like ``ETHTOOL_GRINGPARAM`` ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_RINGS_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_RINGS_HEADER`` nested reply header + ``ETHTOOL_A_RINGS_RX_MAX`` u32 max size of RX ring + ``ETHTOOL_A_RINGS_RX_MINI_MAX`` u32 max size of RX mini ring + ``ETHTOOL_A_RINGS_RX_JUMBO_MAX`` u32 max size of RX jumbo ring + ``ETHTOOL_A_RINGS_TX_MAX`` u32 max size of TX ring + ``ETHTOOL_A_RINGS_RX`` u32 size of RX ring + ``ETHTOOL_A_RINGS_RX_MINI`` u32 size of RX mini ring + ``ETHTOOL_A_RINGS_RX_JUMBO`` u32 size of RX jumbo ring + ``ETHTOOL_A_RINGS_TX`` u32 size of TX ring + ==================================== ====== ========================== + + Request translation =================== @@ -671,7 +699,7 @@ have their netlink replacement yet. ``ETHTOOL_SEEPROM`` n/a ``ETHTOOL_GCOALESCE`` n/a ``ETHTOOL_SCOALESCE`` n/a - ``ETHTOOL_GRINGPARAM`` n/a + ``ETHTOOL_GRINGPARAM`` ``ETHTOOL_MSG_RINGS_GET`` ``ETHTOOL_SRINGPARAM`` n/a ``ETHTOOL_GPAUSEPARAM`` n/a ``ETHTOOL_SPAUSEPARAM`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 7f23a7f0fca1..7cd220f8cf73 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -28,6 +28,7 @@ enum { ETHTOOL_MSG_FEATURES_SET, ETHTOOL_MSG_PRIVFLAGS_GET, ETHTOOL_MSG_PRIVFLAGS_SET, + ETHTOOL_MSG_RINGS_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -52,6 +53,7 @@ enum { ETHTOOL_MSG_FEATURES_NTF, ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, ETHTOOL_MSG_PRIVFLAGS_NTF, + ETHTOOL_MSG_RINGS_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -264,6 +266,25 @@ enum { ETHTOOL_A_PRIVFLAGS_MAX = __ETHTOOL_A_PRIVFLAGS_CNT - 1 }; +/* RINGS */ + +enum { + ETHTOOL_A_RINGS_UNSPEC, + ETHTOOL_A_RINGS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_RINGS_RX_MAX, /* u32 */ + ETHTOOL_A_RINGS_RX_MINI_MAX, /* u32 */ + ETHTOOL_A_RINGS_RX_JUMBO_MAX, /* u32 */ + ETHTOOL_A_RINGS_TX_MAX, /* u32 */ + ETHTOOL_A_RINGS_RX, /* u32 */ + ETHTOOL_A_RINGS_RX_MINI, /* u32 */ + ETHTOOL_A_RINGS_RX_JUMBO, /* u32 */ + ETHTOOL_A_RINGS_TX, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_RINGS_CNT, + ETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index 58708bcc2968..b48b70ef4ed0 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -5,4 +5,4 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ - linkstate.o debug.o wol.o features.o privflags.o + linkstate.o debug.o wol.o features.o privflags.o rings.o diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index dec82b0b80bd..0dc25a490450 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -217,6 +217,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_WOL_GET] = ðnl_wol_request_ops, [ETHTOOL_MSG_FEATURES_GET] = ðnl_features_request_ops, [ETHTOOL_MSG_PRIVFLAGS_GET] = ðnl_privflags_request_ops, + [ETHTOOL_MSG_RINGS_GET] = ðnl_rings_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -748,6 +749,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_privflags, }, + { + .cmd = ETHTOOL_MSG_RINGS_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index ca789f587ef3..01761932ed15 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -339,6 +339,7 @@ extern const struct ethnl_request_ops ethnl_debug_request_ops; extern const struct ethnl_request_ops ethnl_wol_request_ops; extern const struct ethnl_request_ops ethnl_features_request_ops; extern const struct ethnl_request_ops ethnl_privflags_request_ops; +extern const struct ethnl_request_ops ethnl_rings_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c new file mode 100644 index 000000000000..d3129d8a252d --- /dev/null +++ b/net/ethtool/rings.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" + +struct rings_req_info { + struct ethnl_req_info base; +}; + +struct rings_reply_data { + struct ethnl_reply_data base; + struct ethtool_ringparam ringparam; +}; + +#define RINGS_REPDATA(__reply_base) \ + container_of(__reply_base, struct rings_reply_data, base) + +static const struct nla_policy +rings_get_policy[ETHTOOL_A_RINGS_MAX + 1] = { + [ETHTOOL_A_RINGS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_RINGS_RX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX_MINI_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX_JUMBO_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_TX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX_MINI] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_TX] = { .type = NLA_REJECT }, +}; + +static int rings_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct rings_reply_data *data = RINGS_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + if (!dev->ethtool_ops->get_ringparam) + return -EOPNOTSUPP; + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + dev->ethtool_ops->get_ringparam(dev, &data->ringparam); + ethnl_ops_complete(dev); + + return 0; +} + +static int rings_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + return nla_total_size(sizeof(u32)) + /* _RINGS_RX_MAX */ + nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI_MAX */ + nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO_MAX */ + nla_total_size(sizeof(u32)) + /* _RINGS_TX_MAX */ + nla_total_size(sizeof(u32)) + /* _RINGS_RX */ + nla_total_size(sizeof(u32)) + /* _RINGS_RX_MINI */ + nla_total_size(sizeof(u32)) + /* _RINGS_RX_JUMBO */ + nla_total_size(sizeof(u32)); /* _RINGS_TX */ +} + +static int rings_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct rings_reply_data *data = RINGS_REPDATA(reply_base); + const struct ethtool_ringparam *ringparam = &data->ringparam; + + if ((ringparam->rx_max_pending && + (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX, + ringparam->rx_max_pending) || + nla_put_u32(skb, ETHTOOL_A_RINGS_RX, + ringparam->rx_pending))) || + (ringparam->rx_mini_max_pending && + (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX, + ringparam->rx_mini_max_pending) || + nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI, + ringparam->rx_mini_pending))) || + (ringparam->rx_jumbo_max_pending && + (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX, + ringparam->rx_jumbo_max_pending) || + nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO, + ringparam->rx_jumbo_pending))) || + (ringparam->tx_max_pending && + (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX, + ringparam->tx_max_pending) || + nla_put_u32(skb, ETHTOOL_A_RINGS_TX, + ringparam->tx_pending)))) + return -EMSGSIZE; + + return 0; +} + +const struct ethnl_request_ops ethnl_rings_request_ops = { + .request_cmd = ETHTOOL_MSG_RINGS_GET, + .reply_cmd = ETHTOOL_MSG_RINGS_GET_REPLY, + .hdr_attr = ETHTOOL_A_RINGS_HEADER, + .max_attr = ETHTOOL_A_RINGS_MAX, + .req_info_size = sizeof(struct rings_req_info), + .reply_data_size = sizeof(struct rings_reply_data), + .request_policy = rings_get_policy, + + .prepare_data = rings_prepare_data, + .reply_size = rings_reply_size, + .fill_reply = rings_fill_reply, +}; -- cgit v1.2.3 From 2fc2929e807211a9535a6541f24b57fa1c469728 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:28 +0100 Subject: ethtool: set device ring sizes with RINGS_SET request Implement RINGS_SET netlink request to set ring sizes of a network device. These are traditionally set with ETHTOOL_SRINGPARAM ioctl request. Like the ioctl implementation, the generic ethtool code checks if supplied values do not exceed driver defined limits; if they do, first offending attribute is reported using extack. v2: - fix netdev reference leak in error path (found by Jakub Kicinsky) Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 23 ++++++- include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/netlink.c | 5 ++ net/ethtool/netlink.h | 1 + net/ethtool/rings.c | 89 ++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 798c2f97d89b..ba31ae8f1feb 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -194,6 +194,7 @@ Userspace to kernel: ``ETHTOOL_MSG_PRIVFLAGS_GET`` get private flags ``ETHTOOL_MSG_PRIVFLAGS_SET`` set private flags ``ETHTOOL_MSG_RINGS_GET`` get ring sizes + ``ETHTOOL_MSG_RINGS_SET`` set ring sizes ===================================== ================================ Kernel to userspace: @@ -673,6 +674,26 @@ Kernel response contents: ==================================== ====== ========================== +RINGS_SET +========= + +Sets ring sizes like ``ETHTOOL_SRINGPARAM`` ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_RINGS_HEADER`` nested reply header + ``ETHTOOL_A_RINGS_RX`` u32 size of RX ring + ``ETHTOOL_A_RINGS_RX_MINI`` u32 size of RX mini ring + ``ETHTOOL_A_RINGS_RX_JUMBO`` u32 size of RX jumbo ring + ``ETHTOOL_A_RINGS_TX`` u32 size of TX ring + ==================================== ====== ========================== + +Kernel checks that requested ring sizes do not exceed limits reported by +driver. Driver may impose additional constraints and may not suspport all +attributes. + + Request translation =================== @@ -700,7 +721,7 @@ have their netlink replacement yet. ``ETHTOOL_GCOALESCE`` n/a ``ETHTOOL_SCOALESCE`` n/a ``ETHTOOL_GRINGPARAM`` ``ETHTOOL_MSG_RINGS_GET`` - ``ETHTOOL_SRINGPARAM`` n/a + ``ETHTOOL_SRINGPARAM`` ``ETHTOOL_MSG_RINGS_SET`` ``ETHTOOL_GPAUSEPARAM`` n/a ``ETHTOOL_SPAUSEPARAM`` n/a ``ETHTOOL_GRXCSUM`` ``ETHTOOL_MSG_FEATURES_GET`` diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 7cd220f8cf73..ae71801b7aac 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -29,6 +29,7 @@ enum { ETHTOOL_MSG_PRIVFLAGS_GET, ETHTOOL_MSG_PRIVFLAGS_SET, ETHTOOL_MSG_RINGS_GET, + ETHTOOL_MSG_RINGS_SET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 0dc25a490450..6a1ac8897a7e 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -756,6 +756,11 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_RINGS_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_rings, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 01761932ed15..b30426d01890 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -347,5 +347,6 @@ int ethnl_set_debug(struct sk_buff *skb, struct genl_info *info); int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info); int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info); +int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info); #endif /* _NET_ETHTOOL_NETLINK_H */ diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c index d3129d8a252d..93f428e9a6c2 100644 --- a/net/ethtool/rings.c +++ b/net/ethtool/rings.c @@ -106,3 +106,92 @@ const struct ethnl_request_ops ethnl_rings_request_ops = { .reply_size = rings_reply_size, .fill_reply = rings_fill_reply, }; + +/* RINGS_SET */ + +static const struct nla_policy +rings_set_policy[ETHTOOL_A_RINGS_MAX + 1] = { + [ETHTOOL_A_RINGS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_RINGS_RX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX_MINI_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX_JUMBO_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_TX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_RINGS_RX] = { .type = NLA_U32 }, + [ETHTOOL_A_RINGS_RX_MINI] = { .type = NLA_U32 }, + [ETHTOOL_A_RINGS_RX_JUMBO] = { .type = NLA_U32 }, + [ETHTOOL_A_RINGS_TX] = { .type = NLA_U32 }, +}; + +int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[ETHTOOL_A_RINGS_MAX + 1]; + struct ethtool_ringparam ringparam = {}; + struct ethnl_req_info req_info = {}; + const struct nlattr *err_attr; + const struct ethtool_ops *ops; + struct net_device *dev; + bool mod = false; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_RINGS_MAX, rings_set_policy, + info->extack); + if (ret < 0) + return ret; + ret = ethnl_parse_header_dev_get(&req_info, + tb[ETHTOOL_A_RINGS_HEADER], + genl_info_net(info), info->extack, + true); + if (ret < 0) + return ret; + dev = req_info.dev; + ops = dev->ethtool_ops; + ret = -EOPNOTSUPP; + if (!ops->get_ringparam || !ops->set_ringparam) + goto out_dev; + + rtnl_lock(); + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto out_rtnl; + ops->get_ringparam(dev, &ringparam); + + ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); + ethnl_update_u32(&ringparam.rx_mini_pending, + tb[ETHTOOL_A_RINGS_RX_MINI], &mod); + ethnl_update_u32(&ringparam.rx_jumbo_pending, + tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod); + ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod); + ret = 0; + if (!mod) + goto out_ops; + + /* ensure new ring parameters are within limits */ + if (ringparam.rx_pending > ringparam.rx_max_pending) + err_attr = tb[ETHTOOL_A_RINGS_RX]; + else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending) + err_attr = tb[ETHTOOL_A_RINGS_RX_MINI]; + else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending) + err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO]; + else if (ringparam.tx_pending > ringparam.tx_max_pending) + err_attr = tb[ETHTOOL_A_RINGS_TX]; + else + err_attr = NULL; + if (err_attr) { + ret = -EINVAL; + NL_SET_ERR_MSG_ATTR(info->extack, err_attr, + "requested ring size exceeeds maximum"); + goto out_ops; + } + + ret = dev->ethtool_ops->set_ringparam(dev, &ringparam); + +out_ops: + ethnl_ops_complete(dev); +out_rtnl: + rtnl_unlock(); +out_dev: + dev_put(dev); + return ret; +} -- cgit v1.2.3 From bc9d1c995ecb8cccaac8ead2ed570544c2c885eb Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:33 +0100 Subject: ethtool: add RINGS_NTF notification Send ETHTOOL_MSG_RINGS_NTF notification whenever ring sizes of a network device are modified using ETHTOOL_MSG_RINGS_SET netlink message or ETHTOOL_SRINGPARAM ioctl request. Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 1 + include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/ioctl.c | 6 +++++- net/ethtool/netlink.c | 2 ++ net/ethtool/rings.c | 3 +++ 5 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index ba31ae8f1feb..026a5fd4a08b 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -216,6 +216,7 @@ Kernel to userspace: ``ETHTOOL_MSG_PRIVFLAGS_GET_REPLY`` private flags ``ETHTOOL_MSG_PRIVFLAGS_NTF`` private flags ``ETHTOOL_MSG_RINGS_GET_REPLY`` ring sizes + ``ETHTOOL_MSG_RINGS_NTF`` ring sizes ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index ae71801b7aac..abfc8fd626da 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -55,6 +55,7 @@ enum { ETHTOOL_MSG_PRIVFLAGS_GET_REPLY, ETHTOOL_MSG_PRIVFLAGS_NTF, ETHTOOL_MSG_RINGS_GET_REPLY, + ETHTOOL_MSG_RINGS_NTF, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 298822289496..1d5c1b6b81a4 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1635,6 +1635,7 @@ static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr) static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) { struct ethtool_ringparam ringparam, max = { .cmd = ETHTOOL_GRINGPARAM }; + int ret; if (!dev->ethtool_ops->set_ringparam || !dev->ethtool_ops->get_ringparam) return -EOPNOTSUPP; @@ -1651,7 +1652,10 @@ static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) ringparam.tx_pending > max.tx_max_pending) return -EINVAL; - return dev->ethtool_ops->set_ringparam(dev, &ringparam); + ret = dev->ethtool_ops->set_ringparam(dev, &ringparam); + if (!ret) + ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL); + return ret; } static noinline_for_stack int ethtool_get_channels(struct net_device *dev, diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 6a1ac8897a7e..653e009216cd 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -532,6 +532,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { [ETHTOOL_MSG_WOL_NTF] = ðnl_wol_request_ops, [ETHTOOL_MSG_FEATURES_NTF] = ðnl_features_request_ops, [ETHTOOL_MSG_PRIVFLAGS_NTF] = ðnl_privflags_request_ops, + [ETHTOOL_MSG_RINGS_NTF] = ðnl_rings_request_ops, }; /* default notification handler */ @@ -619,6 +620,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { [ETHTOOL_MSG_WOL_NTF] = ethnl_default_notify, [ETHTOOL_MSG_FEATURES_NTF] = ethnl_default_notify, [ETHTOOL_MSG_PRIVFLAGS_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_RINGS_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) diff --git a/net/ethtool/rings.c b/net/ethtool/rings.c index 93f428e9a6c2..c2ebf72be217 100644 --- a/net/ethtool/rings.c +++ b/net/ethtool/rings.c @@ -186,6 +186,9 @@ int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info) } ret = dev->ethtool_ops->set_ringparam(dev, &ringparam); + if (ret < 0) + goto out_ops; + ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL); out_ops: ethnl_ops_complete(dev); -- cgit v1.2.3 From 0c84979c951a85fcb5c77ac2480fd476e283a07c Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:38 +0100 Subject: ethtool: provide channel counts with CHANNELS_GET request Implement CHANNELS_GET request to get channel counts of a network device. These are traditionally available via ETHTOOL_GCHANNELS ioctl request. Omit attributes for channel types which are not supported by driver or device (zero reported for maximum). v2: (all suggested by Jakub Kicinski) - minor cleanup in channels_prepare_data() - more descriptive channels_reply_size() - omit attributes with zero max count Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 30 +++++++- include/uapi/linux/ethtool_netlink.h | 21 ++++++ net/ethtool/Makefile | 3 +- net/ethtool/channels.c | 108 +++++++++++++++++++++++++++ net/ethtool/netlink.c | 8 ++ net/ethtool/netlink.h | 1 + 6 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 net/ethtool/channels.c (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 026a5fd4a08b..decbbddfd8be 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -195,6 +195,7 @@ Userspace to kernel: ``ETHTOOL_MSG_PRIVFLAGS_SET`` set private flags ``ETHTOOL_MSG_RINGS_GET`` get ring sizes ``ETHTOOL_MSG_RINGS_SET`` set ring sizes + ``ETHTOOL_MSG_CHANNELS_GET`` get channel counts ===================================== ================================ Kernel to userspace: @@ -217,6 +218,7 @@ Kernel to userspace: ``ETHTOOL_MSG_PRIVFLAGS_NTF`` private flags ``ETHTOOL_MSG_RINGS_GET_REPLY`` ring sizes ``ETHTOOL_MSG_RINGS_NTF`` ring sizes + ``ETHTOOL_MSG_CHANNELS_GET_REPLY`` channel counts ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device @@ -695,6 +697,32 @@ driver. Driver may impose additional constraints and may not suspport all attributes. +CHANNELS_GET +============ + +Gets channel counts like ``ETHTOOL_GCHANNELS`` ioctl request. + +Request contents: + + ==================================== ====== ========================== + ``ETHTOOL_A_CHANNELS_HEADER`` nested request header + ==================================== ====== ========================== + +Kernel response contents: + + ===================================== ====== ========================== + ``ETHTOOL_A_CHANNELS_HEADER`` nested reply header + ``ETHTOOL_A_CHANNELS_RX_MAX`` u32 max receive channels + ``ETHTOOL_A_CHANNELS_TX_MAX`` u32 max transmit channels + ``ETHTOOL_A_CHANNELS_OTHER_MAX`` u32 max other channels + ``ETHTOOL_A_CHANNELS_COMBINED_MAX`` u32 max combined channels + ``ETHTOOL_A_CHANNELS_RX_COUNT`` u32 receive channel count + ``ETHTOOL_A_CHANNELS_TX_COUNT`` u32 transmit channel count + ``ETHTOOL_A_CHANNELS_OTHER_COUNT`` u32 other channel count + ``ETHTOOL_A_CHANNELS_COMBINED_COUNT`` u32 combined channel count + ===================================== ====== ========================== + + Request translation =================== @@ -765,7 +793,7 @@ have their netlink replacement yet. ``ETHTOOL_SRXFHINDIR`` n/a ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET`` - ``ETHTOOL_GCHANNELS`` n/a + ``ETHTOOL_GCHANNELS`` ``ETHTOOL_MSG_CHANNELS_GET`` ``ETHTOOL_SCHANNELS`` n/a ``ETHTOOL_SET_DUMP`` n/a ``ETHTOOL_GET_DUMP_FLAG`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index abfc8fd626da..2270eb115eca 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -30,6 +30,7 @@ enum { ETHTOOL_MSG_PRIVFLAGS_SET, ETHTOOL_MSG_RINGS_GET, ETHTOOL_MSG_RINGS_SET, + ETHTOOL_MSG_CHANNELS_GET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, @@ -56,6 +57,7 @@ enum { ETHTOOL_MSG_PRIVFLAGS_NTF, ETHTOOL_MSG_RINGS_GET_REPLY, ETHTOOL_MSG_RINGS_NTF, + ETHTOOL_MSG_CHANNELS_GET_REPLY, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, @@ -287,6 +289,25 @@ enum { ETHTOOL_A_RINGS_MAX = (__ETHTOOL_A_RINGS_CNT - 1) }; +/* CHANNELS */ + +enum { + ETHTOOL_A_CHANNELS_UNSPEC, + ETHTOOL_A_CHANNELS_HEADER, /* nest - _A_HEADER_* */ + ETHTOOL_A_CHANNELS_RX_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_TX_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_OTHER_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_COMBINED_MAX, /* u32 */ + ETHTOOL_A_CHANNELS_RX_COUNT, /* u32 */ + ETHTOOL_A_CHANNELS_TX_COUNT, /* u32 */ + ETHTOOL_A_CHANNELS_OTHER_COUNT, /* u32 */ + ETHTOOL_A_CHANNELS_COMBINED_COUNT, /* u32 */ + + /* add new constants above here */ + __ETHTOOL_A_CHANNELS_CNT, + ETHTOOL_A_CHANNELS_MAX = (__ETHTOOL_A_CHANNELS_CNT - 1) +}; + /* generic netlink info */ #define ETHTOOL_GENL_NAME "ethtool" #define ETHTOOL_GENL_VERSION 1 diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile index b48b70ef4ed0..b0bd3decad02 100644 --- a/net/ethtool/Makefile +++ b/net/ethtool/Makefile @@ -5,4 +5,5 @@ obj-y += ioctl.o common.o obj-$(CONFIG_ETHTOOL_NETLINK) += ethtool_nl.o ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ - linkstate.o debug.o wol.o features.o privflags.o rings.o + linkstate.o debug.o wol.o features.o privflags.o rings.o \ + channels.o diff --git a/net/ethtool/channels.c b/net/ethtool/channels.c new file mode 100644 index 000000000000..500dd5ad250b --- /dev/null +++ b/net/ethtool/channels.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "netlink.h" +#include "common.h" + +struct channels_req_info { + struct ethnl_req_info base; +}; + +struct channels_reply_data { + struct ethnl_reply_data base; + struct ethtool_channels channels; +}; + +#define CHANNELS_REPDATA(__reply_base) \ + container_of(__reply_base, struct channels_reply_data, base) + +static const struct nla_policy +channels_get_policy[ETHTOOL_A_CHANNELS_MAX + 1] = { + [ETHTOOL_A_CHANNELS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_CHANNELS_RX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_TX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_OTHER_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_COMBINED_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_RX_COUNT] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_TX_COUNT] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_OTHER_COUNT] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_COMBINED_COUNT] = { .type = NLA_REJECT }, +}; + +static int channels_prepare_data(const struct ethnl_req_info *req_base, + struct ethnl_reply_data *reply_base, + struct genl_info *info) +{ + struct channels_reply_data *data = CHANNELS_REPDATA(reply_base); + struct net_device *dev = reply_base->dev; + int ret; + + if (!dev->ethtool_ops->get_channels) + return -EOPNOTSUPP; + ret = ethnl_ops_begin(dev); + if (ret < 0) + return ret; + dev->ethtool_ops->get_channels(dev, &data->channels); + ethnl_ops_complete(dev); + + return 0; +} + +static int channels_reply_size(const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + return nla_total_size(sizeof(u32)) + /* _CHANNELS_RX_MAX */ + nla_total_size(sizeof(u32)) + /* _CHANNELS_TX_MAX */ + nla_total_size(sizeof(u32)) + /* _CHANNELS_OTHER_MAX */ + nla_total_size(sizeof(u32)) + /* _CHANNELS_COMBINED_MAX */ + nla_total_size(sizeof(u32)) + /* _CHANNELS_RX_COUNT */ + nla_total_size(sizeof(u32)) + /* _CHANNELS_TX_COUNT */ + nla_total_size(sizeof(u32)) + /* _CHANNELS_OTHER_COUNT */ + nla_total_size(sizeof(u32)); /* _CHANNELS_COMBINED_COUNT */ +} + +static int channels_fill_reply(struct sk_buff *skb, + const struct ethnl_req_info *req_base, + const struct ethnl_reply_data *reply_base) +{ + const struct channels_reply_data *data = CHANNELS_REPDATA(reply_base); + const struct ethtool_channels *channels = &data->channels; + + if ((channels->max_rx && + (nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_MAX, + channels->max_rx) || + nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_COUNT, + channels->rx_count))) || + (channels->max_tx && + (nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_MAX, + channels->max_tx) || + nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_COUNT, + channels->tx_count))) || + (channels->max_other && + (nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_MAX, + channels->max_other) || + nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_COUNT, + channels->other_count))) || + (channels->max_combined && + (nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_MAX, + channels->max_combined) || + nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_COUNT, + channels->combined_count)))) + return -EMSGSIZE; + + return 0; +} + +const struct ethnl_request_ops ethnl_channels_request_ops = { + .request_cmd = ETHTOOL_MSG_CHANNELS_GET, + .reply_cmd = ETHTOOL_MSG_CHANNELS_GET_REPLY, + .hdr_attr = ETHTOOL_A_CHANNELS_HEADER, + .max_attr = ETHTOOL_A_CHANNELS_MAX, + .req_info_size = sizeof(struct channels_req_info), + .reply_data_size = sizeof(struct channels_reply_data), + .request_policy = channels_get_policy, + + .prepare_data = channels_prepare_data, + .reply_size = channels_reply_size, + .fill_reply = channels_fill_reply, +}; diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index 653e009216cd..ac35513abeed 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -218,6 +218,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = { [ETHTOOL_MSG_FEATURES_GET] = ðnl_features_request_ops, [ETHTOOL_MSG_PRIVFLAGS_GET] = ðnl_privflags_request_ops, [ETHTOOL_MSG_RINGS_GET] = ðnl_rings_request_ops, + [ETHTOOL_MSG_CHANNELS_GET] = ðnl_channels_request_ops, }; static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) @@ -763,6 +764,13 @@ static const struct genl_ops ethtool_genl_ops[] = { .flags = GENL_UNS_ADMIN_PERM, .doit = ethnl_set_rings, }, + { + .cmd = ETHTOOL_MSG_CHANNELS_GET, + .doit = ethnl_default_doit, + .start = ethnl_default_start, + .dumpit = ethnl_default_dumpit, + .done = ethnl_default_done, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index b30426d01890..53bfe720ff1a 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -340,6 +340,7 @@ extern const struct ethnl_request_ops ethnl_wol_request_ops; extern const struct ethnl_request_ops ethnl_features_request_ops; extern const struct ethnl_request_ops ethnl_privflags_request_ops; extern const struct ethnl_request_ops ethnl_rings_request_ops; +extern const struct ethnl_request_ops ethnl_channels_request_ops; int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); -- cgit v1.2.3 From e19c591eafad4d723a2eed385e253eca0506cc25 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:43 +0100 Subject: ethtool: set device channel counts with CHANNELS_SET request Implement CHANNELS_SET netlink request to set channel counts of a network device. These are traditionally set with ETHTOOL_SCHANNELS ioctl request. Like the ioctl implementation, the generic ethtool code checks if supplied values do not exceed driver defined limits; if they do, first offending attribute is reported using extack. Checks preventing removing channels used for RX indirection table or zerocopy AF_XDP socket are also implemented. Move ethtool_get_max_rxfh_channel() helper into common.c so that it can be used by both ioctl and netlink code. v2: - fix netdev reference leak in error path (found by Jakub Kicinsky) Signed-off-by: Michal Kubecek Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 23 +++++- include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/channels.c | 116 +++++++++++++++++++++++++++ net/ethtool/common.c | 31 +++++++ net/ethtool/common.h | 1 + net/ethtool/ioctl.c | 31 ------- net/ethtool/netlink.c | 5 ++ net/ethtool/netlink.h | 1 + 8 files changed, 177 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index decbbddfd8be..7df7476cf310 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -196,6 +196,7 @@ Userspace to kernel: ``ETHTOOL_MSG_RINGS_GET`` get ring sizes ``ETHTOOL_MSG_RINGS_SET`` set ring sizes ``ETHTOOL_MSG_CHANNELS_GET`` get channel counts + ``ETHTOOL_MSG_CHANNELS_SET`` set channel counts ===================================== ================================ Kernel to userspace: @@ -723,6 +724,26 @@ Kernel response contents: ===================================== ====== ========================== +CHANNELS_SET +============ + +Sets channel counts like ``ETHTOOL_SCHANNELS`` ioctl request. + +Request contents: + + ===================================== ====== ========================== + ``ETHTOOL_A_CHANNELS_HEADER`` nested request header + ``ETHTOOL_A_CHANNELS_RX_COUNT`` u32 receive channel count + ``ETHTOOL_A_CHANNELS_TX_COUNT`` u32 transmit channel count + ``ETHTOOL_A_CHANNELS_OTHER_COUNT`` u32 other channel count + ``ETHTOOL_A_CHANNELS_COMBINED_COUNT`` u32 combined channel count + ===================================== ====== ========================== + +Kernel checks that requested channel counts do not exceed limits reported by +driver. Driver may impose additional constraints and may not suspport all +attributes. + + Request translation =================== @@ -794,7 +815,7 @@ have their netlink replacement yet. ``ETHTOOL_GFEATURES`` ``ETHTOOL_MSG_FEATURES_GET`` ``ETHTOOL_SFEATURES`` ``ETHTOOL_MSG_FEATURES_SET`` ``ETHTOOL_GCHANNELS`` ``ETHTOOL_MSG_CHANNELS_GET`` - ``ETHTOOL_SCHANNELS`` n/a + ``ETHTOOL_SCHANNELS`` ``ETHTOOL_MSG_CHANNELS_SET`` ``ETHTOOL_SET_DUMP`` n/a ``ETHTOOL_GET_DUMP_FLAG`` n/a ``ETHTOOL_GET_DUMP_DATA`` n/a diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index 2270eb115eca..f1384a8f3534 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -31,6 +31,7 @@ enum { ETHTOOL_MSG_RINGS_GET, ETHTOOL_MSG_RINGS_SET, ETHTOOL_MSG_CHANNELS_GET, + ETHTOOL_MSG_CHANNELS_SET, /* add new constants above here */ __ETHTOOL_MSG_USER_CNT, diff --git a/net/ethtool/channels.c b/net/ethtool/channels.c index 500dd5ad250b..ee232c11acae 100644 --- a/net/ethtool/channels.c +++ b/net/ethtool/channels.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only +#include + #include "netlink.h" #include "common.h" @@ -106,3 +108,117 @@ const struct ethnl_request_ops ethnl_channels_request_ops = { .reply_size = channels_reply_size, .fill_reply = channels_fill_reply, }; + +/* CHANNELS_SET */ + +static const struct nla_policy +channels_set_policy[ETHTOOL_A_CHANNELS_MAX + 1] = { + [ETHTOOL_A_CHANNELS_UNSPEC] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_HEADER] = { .type = NLA_NESTED }, + [ETHTOOL_A_CHANNELS_RX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_TX_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_OTHER_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_COMBINED_MAX] = { .type = NLA_REJECT }, + [ETHTOOL_A_CHANNELS_RX_COUNT] = { .type = NLA_U32 }, + [ETHTOOL_A_CHANNELS_TX_COUNT] = { .type = NLA_U32 }, + [ETHTOOL_A_CHANNELS_OTHER_COUNT] = { .type = NLA_U32 }, + [ETHTOOL_A_CHANNELS_COMBINED_COUNT] = { .type = NLA_U32 }, +}; + +int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *tb[ETHTOOL_A_CHANNELS_MAX + 1]; + unsigned int from_channel, old_total, i; + struct ethtool_channels channels = {}; + struct ethnl_req_info req_info = {}; + const struct nlattr *err_attr; + const struct ethtool_ops *ops; + struct net_device *dev; + u32 max_rx_in_use = 0; + bool mod = false; + int ret; + + ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, + ETHTOOL_A_CHANNELS_MAX, channels_set_policy, + info->extack); + if (ret < 0) + return ret; + ret = ethnl_parse_header_dev_get(&req_info, + tb[ETHTOOL_A_CHANNELS_HEADER], + genl_info_net(info), info->extack, + true); + if (ret < 0) + return ret; + dev = req_info.dev; + ops = dev->ethtool_ops; + ret = -EOPNOTSUPP; + if (!ops->get_channels || !ops->set_channels) + goto out_dev; + + rtnl_lock(); + ret = ethnl_ops_begin(dev); + if (ret < 0) + goto out_rtnl; + ops->get_channels(dev, &channels); + old_total = channels.combined_count + + max(channels.rx_count, channels.tx_count); + + ethnl_update_u32(&channels.rx_count, tb[ETHTOOL_A_CHANNELS_RX_COUNT], + &mod); + ethnl_update_u32(&channels.tx_count, tb[ETHTOOL_A_CHANNELS_TX_COUNT], + &mod); + ethnl_update_u32(&channels.other_count, + tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], &mod); + ethnl_update_u32(&channels.combined_count, + tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], &mod); + ret = 0; + if (!mod) + goto out_ops; + + /* ensure new channel counts are within limits */ + if (channels.rx_count > channels.max_rx) + err_attr = tb[ETHTOOL_A_CHANNELS_RX_COUNT]; + else if (channels.tx_count > channels.max_tx) + err_attr = tb[ETHTOOL_A_CHANNELS_TX_COUNT]; + else if (channels.other_count > channels.max_other) + err_attr = tb[ETHTOOL_A_CHANNELS_OTHER_COUNT]; + else if (channels.combined_count > channels.max_combined) + err_attr = tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT]; + else + err_attr = NULL; + if (err_attr) { + ret = -EINVAL; + NL_SET_ERR_MSG_ATTR(info->extack, err_attr, + "requested channel count exceeeds maximum"); + goto out_ops; + } + + /* ensure the new Rx count fits within the configured Rx flow + * indirection table settings + */ + if (netif_is_rxfh_configured(dev) && + !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) && + (channels.combined_count + channels.rx_count) <= max_rx_in_use) { + GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings"); + return -EINVAL; + } + + /* Disabling channels, query zero-copy AF_XDP sockets */ + from_channel = channels.combined_count + + min(channels.rx_count, channels.tx_count); + for (i = from_channel; i < old_total; i++) + if (xdp_get_umem_from_qid(dev, i)) { + GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing zerocopy AF_XDP sockets"); + return -EINVAL; + } + + ret = dev->ethtool_ops->set_channels(dev, &channels); + +out_ops: + ethnl_ops_complete(dev); +out_rtnl: + rtnl_unlock(); +out_dev: + dev_put(dev); + return ret; +} diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 7b6969af5ae7..0b22741b2f8f 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -258,3 +258,34 @@ int __ethtool_get_link(struct net_device *dev) return netif_running(dev) && dev->ethtool_ops->get_link(dev); } + +int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max) +{ + u32 dev_size, current_max = 0; + u32 *indir; + int ret; + + if (!dev->ethtool_ops->get_rxfh_indir_size || + !dev->ethtool_ops->get_rxfh) + return -EOPNOTSUPP; + dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); + if (dev_size == 0) + return -EOPNOTSUPP; + + indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); + if (!indir) + return -ENOMEM; + + ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL); + if (ret) + goto out; + + while (dev_size--) + current_max = max(current_max, indir[dev_size]); + + *max = current_max; + +out: + kfree(indir); + return ret; +} diff --git a/net/ethtool/common.h b/net/ethtool/common.h index 7dc1163800a7..03946e16e623 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -29,5 +29,6 @@ int __ethtool_get_link(struct net_device *dev); bool convert_legacy_settings_to_link_ksettings( struct ethtool_link_ksettings *link_ksettings, const struct ethtool_cmd *legacy_settings); +int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max); #endif /* _ETHTOOL_COMMON_H */ diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 1d5c1b6b81a4..06224a03139e 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -929,37 +929,6 @@ void netdev_rss_key_fill(void *buffer, size_t len) } EXPORT_SYMBOL(netdev_rss_key_fill); -static int ethtool_get_max_rxfh_channel(struct net_device *dev, u32 *max) -{ - u32 dev_size, current_max = 0; - u32 *indir; - int ret; - - if (!dev->ethtool_ops->get_rxfh_indir_size || - !dev->ethtool_ops->get_rxfh) - return -EOPNOTSUPP; - dev_size = dev->ethtool_ops->get_rxfh_indir_size(dev); - if (dev_size == 0) - return -EOPNOTSUPP; - - indir = kcalloc(dev_size, sizeof(indir[0]), GFP_USER); - if (!indir) - return -ENOMEM; - - ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL); - if (ret) - goto out; - - while (dev_size--) - current_max = max(current_max, indir[dev_size]); - - *max = current_max; - -out: - kfree(indir); - return ret; -} - static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev, void __user *useraddr) { diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index ac35513abeed..f61654b8f210 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -771,6 +771,11 @@ static const struct genl_ops ethtool_genl_ops[] = { .dumpit = ethnl_default_dumpit, .done = ethnl_default_done, }, + { + .cmd = ETHTOOL_MSG_CHANNELS_SET, + .flags = GENL_UNS_ADMIN_PERM, + .doit = ethnl_set_channels, + }, }; static const struct genl_multicast_group ethtool_nl_mcgrps[] = { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 53bfe720ff1a..45aad99a6021 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -349,5 +349,6 @@ int ethnl_set_wol(struct sk_buff *skb, struct genl_info *info); int ethnl_set_features(struct sk_buff *skb, struct genl_info *info); int ethnl_set_privflags(struct sk_buff *skb, struct genl_info *info); int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info); +int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info); #endif /* _NET_ETHTOOL_NETLINK_H */ -- cgit v1.2.3 From 546379b9a01b6bdb5b267f8e7433944d5364cbf2 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Thu, 12 Mar 2020 21:08:48 +0100 Subject: ethtool: add CHANNELS_NTF notification Send ETHTOOL_MSG_CHANNELS_NTF notification whenever channel counts of a network device are modified using ETHTOOL_MSG_CHANNELS_SET netlink message or ETHTOOL_SCHANNELS ioctl request. Signed-off-by: Michal Kubecek Signed-off-by: David S. Miller --- Documentation/networking/ethtool-netlink.rst | 1 + include/uapi/linux/ethtool_netlink.h | 1 + net/ethtool/channels.c | 3 +++ net/ethtool/ioctl.c | 6 +++++- net/ethtool/netlink.c | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index 7df7476cf310..31a601cafa3f 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -220,6 +220,7 @@ Kernel to userspace: ``ETHTOOL_MSG_RINGS_GET_REPLY`` ring sizes ``ETHTOOL_MSG_RINGS_NTF`` ring sizes ``ETHTOOL_MSG_CHANNELS_GET_REPLY`` channel counts + ``ETHTOOL_MSG_CHANNELS_NTF`` channel counts ===================================== ================================= ``GET`` requests are sent by userspace applications to retrieve device diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index f1384a8f3534..c7c7a1a550af 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -59,6 +59,7 @@ enum { ETHTOOL_MSG_RINGS_GET_REPLY, ETHTOOL_MSG_RINGS_NTF, ETHTOOL_MSG_CHANNELS_GET_REPLY, + ETHTOOL_MSG_CHANNELS_NTF, /* add new constants above here */ __ETHTOOL_MSG_KERNEL_CNT, diff --git a/net/ethtool/channels.c b/net/ethtool/channels.c index ee232c11acae..8dc5485333a4 100644 --- a/net/ethtool/channels.c +++ b/net/ethtool/channels.c @@ -213,6 +213,9 @@ int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info) } ret = dev->ethtool_ops->set_channels(dev, &channels); + if (ret < 0) + goto out_ops; + ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL); out_ops: ethnl_ops_complete(dev); diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 06224a03139e..258840b19fb5 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1649,6 +1649,7 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev, u16 from_channel, to_channel; u32 max_rx_in_use = 0; unsigned int i; + int ret; if (!dev->ethtool_ops->set_channels || !dev->ethtool_ops->get_channels) return -EOPNOTSUPP; @@ -1680,7 +1681,10 @@ static noinline_for_stack int ethtool_set_channels(struct net_device *dev, if (xdp_get_umem_from_qid(dev, i)) return -EINVAL; - return dev->ethtool_ops->set_channels(dev, &channels); + ret = dev->ethtool_ops->set_channels(dev, &channels); + if (!ret) + ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL); + return ret; } static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr) diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c index f61654b8f210..55c8ce4019d9 100644 --- a/net/ethtool/netlink.c +++ b/net/ethtool/netlink.c @@ -534,6 +534,7 @@ ethnl_default_notify_ops[ETHTOOL_MSG_KERNEL_MAX + 1] = { [ETHTOOL_MSG_FEATURES_NTF] = ðnl_features_request_ops, [ETHTOOL_MSG_PRIVFLAGS_NTF] = ðnl_privflags_request_ops, [ETHTOOL_MSG_RINGS_NTF] = ðnl_rings_request_ops, + [ETHTOOL_MSG_CHANNELS_NTF] = ðnl_channels_request_ops, }; /* default notification handler */ @@ -622,6 +623,7 @@ static const ethnl_notify_handler_t ethnl_notify_handlers[] = { [ETHTOOL_MSG_FEATURES_NTF] = ethnl_default_notify, [ETHTOOL_MSG_PRIVFLAGS_NTF] = ethnl_default_notify, [ETHTOOL_MSG_RINGS_NTF] = ethnl_default_notify, + [ETHTOOL_MSG_CHANNELS_NTF] = ethnl_default_notify, }; void ethtool_notify(struct net_device *dev, unsigned int cmd, const void *data) -- cgit v1.2.3 From 611d779af7cad2b87487ff58e4931a90c20b113c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 12 Mar 2020 22:25:20 +0100 Subject: net: phy: fix MDIO bus PM PHY resuming So far we have the unfortunate situation that mdio_bus_phy_may_suspend() is called in suspend AND resume path, assuming that function result is the same. After the original change this is no longer the case, resulting in broken resume as reported by Geert. To fix this call mdio_bus_phy_may_suspend() in the suspend path only, and let the phy_device store the info whether it was suspended by MDIO bus PM. Fixes: 503ba7c69610 ("net: phy: Avoid multiple suspends") Reported-by: Geert Uytterhoeven Tested-by: Geert Uytterhoeven Signed-off-by: Heiner Kallweit Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/phy/phy_device.c | 6 +++++- include/linux/phy.h | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c8b0c34030d3..28e3c5c0e3c3 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -286,6 +286,8 @@ static int mdio_bus_phy_suspend(struct device *dev) if (!mdio_bus_phy_may_suspend(phydev)) return 0; + phydev->suspended_by_mdio_bus = 1; + return phy_suspend(phydev); } @@ -294,9 +296,11 @@ static int mdio_bus_phy_resume(struct device *dev) struct phy_device *phydev = to_phy_device(dev); int ret; - if (!mdio_bus_phy_may_suspend(phydev)) + if (!phydev->suspended_by_mdio_bus) goto no_resume; + phydev->suspended_by_mdio_bus = 0; + ret = phy_resume(phydev); if (ret < 0) return ret; diff --git a/include/linux/phy.h b/include/linux/phy.h index 22f5e763e894..452e8ba8665f 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -357,6 +357,7 @@ struct macsec_ops; * is_gigabit_capable: Set to true if PHY supports 1000Mbps * has_fixups: Set to true if this phy has fixups/quirks. * suspended: Set to true if this phy has been suspended successfully. + * suspended_by_mdio_bus: Set to true if this phy was suspended by MDIO bus. * sysfs_links: Internal boolean tracking sysfs symbolic links setup/removal. * loopback_enabled: Set true if this phy has been loopbacked successfully. * state: state of the PHY for management purposes @@ -396,6 +397,7 @@ struct phy_device { unsigned is_gigabit_capable:1; unsigned has_fixups:1; unsigned suspended:1; + unsigned suspended_by_mdio_bus:1; unsigned sysfs_links:1; unsigned loopback_enabled:1; -- cgit v1.2.3 From e31a50162feb352147d3fc87b9e036703c8f2636 Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Thu, 12 Mar 2020 11:44:27 -0500 Subject: bitfield.h: add FIELD_MAX() and field_max() Define FIELD_MAX(), which supplies the maximum value that can be represented by a field value. Define field_max() as well, to go along with the lower-case forms of the field mask functions. Signed-off-by: Alex Elder Acked-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/linux/bitfield.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/linux/bitfield.h b/include/linux/bitfield.h index 4bbb5f1c8b5b..48ea093ff04c 100644 --- a/include/linux/bitfield.h +++ b/include/linux/bitfield.h @@ -55,6 +55,19 @@ (1ULL << __bf_shf(_mask))); \ }) +/** + * FIELD_MAX() - produce the maximum value representable by a field + * @_mask: shifted mask defining the field's length and position + * + * FIELD_MAX() returns the maximum value that can be held in the field + * specified by @_mask. + */ +#define FIELD_MAX(_mask) \ + ({ \ + __BF_FIELD_CHECK(_mask, 0ULL, 0ULL, "FIELD_MAX: "); \ + (typeof(_mask))((_mask) >> __bf_shf(_mask)); \ + }) + /** * FIELD_FIT() - check if value fits in the field * @_mask: shifted mask defining the field's length and position @@ -110,6 +123,7 @@ static __always_inline u64 field_mask(u64 field) { return field / field_multiplier(field); } +#define field_max(field) ((typeof(field))field_mask(field)) #define ____MAKE_OP(type,base,to,from) \ static __always_inline __##type type##_encode_bits(base v, base field) \ { \ -- cgit v1.2.3 From fcf4638075964268bf8a0e212407096c6aab6fd3 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 6 Mar 2020 18:46:20 -0500 Subject: drm/dp_mst: Use full_pbn instead of available_pbn for bandwidth checks DisplayPort specifications are fun. For a while, it's been really unclear to us what available_pbn actually does. There's a somewhat vague explanation in the DisplayPort spec (starting from 1.2) that partially explains it: The minimum payload bandwidth number supported by the path. Each node updates this number with its available payload bandwidth number if its payload bandwidth number is less than that in the Message Transaction reply. So, it sounds like available_pbn represents the smallest link rate in use between the source and the branch device. Cool, so full_pbn is just the highest possible PBN that the branch device supports right? Well, we assumed that for quite a while until Sean Paul noticed that on some MST hubs, available_pbn will actually get set to 0 whenever there's any active payloads on the respective branch device. This caused quite a bit of confusion since clearing the payload ID table would end up fixing the available_pbn value. So, we just went with that until commit cd82d82cbc04 ("drm/dp_mst: Add branch bandwidth validation to MST atomic check") started breaking people's setups due to us getting erroneous available_pbn values. So, we did some more digging and got confused until we finally looked at the definition for full_pbn: The bandwidth of the link at the trained link rate and lane count between the DP Source device and the DP Sink device with no time slots allocated to VC Payloads, represented as a Payload Bandwidth Number. As with the Available_Payload_Bandwidth_Number, this number is determined by the link with the lowest lane count and link rate. That's what we get for not reading specs closely enough, hehe. So, since full_pbn is definitely what we want for doing bandwidth restriction checks - let's start using that instead and ignore available_pbn entirely. Signed-off-by: Lyude Paul Fixes: cd82d82cbc04 ("drm/dp_mst: Add branch bandwidth validation to MST atomic check") Cc: Mikita Lipski Cc: Hans de Goede Cc: Sean Paul Reviewed-by: Mikita Lipski Link: https://patchwork.freedesktop.org/patch/msgid/20200306234623.547525-3-lyude@redhat.com Reviewed-by: Alex Deucher Tested-by: Hans de Goede --- drivers/gpu/drm/drm_dp_mst_topology.c | 15 +++++++-------- include/drm/drm_dp_mst_helper.h | 4 ++-- 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 9188c53f5c96..7df7676b45c4 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2309,7 +2309,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb, port); } } else { - port->available_pbn = 0; + port->full_pbn = 0; } } @@ -2404,7 +2404,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb, if (port->ddps) { dowork = true; } else { - port->available_pbn = 0; + port->full_pbn = 0; } } @@ -2556,7 +2556,7 @@ static int drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mg if (port->input || !port->ddps) continue; - if (!port->available_pbn) { + if (!port->full_pbn) { drm_modeset_lock(&mgr->base.lock, NULL); drm_dp_send_enum_path_resources(mgr, mstb, port); drm_modeset_unlock(&mgr->base.lock); @@ -3002,8 +3002,7 @@ drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, path_res->port_number, path_res->full_payload_bw_number, path_res->avail_payload_bw_number); - port->available_pbn = - path_res->avail_payload_bw_number; + port->full_pbn = path_res->full_payload_bw_number; port->fec_capable = path_res->fec_capable; } } @@ -3598,7 +3597,7 @@ drm_dp_mst_topology_mgr_invalidate_mstb(struct drm_dp_mst_branch *mstb) list_for_each_entry(port, &mstb->ports, next) { /* The PBN for each port will also need to be re-probed */ - port->available_pbn = 0; + port->full_pbn = 0; if (port->mstb) drm_dp_mst_topology_mgr_invalidate_mstb(port->mstb); @@ -4842,8 +4841,8 @@ int drm_dp_mst_atomic_check_bw_limit(struct drm_dp_mst_branch *branch, if (drm_dp_mst_atomic_check_bw_limit(port->mstb, mst_state)) return -ENOSPC; - if (port->available_pbn > 0) - pbn_limit = port->available_pbn; + if (port->full_pbn > 0) + pbn_limit = port->full_pbn; } DRM_DEBUG_ATOMIC("[MST BRANCH:%p] branch has %d PBN available\n", branch, pbn_limit); diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index bcb39da9adb4..41725d88d27e 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -81,7 +81,7 @@ struct drm_dp_vcpi { * &drm_dp_mst_topology_mgr.base.lock. * @num_sdp_stream_sinks: Number of stream sinks. Protected by * &drm_dp_mst_topology_mgr.base.lock. - * @available_pbn: Available bandwidth for this port. Protected by + * @full_pbn: Max possible bandwidth for this port. Protected by * &drm_dp_mst_topology_mgr.base.lock. * @next: link to next port on this branch device * @aux: i2c aux transport to talk to device connected to this port, protected @@ -126,7 +126,7 @@ struct drm_dp_mst_port { u8 dpcd_rev; u8 num_sdp_streams; u8 num_sdp_stream_sinks; - uint16_t available_pbn; + uint16_t full_pbn; struct list_head next; /** * @mstb: the branch device connected to this port, if there is one. -- cgit v1.2.3 From 1e2328e762548c7d17b7ba8ded9f409d05710dd1 Mon Sep 17 00:00:00 2001 From: Carlos Neira Date: Wed, 4 Mar 2020 17:41:55 -0300 Subject: fs/nsfs.c: Added ns_match ns_match returns true if the namespace inode and dev_t matches the ones provided by the caller. Signed-off-by: Carlos Neira Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200304204157.58695-2-cneirabustos@gmail.com --- fs/nsfs.c | 14 ++++++++++++++ include/linux/proc_ns.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/fs/nsfs.c b/fs/nsfs.c index b13bfd406820..4f1205725cfe 100644 --- a/fs/nsfs.c +++ b/fs/nsfs.c @@ -247,6 +247,20 @@ out_invalid: return ERR_PTR(-EINVAL); } +/** + * ns_match() - Returns true if current namespace matches dev/ino provided. + * @ns_common: current ns + * @dev: dev_t from nsfs that will be matched against current nsfs + * @ino: ino_t from nsfs that will be matched against current nsfs + * + * Return: true if dev and ino matches the current nsfs. + */ +bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino) +{ + return (ns->inum == ino) && (nsfs_mnt->mnt_sb->s_dev == dev); +} + + static int nsfs_show_path(struct seq_file *seq, struct dentry *dentry) { struct inode *inode = d_inode(dentry); diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h index 4626b1ac3b6c..adff08bfecf9 100644 --- a/include/linux/proc_ns.h +++ b/include/linux/proc_ns.h @@ -85,6 +85,8 @@ typedef struct ns_common *ns_get_path_helper_t(void *); extern int ns_get_path_cb(struct path *path, ns_get_path_helper_t ns_get_cb, void *private_data); +extern bool ns_match(const struct ns_common *ns, dev_t dev, ino_t ino); + extern int ns_get_name(char *buf, size_t size, struct task_struct *task, const struct proc_ns_operations *ns_ops); extern void nsfs_init(void); -- cgit v1.2.3 From b4490c5c4e023f09b7d27c9a9d3e7ad7d09ea6bf Mon Sep 17 00:00:00 2001 From: Carlos Neira Date: Wed, 4 Mar 2020 17:41:56 -0300 Subject: bpf: Added new helper bpf_get_ns_current_pid_tgid New bpf helper bpf_get_ns_current_pid_tgid, This helper will return pid and tgid from current task which namespace matches dev_t and inode number provided, this will allows us to instrument a process inside a container. Signed-off-by: Carlos Neira Signed-off-by: Alexei Starovoitov Acked-by: Yonghong Song Link: https://lore.kernel.org/bpf/20200304204157.58695-3-cneirabustos@gmail.com --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 20 ++++++++++++++++++- kernel/bpf/core.c | 1 + kernel/bpf/helpers.c | 45 ++++++++++++++++++++++++++++++++++++++++++ kernel/trace/bpf_trace.c | 2 ++ scripts/bpf_helpers_doc.py | 1 + tools/include/uapi/linux/bpf.h | 20 ++++++++++++++++++- 7 files changed, 88 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4fd91b7c95ea..4ec835334a1f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1497,6 +1497,7 @@ extern const struct bpf_func_proto bpf_strtol_proto; extern const struct bpf_func_proto bpf_strtoul_proto; extern const struct bpf_func_proto bpf_tcp_sock_proto; extern const struct bpf_func_proto bpf_jiffies64_proto; +extern const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto; /* Shared helpers among cBPF and eBPF. */ void bpf_user_rnd_init_once(void); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 40b2d9476268..15b239da775b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2914,6 +2914,19 @@ union bpf_attr { * of sizeof(struct perf_branch_entry). * * **-ENOENT** if architecture does not support branch records. + * + * int bpf_get_ns_current_pid_tgid(u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size) + * Description + * Returns 0 on success, values for *pid* and *tgid* as seen from the current + * *namespace* will be returned in *nsdata*. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if dev and inum supplied don't match dev_t and inode number + * with nsfs of current task, or if dev conversion to dev_t lost high bits. + * + * **-ENOENT** if pidns does not exists for the current task. + * */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3035,7 +3048,8 @@ union bpf_attr { FN(tcp_send_ack), \ FN(send_signal_thread), \ FN(jiffies64), \ - FN(read_branch_records), + FN(read_branch_records), \ + FN(get_ns_current_pid_tgid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -3829,4 +3843,8 @@ struct bpf_sockopt { __s32 retval; }; +struct bpf_pidns_info { + __u32 pid; + __u32 tgid; +}; #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 973a20d49749..0f9ca46e1978 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -2149,6 +2149,7 @@ const struct bpf_func_proto bpf_get_current_uid_gid_proto __weak; const struct bpf_func_proto bpf_get_current_comm_proto __weak; const struct bpf_func_proto bpf_get_current_cgroup_id_proto __weak; const struct bpf_func_proto bpf_get_local_storage_proto __weak; +const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto __weak; const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void) { diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index d8b7b110a1c5..01878db15eaf 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "../../lib/kstrtox.h" @@ -499,3 +501,46 @@ const struct bpf_func_proto bpf_strtoul_proto = { .arg4_type = ARG_PTR_TO_LONG, }; #endif + +BPF_CALL_4(bpf_get_ns_current_pid_tgid, u64, dev, u64, ino, + struct bpf_pidns_info *, nsdata, u32, size) +{ + struct task_struct *task = current; + struct pid_namespace *pidns; + int err = -EINVAL; + + if (unlikely(size != sizeof(struct bpf_pidns_info))) + goto clear; + + if (unlikely((u64)(dev_t)dev != dev)) + goto clear; + + if (unlikely(!task)) + goto clear; + + pidns = task_active_pid_ns(task); + if (unlikely(!pidns)) { + err = -ENOENT; + goto clear; + } + + if (!ns_match(&pidns->ns, (dev_t)dev, ino)) + goto clear; + + nsdata->pid = task_pid_nr_ns(task, pidns); + nsdata->tgid = task_tgid_nr_ns(task, pidns); + return 0; +clear: + memset((void *)nsdata, 0, (size_t) size); + return err; +} + +const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto = { + .func = bpf_get_ns_current_pid_tgid, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_ANYTHING, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_PTR_TO_UNINIT_MEM, + .arg4_type = ARG_CONST_SIZE, +}; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 6a490d8ce9de..b5071c7e93ca 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -843,6 +843,8 @@ tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) return &bpf_send_signal_thread_proto; case BPF_FUNC_perf_event_read_value: return &bpf_perf_event_read_value_proto; + case BPF_FUNC_get_ns_current_pid_tgid: + return &bpf_get_ns_current_pid_tgid_proto; default: return NULL; } diff --git a/scripts/bpf_helpers_doc.py b/scripts/bpf_helpers_doc.py index cebed6fb5bbb..c1e2b5410faa 100755 --- a/scripts/bpf_helpers_doc.py +++ b/scripts/bpf_helpers_doc.py @@ -435,6 +435,7 @@ class PrinterHelpers(Printer): 'struct bpf_fib_lookup', 'struct bpf_perf_event_data', 'struct bpf_perf_event_value', + 'struct bpf_pidns_info', 'struct bpf_sock', 'struct bpf_sock_addr', 'struct bpf_sock_ops', diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 40b2d9476268..15b239da775b 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2914,6 +2914,19 @@ union bpf_attr { * of sizeof(struct perf_branch_entry). * * **-ENOENT** if architecture does not support branch records. + * + * int bpf_get_ns_current_pid_tgid(u64 dev, u64 ino, struct bpf_pidns_info *nsdata, u32 size) + * Description + * Returns 0 on success, values for *pid* and *tgid* as seen from the current + * *namespace* will be returned in *nsdata*. + * + * On failure, the returned value is one of the following: + * + * **-EINVAL** if dev and inum supplied don't match dev_t and inode number + * with nsfs of current task, or if dev conversion to dev_t lost high bits. + * + * **-ENOENT** if pidns does not exists for the current task. + * */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3035,7 +3048,8 @@ union bpf_attr { FN(tcp_send_ack), \ FN(send_signal_thread), \ FN(jiffies64), \ - FN(read_branch_records), + FN(read_branch_records), \ + FN(get_ns_current_pid_tgid), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -3829,4 +3843,8 @@ struct bpf_sockopt { __s32 retval; }; +struct bpf_pidns_info { + __u32 pid; + __u32 tgid; +}; #endif /* _UAPI__LINUX_BPF_H__ */ -- cgit v1.2.3 From d831ee84bfc9173eecf30dbbc2553ae81b996c60 Mon Sep 17 00:00:00 2001 From: Eelco Chaudron Date: Fri, 6 Mar 2020 08:59:23 +0000 Subject: bpf: Add bpf_xdp_output() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce new helper that reuses existing xdp perf_event output implementation, but can be called from raw_tracepoint programs that receive 'struct xdp_buff *' as a tracepoint argument. Signed-off-by: Eelco Chaudron Signed-off-by: Alexei Starovoitov Acked-by: John Fastabend Acked-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/bpf/158348514556.2239.11050972434793741444.stgit@xdp-tutorial --- include/uapi/linux/bpf.h | 26 ++++++++++- kernel/bpf/verifier.c | 4 +- kernel/trace/bpf_trace.c | 3 ++ net/core/filter.c | 16 ++++++- tools/include/uapi/linux/bpf.h | 26 ++++++++++- .../testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c | 53 ++++++++++++++++++++++ .../testing/selftests/bpf/progs/test_xdp_bpf2bpf.c | 24 ++++++++++ 7 files changed, 148 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 15b239da775b..5d01c5c7e598 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -2927,6 +2927,29 @@ union bpf_attr { * * **-ENOENT** if pidns does not exists for the current task. * + * int bpf_xdp_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) + * Description + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct xdp_buff. + * + * This helper is similar to **bpf_perf_eventoutput**\ () but + * restricted to raw_tracepoint bpf programs. + * Return + * 0 on success, or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3049,7 +3072,8 @@ union bpf_attr { FN(send_signal_thread), \ FN(jiffies64), \ FN(read_branch_records), \ - FN(get_ns_current_pid_tgid), + FN(get_ns_current_pid_tgid), \ + FN(xdp_output), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 55d376c53f7d..745f3cfdf3b2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3650,7 +3650,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, if (func_id != BPF_FUNC_perf_event_read && func_id != BPF_FUNC_perf_event_output && func_id != BPF_FUNC_skb_output && - func_id != BPF_FUNC_perf_event_read_value) + func_id != BPF_FUNC_perf_event_read_value && + func_id != BPF_FUNC_xdp_output) goto error; break; case BPF_MAP_TYPE_STACK_TRACE: @@ -3740,6 +3741,7 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, case BPF_FUNC_perf_event_output: case BPF_FUNC_perf_event_read_value: case BPF_FUNC_skb_output: + case BPF_FUNC_xdp_output: if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) goto error; break; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index b5071c7e93ca..e619eedb5919 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -1145,6 +1145,7 @@ static const struct bpf_func_proto bpf_perf_event_output_proto_raw_tp = { }; extern const struct bpf_func_proto bpf_skb_output_proto; +extern const struct bpf_func_proto bpf_xdp_output_proto; BPF_CALL_3(bpf_get_stackid_raw_tp, struct bpf_raw_tracepoint_args *, args, struct bpf_map *, map, u64, flags) @@ -1220,6 +1221,8 @@ tracing_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog) #ifdef CONFIG_NET case BPF_FUNC_skb_output: return &bpf_skb_output_proto; + case BPF_FUNC_xdp_output: + return &bpf_xdp_output_proto; #endif default: return raw_tp_prog_func_proto(func_id, prog); diff --git a/net/core/filter.c b/net/core/filter.c index cd0a532db4e7..22219544410f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4061,7 +4061,8 @@ BPF_CALL_5(bpf_xdp_event_output, struct xdp_buff *, xdp, struct bpf_map *, map, if (unlikely(flags & ~(BPF_F_CTXLEN_MASK | BPF_F_INDEX_MASK))) return -EINVAL; - if (unlikely(xdp_size > (unsigned long)(xdp->data_end - xdp->data))) + if (unlikely(!xdp || + xdp_size > (unsigned long)(xdp->data_end - xdp->data))) return -EFAULT; return bpf_event_output(map, flags, meta, meta_size, xdp->data, @@ -4079,6 +4080,19 @@ static const struct bpf_func_proto bpf_xdp_event_output_proto = { .arg5_type = ARG_CONST_SIZE_OR_ZERO, }; +static int bpf_xdp_output_btf_ids[5]; +const struct bpf_func_proto bpf_xdp_output_proto = { + .func = bpf_xdp_event_output, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_BTF_ID, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_MEM, + .arg5_type = ARG_CONST_SIZE_OR_ZERO, + .btf_id = bpf_xdp_output_btf_ids, +}; + BPF_CALL_1(bpf_get_socket_cookie, struct sk_buff *, skb) { return skb->sk ? sock_gen_cookie(skb->sk) : 0; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 15b239da775b..5d01c5c7e598 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -2927,6 +2927,29 @@ union bpf_attr { * * **-ENOENT** if pidns does not exists for the current task. * + * int bpf_xdp_output(void *ctx, struct bpf_map *map, u64 flags, void *data, u64 size) + * Description + * Write raw *data* blob into a special BPF perf event held by + * *map* of type **BPF_MAP_TYPE_PERF_EVENT_ARRAY**. This perf + * event must have the following attributes: **PERF_SAMPLE_RAW** + * as **sample_type**, **PERF_TYPE_SOFTWARE** as **type**, and + * **PERF_COUNT_SW_BPF_OUTPUT** as **config**. + * + * The *flags* are used to indicate the index in *map* for which + * the value must be put, masked with **BPF_F_INDEX_MASK**. + * Alternatively, *flags* can be set to **BPF_F_CURRENT_CPU** + * to indicate that the index of the current CPU core should be + * used. + * + * The value to write, of *size*, is passed through eBPF stack and + * pointed by *data*. + * + * *ctx* is a pointer to in-kernel struct xdp_buff. + * + * This helper is similar to **bpf_perf_eventoutput**\ () but + * restricted to raw_tracepoint bpf programs. + * Return + * 0 on success, or a negative error in case of failure. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3049,7 +3072,8 @@ union bpf_attr { FN(send_signal_thread), \ FN(jiffies64), \ FN(read_branch_records), \ - FN(get_ns_current_pid_tgid), + FN(get_ns_current_pid_tgid), \ + FN(xdp_output), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c b/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c index 4ba011031d4c..a0f688c37023 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_bpf2bpf.c @@ -4,17 +4,51 @@ #include "test_xdp.skel.h" #include "test_xdp_bpf2bpf.skel.h" +struct meta { + int ifindex; + int pkt_len; +}; + +static void on_sample(void *ctx, int cpu, void *data, __u32 size) +{ + int duration = 0; + struct meta *meta = (struct meta *)data; + struct ipv4_packet *trace_pkt_v4 = data + sizeof(*meta); + + if (CHECK(size < sizeof(pkt_v4) + sizeof(*meta), + "check_size", "size %u < %zu\n", + size, sizeof(pkt_v4) + sizeof(*meta))) + return; + + if (CHECK(meta->ifindex != if_nametoindex("lo"), "check_meta_ifindex", + "meta->ifindex = %d\n", meta->ifindex)) + return; + + if (CHECK(meta->pkt_len != sizeof(pkt_v4), "check_meta_pkt_len", + "meta->pkt_len = %zd\n", sizeof(pkt_v4))) + return; + + if (CHECK(memcmp(trace_pkt_v4, &pkt_v4, sizeof(pkt_v4)), + "check_packet_content", "content not the same\n")) + return; + + *(bool *)ctx = true; +} + void test_xdp_bpf2bpf(void) { __u32 duration = 0, retval, size; char buf[128]; int err, pkt_fd, map_fd; + bool passed = false; struct iphdr *iph = (void *)buf + sizeof(struct ethhdr); struct iptnl_info value4 = {.family = AF_INET}; struct test_xdp *pkt_skel = NULL; struct test_xdp_bpf2bpf *ftrace_skel = NULL; struct vip key4 = {.protocol = 6, .family = AF_INET}; struct bpf_program *prog; + struct perf_buffer *pb = NULL; + struct perf_buffer_opts pb_opts = {}; /* Load XDP program to introspect */ pkt_skel = test_xdp__open_and_load(); @@ -50,6 +84,14 @@ void test_xdp_bpf2bpf(void) if (CHECK(err, "ftrace_attach", "ftrace attach failed: %d\n", err)) goto out; + /* Set up perf buffer */ + pb_opts.sample_cb = on_sample; + pb_opts.ctx = &passed; + pb = perf_buffer__new(bpf_map__fd(ftrace_skel->maps.perf_buf_map), + 1, &pb_opts); + if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb))) + goto out; + /* Run test program */ err = bpf_prog_test_run(pkt_fd, 1, &pkt_v4, sizeof(pkt_v4), buf, &size, &retval, &duration); @@ -60,6 +102,15 @@ void test_xdp_bpf2bpf(void) err, errno, retval, size)) goto out; + /* Make sure bpf_xdp_output() was triggered and it sent the expected + * data to the perf ring buffer. + */ + err = perf_buffer__poll(pb, 100); + if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err)) + goto out; + + CHECK_FAIL(!passed); + /* Verify test results */ if (CHECK(ftrace_skel->bss->test_result_fentry != if_nametoindex("lo"), "result", "fentry failed err %llu\n", @@ -70,6 +121,8 @@ void test_xdp_bpf2bpf(void) "fexit failed err %llu\n", ftrace_skel->bss->test_result_fexit); out: + if (pb) + perf_buffer__free(pb); test_xdp__destroy(pkt_skel); test_xdp_bpf2bpf__destroy(ftrace_skel); } diff --git a/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c b/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c index 42dd2fedd588..a038e827f850 100644 --- a/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c +++ b/tools/testing/selftests/bpf/progs/test_xdp_bpf2bpf.c @@ -3,6 +3,8 @@ #include #include +char _license[] SEC("license") = "GPL"; + struct net_device { /* Structure does not need to contain all entries, * as "preserve_access_index" will use BTF to fix this... @@ -27,10 +29,32 @@ struct xdp_buff { struct xdp_rxq_info *rxq; } __attribute__((preserve_access_index)); +struct meta { + int ifindex; + int pkt_len; +}; + +struct { + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); + __uint(key_size, sizeof(int)); + __uint(value_size, sizeof(int)); +} perf_buf_map SEC(".maps"); + __u64 test_result_fentry = 0; SEC("fentry/FUNC") int BPF_PROG(trace_on_entry, struct xdp_buff *xdp) { + struct meta meta; + void *data_end = (void *)(long)xdp->data_end; + void *data = (void *)(long)xdp->data; + + meta.ifindex = xdp->rxq->dev->ifindex; + meta.pkt_len = data_end - data; + bpf_xdp_output(xdp, &perf_buf_map, + ((__u64) meta.pkt_len << 32) | + BPF_F_CURRENT_CPU, + &meta, sizeof(meta)); + test_result_fentry = xdp->rxq->dev->ifindex; return 0; } -- cgit v1.2.3 From 1cd9b3abf5332102d4d967555e7ed861a75094bf Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 11 Mar 2020 15:29:25 +0900 Subject: net: drop_monitor: use IS_REACHABLE() to guard net_dm_hw_report() In net/Kconfig, NET_DEVLINK implies NET_DROP_MONITOR. The original behavior of the 'imply' keyword prevents NET_DROP_MONITOR from being 'm' when NET_DEVLINK=y. With the planned Kconfig change that relaxes the 'imply', the combination of NET_DEVLINK=y and NET_DROP_MONITOR=m would be allowed. Use IS_REACHABLE() to avoid the vmlinux link error for this case. Reported-by: Stephen Rothwell Signed-off-by: Masahiro Yamada Acked-by: Neil Horman --- include/net/drop_monitor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/drop_monitor.h b/include/net/drop_monitor.h index 2ab668461463..f68bc373544a 100644 --- a/include/net/drop_monitor.h +++ b/include/net/drop_monitor.h @@ -19,7 +19,7 @@ struct net_dm_hw_metadata { struct net_device *input_dev; }; -#if IS_ENABLED(CONFIG_NET_DROP_MONITOR) +#if IS_REACHABLE(CONFIG_NET_DROP_MONITOR) void net_dm_hw_report(struct sk_buff *skb, const struct net_dm_hw_metadata *hw_metadata); #else -- cgit v1.2.3 From 04fac2412ba413fc9f7f792baa0b67d92ff4d1a4 Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Tue, 10 Mar 2020 16:13:56 +0530 Subject: soc/tegra: pmc: Add pins for Tegra194 Extend the Tegra194 IO pad table with additional information such as pin names and 1.8/3.3 V settings to allow a table of voltage control pins to generated from it. This is similar to what's done for older chips and is needed to support high-speed modes for SDHCI where switching the pins to 1.8V or 3.3V is necessary. Signed-off-by: Venkat Reddy Talla Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 110 +++++++++++++++++++++++++++--------------------- include/soc/tegra/pmc.h | 3 +- 2 files changed, 63 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index f65aea61149d..56c9838df987 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3,7 +3,7 @@ * drivers/soc/tegra/pmc.c * * Copyright (c) 2010 Google, Inc - * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross @@ -3227,54 +3227,64 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .has_blink_output = false, }; +#define TEGRA194_IO_PAD_TABLE(_pad) \ + /* .id .dpd .voltage .name */ \ + _pad(TEGRA_IO_PAD_CSIA, 0, UINT_MAX, "csia"), \ + _pad(TEGRA_IO_PAD_CSIB, 1, UINT_MAX, "csib"), \ + _pad(TEGRA_IO_PAD_MIPI_BIAS, 3, UINT_MAX, "mipi-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK_BIAS, 4, UINT_MAX, "pex-clk-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK3, 5, UINT_MAX, "pex-clk3"), \ + _pad(TEGRA_IO_PAD_PEX_CLK2, 6, UINT_MAX, "pex-clk2"), \ + _pad(TEGRA_IO_PAD_PEX_CLK1, 7, UINT_MAX, "pex-clk1"), \ + _pad(TEGRA_IO_PAD_EQOS, 8, UINT_MAX, "eqos"), \ + _pad(TEGRA_IO_PAD_PEX_CLK_2_BIAS,9, UINT_MAX, "pex-clk-2-bias"), \ + _pad(TEGRA_IO_PAD_PEX_CLK_2, 10, UINT_MAX, "pex-clk-2"), \ + _pad(TEGRA_IO_PAD_DAP3, 11, UINT_MAX, "dap3"), \ + _pad(TEGRA_IO_PAD_DAP5, 12, UINT_MAX, "dap5"), \ + _pad(TEGRA_IO_PAD_UART, 14, UINT_MAX, "uart"), \ + _pad(TEGRA_IO_PAD_PWR_CTL, 15, UINT_MAX, "pwr-ctl"), \ + _pad(TEGRA_IO_PAD_SOC_GPIO53, 16, UINT_MAX, "soc-gpio53"), \ + _pad(TEGRA_IO_PAD_AUDIO, 17, UINT_MAX, "audio"), \ + _pad(TEGRA_IO_PAD_GP_PWM2, 18, UINT_MAX, "gp-pwm2"), \ + _pad(TEGRA_IO_PAD_GP_PWM3, 19, UINT_MAX, "gp-pwm3"), \ + _pad(TEGRA_IO_PAD_SOC_GPIO12, 20, UINT_MAX, "soc-gpio12"), \ + _pad(TEGRA_IO_PAD_SOC_GPIO13, 21, UINT_MAX, "soc-gpio13"), \ + _pad(TEGRA_IO_PAD_SOC_GPIO10, 22, UINT_MAX, "soc-gpio10"), \ + _pad(TEGRA_IO_PAD_UART4, 23, UINT_MAX, "uart4"), \ + _pad(TEGRA_IO_PAD_UART5, 24, UINT_MAX, "uart5"), \ + _pad(TEGRA_IO_PAD_DBG, 25, UINT_MAX, "dbg"), \ + _pad(TEGRA_IO_PAD_HDMI_DP3, 26, UINT_MAX, "hdmi-dp3"), \ + _pad(TEGRA_IO_PAD_HDMI_DP2, 27, UINT_MAX, "hdmi-dp2"), \ + _pad(TEGRA_IO_PAD_HDMI_DP0, 28, UINT_MAX, "hdmi-dp0"), \ + _pad(TEGRA_IO_PAD_HDMI_DP1, 29, UINT_MAX, "hdmi-dp1"), \ + _pad(TEGRA_IO_PAD_PEX_CNTRL, 32, UINT_MAX, "pex-cntrl"), \ + _pad(TEGRA_IO_PAD_PEX_CTL2, 33, UINT_MAX, "pex-ctl2"), \ + _pad(TEGRA_IO_PAD_PEX_L0_RST_N, 34, UINT_MAX, "pex-l0-rst"), \ + _pad(TEGRA_IO_PAD_PEX_L1_RST_N, 35, UINT_MAX, "pex-l1-rst"), \ + _pad(TEGRA_IO_PAD_SDMMC4, 36, UINT_MAX, "sdmmc4"), \ + _pad(TEGRA_IO_PAD_PEX_L5_RST_N, 37, UINT_MAX, "pex-l5-rst"), \ + _pad(TEGRA_IO_PAD_CAM, 38, UINT_MAX, "cam"), \ + _pad(TEGRA_IO_PAD_CSIC, 43, UINT_MAX, "csic"), \ + _pad(TEGRA_IO_PAD_CSID, 44, UINT_MAX, "csid"), \ + _pad(TEGRA_IO_PAD_CSIE, 45, UINT_MAX, "csie"), \ + _pad(TEGRA_IO_PAD_CSIF, 46, UINT_MAX, "csif"), \ + _pad(TEGRA_IO_PAD_SPI, 47, UINT_MAX, "spi"), \ + _pad(TEGRA_IO_PAD_UFS, 49, UINT_MAX, "ufs"), \ + _pad(TEGRA_IO_PAD_CSIG, 50, UINT_MAX, "csig"), \ + _pad(TEGRA_IO_PAD_CSIH, 51, UINT_MAX, "csih"), \ + _pad(TEGRA_IO_PAD_EDP, 53, UINT_MAX, "edp"), \ + _pad(TEGRA_IO_PAD_SDMMC1_HV, 55, 4, "sdmmc1-hv"), \ + _pad(TEGRA_IO_PAD_SDMMC3_HV, 56, 6, "sdmmc3-hv"), \ + _pad(TEGRA_IO_PAD_CONN, 60, UINT_MAX, "conn"), \ + _pad(TEGRA_IO_PAD_AUDIO_HV, 61, 1, "audio-hv"), \ + _pad(TEGRA_IO_PAD_AO_HV, UINT_MAX, 0, "ao-hv") + static const struct tegra_io_pad_soc tegra194_io_pads[] = { - { .id = TEGRA_IO_PAD_CSIA, .dpd = 0, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIB, .dpd = 1, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_MIPI_BIAS, .dpd = 3, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK_BIAS, .dpd = 4, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK3, .dpd = 5, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 6, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK1, .dpd = 7, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_EQOS, .dpd = 8, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK2_BIAS, .dpd = 9, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CLK2, .dpd = 10, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DAP3, .dpd = 11, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DAP5, .dpd = 12, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UART, .dpd = 14, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PWR_CTL, .dpd = 15, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SOC_GPIO53, .dpd = 16, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_AUDIO, .dpd = 17, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_GP_PWM2, .dpd = 18, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_GP_PWM3, .dpd = 19, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SOC_GPIO12, .dpd = 20, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SOC_GPIO13, .dpd = 21, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SOC_GPIO10, .dpd = 22, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UART4, .dpd = 23, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UART5, .dpd = 24, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_DBG, .dpd = 25, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI_DP3, .dpd = 26, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI_DP2, .dpd = 27, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI_DP0, .dpd = 28, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_HDMI_DP1, .dpd = 29, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CNTRL, .dpd = 32, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_CTL2, .dpd = 33, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_L0_RST_N, .dpd = 34, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_L1_RST_N, .dpd = 35, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC4, .dpd = 36, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_PEX_L5_RST_N, .dpd = 37, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIC, .dpd = 43, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSID, .dpd = 44, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIE, .dpd = 45, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIF, .dpd = 46, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SPI, .dpd = 47, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_UFS, .dpd = 49, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIG, .dpd = 50, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CSIH, .dpd = 51, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_EDP, .dpd = 53, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC1_HV, .dpd = 55, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_SDMMC3_HV, .dpd = 56, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_CONN, .dpd = 60, .voltage = UINT_MAX }, - { .id = TEGRA_IO_PAD_AUDIO_HV, .dpd = 61, .voltage = UINT_MAX }, + TEGRA194_IO_PAD_TABLE(TEGRA_IO_PAD) +}; + +static const struct pinctrl_pin_desc tegra194_pin_descs[] = { + TEGRA194_IO_PAD_TABLE(TEGRA_IO_PIN_DESC) }; static const struct tegra_pmc_regs tegra194_pmc_regs = { @@ -3327,10 +3337,12 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { .has_tsense_reset = false, .has_gpu_clamps = false, .needs_mbist_war = false, - .has_impl_33v_pwr = false, + .has_impl_33v_pwr = true, .maybe_tz_only = false, .num_io_pads = ARRAY_SIZE(tegra194_io_pads), .io_pads = tegra194_io_pads, + .num_pin_descs = ARRAY_SIZE(tegra194_pin_descs), + .pin_descs = tegra194_pin_descs, .regs = &tegra194_pmc_regs, .init = NULL, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index 57e58faf660b..0dd52b0a5c1b 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -113,8 +113,9 @@ enum tegra_io_pad { TEGRA_IO_PAD_PEX_CLK_BIAS, TEGRA_IO_PAD_PEX_CLK1, TEGRA_IO_PAD_PEX_CLK2, - TEGRA_IO_PAD_PEX_CLK2_BIAS, TEGRA_IO_PAD_PEX_CLK3, + TEGRA_IO_PAD_PEX_CLK_2_BIAS, + TEGRA_IO_PAD_PEX_CLK_2, TEGRA_IO_PAD_PEX_CNTRL, TEGRA_IO_PAD_PEX_CTL2, TEGRA_IO_PAD_PEX_L0_RST_N, -- cgit v1.2.3 From 7741868f3837fb7eca7df8f4046f9faa3007831b Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 25 Feb 2020 01:40:46 +0300 Subject: ARM: tegra: Expose PM functions required for new cpuidle driver The upcoming unified CPUIDLE driver will be added to the drivers/cpuidle/ directory and it will require all these exposed Tegra PM-core functions. Acked-by: Peter De Schrijver Tested-by: Peter Geis Tested-by: Jasper Korten Tested-by: David Heidelberg Tested-by: Nicolas Chauvet Acked-by: Daniel Lezcano Signed-off-by: Dmitry Osipenko [treding@nvidia.com: fixup missing include rename] Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/cpuidle-tegra114.c | 3 ++- arch/arm/mach-tegra/cpuidle-tegra20.c | 4 ++-- arch/arm/mach-tegra/cpuidle-tegra30.c | 3 ++- arch/arm/mach-tegra/irq.c | 3 ++- arch/arm/mach-tegra/irq.h | 11 ----------- arch/arm/mach-tegra/pm.h | 8 -------- arch/arm/mach-tegra/sleep.h | 1 - arch/arm/mach-tegra/tegra.c | 1 - include/soc/tegra/irq.h | 13 +++++++++++++ include/soc/tegra/pm.h | 31 +++++++++++++++++++++++++++++++ 10 files changed, 52 insertions(+), 26 deletions(-) delete mode 100644 arch/arm/mach-tegra/irq.h create mode 100644 include/soc/tegra/irq.h (limited to 'include') diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c index 5118f777fd66..2d8527837aeb 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra114.c +++ b/arch/arm/mach-tegra/cpuidle-tegra114.c @@ -12,13 +12,14 @@ #include +#include + #include #include #include #include #include "cpuidle.h" -#include "pm.h" #include "sleep.h" #ifdef CONFIG_PM_SLEEP diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index b3087ece9421..af8c0c2d5714 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include @@ -25,8 +27,6 @@ #include "cpuidle.h" #include "iomap.h" -#include "irq.h" -#include "pm.h" #include "reset.h" #include "sleep.h" diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 17cbd118abee..3e91c29891f7 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -17,12 +17,13 @@ #include #include +#include + #include #include #include #include "cpuidle.h" -#include "pm.h" #include "sleep.h" #ifdef CONFIG_PM_SLEEP diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index ace7a390b5fe..4e1ee70b2a3f 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -18,9 +18,10 @@ #include #include +#include + #include "board.h" #include "iomap.h" -#include "irq.h" #define SGI_MASK 0xFFFF diff --git a/arch/arm/mach-tegra/irq.h b/arch/arm/mach-tegra/irq.h deleted file mode 100644 index 7a94cf121448..000000000000 --- a/arch/arm/mach-tegra/irq.h +++ /dev/null @@ -1,11 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2012, NVIDIA Corporation. All rights reserved. - */ - -#ifndef __TEGRA_IRQ_H -#define __TEGRA_IRQ_H - -bool tegra_pending_sgi(void); - -#endif diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 7d72f31dee77..81525f5f4a44 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -23,20 +23,12 @@ void tegra20_sleep_core_init(void); void tegra30_lp1_iram_hook(void); void tegra30_sleep_core_init(void); -void tegra_clear_cpu_in_lp2(void); -void tegra_set_cpu_in_lp2(void); -int tegra_idle_lp2_last(void); extern void (*tegra_tear_down_cpu)(void); #ifdef CONFIG_PM_SLEEP void tegra_init_suspend(void); -int tegra_pm_park_secondary_cpu(unsigned long cpu); #else static inline void tegra_init_suspend(void) {} -static inline int tegra_pm_park_secondary_cpu(unsigned long cpu) -{ - return -ENOTSUPP; -} #endif #endif /* _MACH_TEGRA_PM_H_ */ diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 4978def9db46..4718a3cb45a1 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -122,7 +122,6 @@ void tegra20_hotplug_shutdown(void); void tegra30_hotplug_shutdown(void); void tegra20_tear_down_cpu(void); -int tegra30_sleep_cpu_secondary_finish(unsigned long); void tegra30_tear_down_cpu(void); #endif diff --git a/arch/arm/mach-tegra/tegra.c b/arch/arm/mach-tegra/tegra.c index e512e606eabd..00aaf495bbf7 100644 --- a/arch/arm/mach-tegra/tegra.c +++ b/arch/arm/mach-tegra/tegra.c @@ -42,7 +42,6 @@ #include "common.h" #include "cpuidle.h" #include "iomap.h" -#include "irq.h" #include "pm.h" #include "reset.h" #include "sleep.h" diff --git a/include/soc/tegra/irq.h b/include/soc/tegra/irq.h new file mode 100644 index 000000000000..8eb11a7109e4 --- /dev/null +++ b/include/soc/tegra/irq.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2012, NVIDIA Corporation. All rights reserved. + */ + +#ifndef __SOC_TEGRA_IRQ_H +#define __SOC_TEGRA_IRQ_H + +#if defined(CONFIG_ARM) +bool tegra_pending_sgi(void); +#endif + +#endif /* __SOC_TEGRA_IRQ_H */ diff --git a/include/soc/tegra/pm.h b/include/soc/tegra/pm.h index 951fcd738d55..1974e8405098 100644 --- a/include/soc/tegra/pm.h +++ b/include/soc/tegra/pm.h @@ -6,6 +6,8 @@ #ifndef __SOC_TEGRA_PM_H__ #define __SOC_TEGRA_PM_H__ +#include + enum tegra_suspend_mode { TEGRA_SUSPEND_NONE = 0, TEGRA_SUSPEND_LP2, /* CPU voltage off */ @@ -20,6 +22,12 @@ tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode); /* low-level resume entry point */ void tegra_resume(void); + +int tegra30_sleep_cpu_secondary_finish(unsigned long arg); +void tegra_clear_cpu_in_lp2(void); +void tegra_set_cpu_in_lp2(void); +int tegra_idle_lp2_last(void); +int tegra_pm_park_secondary_cpu(unsigned long cpu); #else static inline enum tegra_suspend_mode tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode) @@ -30,6 +38,29 @@ tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode) static inline void tegra_resume(void) { } + +static inline int tegra30_sleep_cpu_secondary_finish(unsigned long arg) +{ + return -ENOTSUPP; +} + +static inline void tegra_clear_cpu_in_lp2(void) +{ +} + +static inline void tegra_set_cpu_in_lp2(void) +{ +} + +static inline int tegra_idle_lp2_last(void) +{ + return -ENOTSUPP; +} + +static inline int tegra_pm_park_secondary_cpu(unsigned long cpu) +{ + return -ENOTSUPP; +} #endif /* CONFIG_PM_SLEEP */ #endif /* __SOC_TEGRA_PM_H__ */ -- cgit v1.2.3 From 1f3e18ec95f61589404d1471dc01c9599352dd93 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 25 Feb 2020 01:40:47 +0300 Subject: ARM: tegra: Rename some of the newly exposed PM functions Rename some of the recently exposed PM functions, prefixing them with "tegra_pm_" in order to make the naming of the PM functions consistent. Acked-by: Peter De Schrijver Tested-by: Peter Geis Tested-by: Jasper Korten Tested-by: David Heidelberg Tested-by: Nicolas Chauvet Acked-by: Daniel Lezcano Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/cpuidle-tegra114.c | 6 +++--- arch/arm/mach-tegra/cpuidle-tegra20.c | 6 +++--- arch/arm/mach-tegra/cpuidle-tegra30.c | 8 ++++---- arch/arm/mach-tegra/pm.c | 10 +++++----- arch/arm/mach-tegra/sleep-tegra30.S | 6 +++--- include/soc/tegra/pm.h | 16 ++++++++-------- 6 files changed, 26 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c index 2d8527837aeb..858c30cc5dc7 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra114.c +++ b/arch/arm/mach-tegra/cpuidle-tegra114.c @@ -35,17 +35,17 @@ static int tegra114_idle_power_down(struct cpuidle_device *dev, { local_fiq_disable(); - tegra_set_cpu_in_lp2(); + tegra_pm_set_cpu_in_lp2(); cpu_pm_enter(); call_firmware_op(prepare_idle, TF_PM_MODE_LP2_NOFLUSH_L2); /* Do suspend by ourselves if the firmware does not implement it */ if (call_firmware_op(do_idle, 0) == -ENOSYS) - cpu_suspend(0, tegra30_sleep_cpu_secondary_finish); + cpu_suspend(0, tegra30_pm_secondary_cpu_suspend); cpu_pm_exit(); - tegra_clear_cpu_in_lp2(); + tegra_pm_clear_cpu_in_lp2(); local_fiq_enable(); diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index af8c0c2d5714..3b61dd97bf3b 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -91,7 +91,7 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, while (!tegra_cpu_rail_off_ready()) cpu_relax(); - ret = !tegra_idle_lp2_last(); + ret = !tegra_pm_enter_lp2(); if (cpu_online(1)) tegra20_wake_cpu1_from_reset(); @@ -136,7 +136,7 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, local_fiq_disable(); - tegra_set_cpu_in_lp2(); + tegra_pm_set_cpu_in_lp2(); cpu_pm_enter(); if (dev->cpu == 0) @@ -145,7 +145,7 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index); cpu_pm_exit(); - tegra_clear_cpu_in_lp2(); + tegra_pm_clear_cpu_in_lp2(); local_fiq_enable(); diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 3e91c29891f7..a4f0add46a27 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -69,7 +69,7 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, return false; } - return !tegra_idle_lp2_last(); + return !tegra_pm_enter_lp2(); } #ifdef CONFIG_SMP @@ -79,7 +79,7 @@ static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev, { smp_wmb(); - cpu_suspend(0, tegra30_sleep_cpu_secondary_finish); + cpu_suspend(0, tegra30_pm_secondary_cpu_suspend); return true; } @@ -100,7 +100,7 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, local_fiq_disable(); - tegra_set_cpu_in_lp2(); + tegra_pm_set_cpu_in_lp2(); cpu_pm_enter(); if (dev->cpu == 0) @@ -109,7 +109,7 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index); cpu_pm_exit(); - tegra_clear_cpu_in_lp2(); + tegra_pm_clear_cpu_in_lp2(); local_fiq_enable(); diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index a094acaca307..7d9ef26e52a7 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -110,7 +110,7 @@ static void suspend_cpu_complex(void) flowctrl_cpu_suspend_enter(cpu); } -void tegra_clear_cpu_in_lp2(void) +void tegra_pm_clear_cpu_in_lp2(void) { int phy_cpu_id = cpu_logical_map(smp_processor_id()); u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; @@ -123,7 +123,7 @@ void tegra_clear_cpu_in_lp2(void) spin_unlock(&tegra_lp2_lock); } -void tegra_set_cpu_in_lp2(void) +void tegra_pm_set_cpu_in_lp2(void) { int phy_cpu_id = cpu_logical_map(smp_processor_id()); u32 *cpu_in_lp2 = tegra_cpu_lp2_mask; @@ -189,7 +189,7 @@ static void tegra_pm_set(enum tegra_suspend_mode mode) tegra_pmc_enter_suspend_mode(mode); } -int tegra_idle_lp2_last(void) +int tegra_pm_enter_lp2(void) { int err; @@ -356,7 +356,7 @@ static int tegra_suspend_enter(suspend_state_t state) tegra_suspend_enter_lp1(); break; case TEGRA_SUSPEND_LP2: - tegra_set_cpu_in_lp2(); + tegra_pm_set_cpu_in_lp2(); break; default: break; @@ -377,7 +377,7 @@ static int tegra_suspend_enter(suspend_state_t state) tegra_suspend_exit_lp1(); break; case TEGRA_SUSPEND_LP2: - tegra_clear_cpu_in_lp2(); + tegra_pm_clear_cpu_in_lp2(); break; default: break; diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index 02cc6ff96f30..e7bcf7dc4675 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -265,11 +265,11 @@ ENTRY(tegra30_sleep_core_finish) ENDPROC(tegra30_sleep_core_finish) /* - * tegra30_sleep_cpu_secondary_finish(unsigned long v2p) + * tegra30_pm_secondary_cpu_suspend(unsigned long unused_arg) * * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU. */ -ENTRY(tegra30_sleep_cpu_secondary_finish) +ENTRY(tegra30_pm_secondary_cpu_suspend) mov r7, lr /* Flush and disable the L1 data cache */ @@ -281,7 +281,7 @@ ENTRY(tegra30_sleep_cpu_secondary_finish) bl tegra30_cpu_shutdown mov r0, #1 @ never return here ret r7 -ENDPROC(tegra30_sleep_cpu_secondary_finish) +ENDPROC(tegra30_pm_secondary_cpu_suspend) /* * tegra30_tear_down_cpu diff --git a/include/soc/tegra/pm.h b/include/soc/tegra/pm.h index 1974e8405098..08477d7bfab9 100644 --- a/include/soc/tegra/pm.h +++ b/include/soc/tegra/pm.h @@ -23,10 +23,10 @@ tegra_pm_validate_suspend_mode(enum tegra_suspend_mode mode); /* low-level resume entry point */ void tegra_resume(void); -int tegra30_sleep_cpu_secondary_finish(unsigned long arg); -void tegra_clear_cpu_in_lp2(void); -void tegra_set_cpu_in_lp2(void); -int tegra_idle_lp2_last(void); +int tegra30_pm_secondary_cpu_suspend(unsigned long arg); +void tegra_pm_clear_cpu_in_lp2(void); +void tegra_pm_set_cpu_in_lp2(void); +int tegra_pm_enter_lp2(void); int tegra_pm_park_secondary_cpu(unsigned long cpu); #else static inline enum tegra_suspend_mode @@ -39,20 +39,20 @@ static inline void tegra_resume(void) { } -static inline int tegra30_sleep_cpu_secondary_finish(unsigned long arg) +static inline int tegra30_pm_secondary_cpu_suspend(unsigned long arg) { return -ENOTSUPP; } -static inline void tegra_clear_cpu_in_lp2(void) +static inline void tegra_pm_clear_cpu_in_lp2(void) { } -static inline void tegra_set_cpu_in_lp2(void) +static inline void tegra_pm_set_cpu_in_lp2(void) { } -static inline int tegra_idle_lp2_last(void) +static inline int tegra_pm_enter_lp2(void) { return -ENOTSUPP; } -- cgit v1.2.3 From 860fbde438dc88d2fedf75965963b96c9041a0d5 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 25 Feb 2020 01:40:52 +0300 Subject: cpuidle: Refactor and move out NVIDIA Tegra20 driver into drivers/cpuidle The driver's code is refactored in a way that will make it easy to support Tegra30/114/124 SoCs by this unified driver later on. The current functionality is equal to the old Tegra20 driver, only the code's structure changed a tad. This is also a proper platform driver now. Acked-by: Peter De Schrijver Signed-off-by: Dmitry Osipenko Acked-by: Daniel Lezcano Signed-off-by: Thierry Reding --- arch/arm/mach-tegra/Makefile | 3 - arch/arm/mach-tegra/cpuidle-tegra20.c | 219 -------------------------- arch/arm/mach-tegra/cpuidle.c | 14 +- arch/arm/mach-tegra/cpuidle.h | 4 - drivers/cpuidle/Kconfig.arm | 8 + drivers/cpuidle/Makefile | 1 + drivers/cpuidle/cpuidle-tegra.c | 280 ++++++++++++++++++++++++++++++++++ include/soc/tegra/cpuidle.h | 2 +- 8 files changed, 292 insertions(+), 239 deletions(-) delete mode 100644 arch/arm/mach-tegra/cpuidle-tegra20.c create mode 100644 drivers/cpuidle/cpuidle-tegra.c (limited to 'include') diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 965862608ff6..8425bb5608d5 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -12,9 +12,6 @@ obj-y += sleep-tegra20.o obj-y += sleep-tegra30.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o -ifeq ($(CONFIG_CPU_IDLE),y) -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += cpuidle-tegra20.o -endif obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c deleted file mode 100644 index a06a4b517fb8..000000000000 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ /dev/null @@ -1,219 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * CPU idle driver for Tegra CPUs - * - * Copyright (c) 2010-2012, NVIDIA Corporation. - * Copyright (c) 2011 Google, Inc. - * Author: Colin Cross - * Gary King - * - * Rework for 3.3 by Peter De Schrijver - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "cpuidle.h" -#include "iomap.h" -#include "reset.h" -#include "sleep.h" - -#ifdef CONFIG_PM_SLEEP -static atomic_t abort_flag; -static atomic_t abort_barrier; -static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index); -#define TEGRA20_MAX_STATES 2 -#else -#define TEGRA20_MAX_STATES 1 -#endif - -static struct cpuidle_driver tegra_idle_driver = { - .name = "tegra_idle", - .owner = THIS_MODULE, - .states = { - ARM_CPUIDLE_WFI_STATE_PWR(600), -#ifdef CONFIG_PM_SLEEP - { - .enter = tegra20_idle_lp2_coupled, - .exit_latency = 5000, - .target_residency = 10000, - .power_usage = 0, - .flags = CPUIDLE_FLAG_COUPLED | - CPUIDLE_FLAG_TIMER_STOP, - .name = "powered-down", - .desc = "CPU power gated", - }, -#endif - }, - .state_count = TEGRA20_MAX_STATES, - .safe_state_index = 0, -}; - -#ifdef CONFIG_PM_SLEEP -#ifdef CONFIG_SMP -static void tegra20_wake_cpu1_from_reset(void) -{ - /* enable cpu clock on cpu */ - tegra_enable_cpu_clock(1); - - /* take the CPU out of reset */ - tegra_cpu_out_of_reset(1); - - /* unhalt the cpu */ - flowctrl_write_cpu_halt(1, 0); -} -#else -static inline void tegra20_wake_cpu1_from_reset(void) -{ -} -#endif - -static void tegra20_report_cpus_state(void) -{ - unsigned long cpu, lcpu, csr; - - for_each_cpu(lcpu, cpu_possible_mask) { - cpu = cpu_logical_map(lcpu); - csr = flowctrl_read_cpu_csr(cpu); - - pr_err("cpu%lu: online=%d flowctrl_csr=0x%08lx\n", - cpu, cpu_online(lcpu), csr); - } -} - -static int tegra20_wait_for_secondary_cpu_parking(void) -{ - unsigned int retries = 3; - - while (retries--) { - unsigned int delay_us = 10; - unsigned int timeout_us = 500 * 1000 / delay_us; - - /* - * The primary CPU0 core shall wait for the secondaries - * shutdown in order to power-off CPU's cluster safely. - * The timeout value depends on the current CPU frequency, - * it takes about 40-150us in average and over 1000us in - * a worst case scenario. - */ - do { - if (tegra_cpu_rail_off_ready()) - return 0; - - udelay(delay_us); - - } while (timeout_us--); - - pr_err("secondary CPU taking too long to park\n"); - - tegra20_report_cpus_state(); - } - - pr_err("timed out waiting secondaries to park\n"); - - return -ETIMEDOUT; -} - -static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - bool ret; - - if (tegra20_wait_for_secondary_cpu_parking()) - return false; - - ret = !tegra_pm_enter_lp2(); - - if (cpu_online(1)) - tegra20_wake_cpu1_from_reset(); - - return ret; -} - -#ifdef CONFIG_SMP -static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - cpu_suspend(dev->cpu, tegra_pm_park_secondary_cpu); - - return true; -} -#else -static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - return true; -} -#endif - -static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, - struct cpuidle_driver *drv, - int index) -{ - bool entered_lp2 = false; - - if (tegra_pending_sgi()) - atomic_set(&abort_flag, 1); - - cpuidle_coupled_parallel_barrier(dev, &abort_barrier); - - if (atomic_read(&abort_flag)) { - cpuidle_coupled_parallel_barrier(dev, &abort_barrier); - /* clean flag for next coming */ - atomic_set(&abort_flag, 0); - return -EINTR; - } - - local_fiq_disable(); - - tegra_pm_set_cpu_in_lp2(); - cpu_pm_enter(); - - if (dev->cpu == 0) - entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index); - else - entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index); - - cpu_pm_exit(); - tegra_pm_clear_cpu_in_lp2(); - - local_fiq_enable(); - - return entered_lp2 ? index : 0; -} -#endif - -/* - * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether - * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around - * this, simply disable LP2 if the PCI driver and DT node are both enabled. - */ -void tegra20_cpuidle_pcie_irqs_in_use(void) -{ - pr_info_once( - "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n"); - cpuidle_driver_state_disabled(&tegra_idle_driver, 1, true); -} - -int __init tegra20_cpuidle_init(void) -{ - return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); -} diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c index d565c44cfc93..eee85d517783 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -14,6 +14,7 @@ #include #include +#include #include @@ -23,8 +24,7 @@ void __init tegra_cpuidle_init(void) { switch (tegra_get_chip_id()) { case TEGRA20: - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) - tegra20_cpuidle_init(); + platform_device_register_simple("tegra-cpuidle", -1, NULL, 0); break; case TEGRA30: if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC)) @@ -38,13 +38,3 @@ void __init tegra_cpuidle_init(void) break; } } - -void tegra_cpuidle_pcie_irqs_in_use(void) -{ - switch (tegra_get_chip_id()) { - case TEGRA20: - if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) - tegra20_cpuidle_pcie_irqs_in_use(); - break; - } -} diff --git a/arch/arm/mach-tegra/cpuidle.h b/arch/arm/mach-tegra/cpuidle.h index 4e1f459f5bd8..eeb37baf18e1 100644 --- a/arch/arm/mach-tegra/cpuidle.h +++ b/arch/arm/mach-tegra/cpuidle.h @@ -7,15 +7,11 @@ #define __MACH_TEGRA_CPUIDLE_H #ifdef CONFIG_CPU_IDLE -int tegra20_cpuidle_init(void); -void tegra20_cpuidle_pcie_irqs_in_use(void); int tegra30_cpuidle_init(void); int tegra114_cpuidle_init(void); void tegra_cpuidle_init(void); -void tegra_cpuidle_pcie_irqs_in_use(void); #else static inline void tegra_cpuidle_init(void) {} -static inline void tegra_cpuidle_pcie_irqs_in_use(void) {} #endif #endif diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 62272ecfa771..99a2d72ac02b 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -86,3 +86,11 @@ config ARM_MVEBU_V7_CPUIDLE depends on (ARCH_MVEBU || COMPILE_TEST) && !ARM64 help Select this to enable cpuidle on Armada 370, 38x and XP processors. + +config ARM_TEGRA_CPUIDLE + bool "CPU Idle Driver for NVIDIA Tegra SoCs" + depends on ARCH_TEGRA && !ARM64 + select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP + select ARM_CPU_SUSPEND + help + Select this to enable cpuidle for NVIDIA Tegra20/30/114/124 SoCs. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index cc8c769d7fa9..55a464f6a78b 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.o obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle_psci.o cpuidle_psci-y := cpuidle-psci.o cpuidle_psci-$(CONFIG_PM_GENERIC_DOMAINS_OF) += cpuidle-psci-domain.o +obj-$(CONFIG_ARM_TEGRA_CPUIDLE) += cpuidle-tegra.o ############################################################################### # MIPS drivers diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c new file mode 100644 index 000000000000..5691bdcf11cb --- /dev/null +++ b/drivers/cpuidle/cpuidle-tegra.c @@ -0,0 +1,280 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CPU idle driver for Tegra CPUs + * + * Copyright (c) 2010-2013, NVIDIA Corporation. + * Copyright (c) 2011 Google, Inc. + * Author: Colin Cross + * Gary King + * + * Rework for 3.3 by Peter De Schrijver + * + * Tegra20/124 driver unification by Dmitry Osipenko + */ + +#define pr_fmt(fmt) "tegra-cpuidle: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +enum tegra_state { + TEGRA_C1, + TEGRA_CC6, + TEGRA_STATE_COUNT, +}; + +static atomic_t tegra_idle_barrier; +static atomic_t tegra_abort_flag; + +static void tegra_cpuidle_report_cpus_state(void) +{ + unsigned long cpu, lcpu, csr; + + for_each_cpu(lcpu, cpu_possible_mask) { + cpu = cpu_logical_map(lcpu); + csr = flowctrl_read_cpu_csr(cpu); + + pr_err("cpu%lu: online=%d flowctrl_csr=0x%08lx\n", + cpu, cpu_online(lcpu), csr); + } +} + +static int tegra_cpuidle_wait_for_secondary_cpus_parking(void) +{ + unsigned int retries = 3; + + while (retries--) { + unsigned int delay_us = 10; + unsigned int timeout_us = 500 * 1000 / delay_us; + + /* + * The primary CPU0 core shall wait for the secondaries + * shutdown in order to power-off CPU's cluster safely. + * The timeout value depends on the current CPU frequency, + * it takes about 40-150us in average and over 1000us in + * a worst case scenario. + */ + do { + if (tegra_cpu_rail_off_ready()) + return 0; + + udelay(delay_us); + + } while (timeout_us--); + + pr_err("secondary CPU taking too long to park\n"); + + tegra_cpuidle_report_cpus_state(); + } + + pr_err("timed out waiting secondaries to park\n"); + + return -ETIMEDOUT; +} + +static void tegra_cpuidle_unpark_secondary_cpus(void) +{ + unsigned int cpu, lcpu; + + for_each_cpu(lcpu, cpu_online_mask) { + cpu = cpu_logical_map(lcpu); + + if (cpu > 0) { + tegra_enable_cpu_clock(cpu); + tegra_cpu_out_of_reset(cpu); + flowctrl_write_cpu_halt(cpu, 0); + } + } +} + +static int tegra_cpuidle_cc6_enter(unsigned int cpu) +{ + int ret; + + if (cpu > 0) { + ret = cpu_suspend(cpu, tegra_pm_park_secondary_cpu); + } else { + ret = tegra_cpuidle_wait_for_secondary_cpus_parking(); + if (!ret) + ret = tegra_pm_enter_lp2(); + + tegra_cpuidle_unpark_secondary_cpus(); + } + + return ret; +} + +static int tegra_cpuidle_coupled_barrier(struct cpuidle_device *dev) +{ + if (tegra_pending_sgi()) { + /* + * CPU got local interrupt that will be lost after GIC's + * shutdown because GIC driver doesn't save/restore the + * pending SGI state across CPU cluster PM. Abort and retry + * next time. + */ + atomic_set(&tegra_abort_flag, 1); + } + + cpuidle_coupled_parallel_barrier(dev, &tegra_idle_barrier); + + if (atomic_read(&tegra_abort_flag)) { + cpuidle_coupled_parallel_barrier(dev, &tegra_idle_barrier); + atomic_set(&tegra_abort_flag, 0); + return -EINTR; + } + + return 0; +} + +static int tegra_cpuidle_state_enter(struct cpuidle_device *dev, + int index, unsigned int cpu) +{ + int ret; + + /* + * CC6 state is the "CPU cluster power-off" state. In order to + * enter this state, at first the secondary CPU cores need to be + * parked into offline mode, then the last CPU should clean out + * remaining dirty cache lines into DRAM and trigger Flow Controller + * logic that turns off the cluster's power domain (which includes + * CPU cores, GIC and L2 cache). + */ + if (index == TEGRA_CC6) { + ret = tegra_cpuidle_coupled_barrier(dev); + if (ret) + return ret; + } + + local_fiq_disable(); + tegra_pm_set_cpu_in_lp2(); + cpu_pm_enter(); + + switch (index) { + case TEGRA_CC6: + ret = tegra_cpuidle_cc6_enter(cpu); + break; + + default: + ret = -EINVAL; + break; + } + + cpu_pm_exit(); + tegra_pm_clear_cpu_in_lp2(); + local_fiq_enable(); + + return ret; +} + +static int tegra_cpuidle_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, + int index) +{ + unsigned int cpu = cpu_logical_map(dev->cpu); + int err; + + err = tegra_cpuidle_state_enter(dev, index, cpu); + if (err && err != -EINTR) + pr_err_once("cpu%u failed to enter idle state %d err: %d\n", + cpu, index, err); + + return err ? -1 : index; +} + +/* + * The previous versions of Tegra CPUIDLE driver used a different "legacy" + * terminology for naming of the idling states, while this driver uses the + * new terminology. + * + * Mapping of the old terms into the new ones: + * + * Old | New + * --------- + * LP3 | C1 (CPU core clock gating) + * LP2 | C7 (CPU core power gating) + * LP2 | CC6 (CPU cluster power gating) + * + * Note that that the older CPUIDLE driver versions didn't explicitly + * differentiate the LP2 states because these states either used the same + * code path or because CC6 wasn't supported. + */ +static struct cpuidle_driver tegra_idle_driver = { + .name = "tegra_idle", + .states = { + [TEGRA_C1] = ARM_CPUIDLE_WFI_STATE_PWR(600), + [TEGRA_CC6] = { + .enter = tegra_cpuidle_enter, + .exit_latency = 5000, + .target_residency = 10000, + .power_usage = 0, + .flags = CPUIDLE_FLAG_TIMER_STOP | + CPUIDLE_FLAG_COUPLED, + .name = "CC6", + .desc = "CPU cluster powered off", + }, + }, + .state_count = TEGRA_STATE_COUNT, + .safe_state_index = TEGRA_C1, +}; + +static inline void tegra_cpuidle_disable_state(enum tegra_state state) +{ + cpuidle_driver_state_disabled(&tegra_idle_driver, state, true); +} + +/* + * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether + * they are legacy IRQs or MSI, are lost when CC6 is enabled. To work around + * this, simply disable CC6 if the PCI driver and DT node are both enabled. + */ +void tegra_cpuidle_pcie_irqs_in_use(void) +{ + struct cpuidle_state *state_cc6 = &tegra_idle_driver.states[TEGRA_CC6]; + + if ((state_cc6->flags & CPUIDLE_FLAG_UNUSABLE) || + tegra_get_chip_id() != TEGRA20) + return; + + pr_info("disabling CC6 state, since PCIe IRQs are in use\n"); + tegra_cpuidle_disable_state(TEGRA_CC6); +} + +static int tegra_cpuidle_probe(struct platform_device *pdev) +{ + /* + * Required suspend-resume functionality, which is provided by the + * Tegra-arch core and PMC driver, is unavailable if PM-sleep option + * is disabled. + */ + if (!IS_ENABLED(CONFIG_PM_SLEEP)) + tegra_cpuidle_disable_state(TEGRA_CC6); + + return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); +} + +static struct platform_driver tegra_cpuidle_driver = { + .probe = tegra_cpuidle_probe, + .driver = { + .name = "tegra-cpuidle", + }, +}; +builtin_platform_driver(tegra_cpuidle_driver); diff --git a/include/soc/tegra/cpuidle.h b/include/soc/tegra/cpuidle.h index 029ba1f4b2cc..5665975015d8 100644 --- a/include/soc/tegra/cpuidle.h +++ b/include/soc/tegra/cpuidle.h @@ -6,7 +6,7 @@ #ifndef __SOC_TEGRA_CPUIDLE_H__ #define __SOC_TEGRA_CPUIDLE_H__ -#if defined(CONFIG_ARM) && defined(CONFIG_ARCH_TEGRA) && defined(CONFIG_CPU_IDLE) +#ifdef CONFIG_ARM_TEGRA_CPUIDLE void tegra_cpuidle_pcie_irqs_in_use(void); #else static inline void tegra_cpuidle_pcie_irqs_in_use(void) -- cgit v1.2.3 From ba3b01d7a6f4ab9f8a0557044c9a7678f64ae070 Mon Sep 17 00:00:00 2001 From: Megha Dey Date: Mon, 9 Mar 2020 13:09:46 -0700 Subject: iommu/vt-d: Fix debugfs register reads Commit 6825d3ea6cde ("iommu/vt-d: Add debugfs support to show register contents") dumps the register contents for all IOMMU devices. Currently, a 64 bit read(dmar_readq) is done for all the IOMMU registers, even though some of the registers are 32 bits, which is incorrect. Use the correct read function variant (dmar_readl/dmar_readq) while reading the contents of 32/64 bit registers respectively. Signed-off-by: Megha Dey Link: https://lore.kernel.org/r/1583784587-26126-2-git-send-email-megha.dey@linux.intel.com Acked-by: Lu Baolu Signed-off-by: Joerg Roedel --- drivers/iommu/intel-iommu-debugfs.c | 40 +++++++++++++++++++++++-------------- include/linux/intel-iommu.h | 2 ++ 2 files changed, 27 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/iommu/intel-iommu-debugfs.c b/drivers/iommu/intel-iommu-debugfs.c index c1257bef553c..0a7791934a16 100644 --- a/drivers/iommu/intel-iommu-debugfs.c +++ b/drivers/iommu/intel-iommu-debugfs.c @@ -33,38 +33,42 @@ struct iommu_regset { #define IOMMU_REGSET_ENTRY(_reg_) \ { DMAR_##_reg_##_REG, __stringify(_reg_) } -static const struct iommu_regset iommu_regs[] = { + +static const struct iommu_regset iommu_regs_32[] = { IOMMU_REGSET_ENTRY(VER), - IOMMU_REGSET_ENTRY(CAP), - IOMMU_REGSET_ENTRY(ECAP), IOMMU_REGSET_ENTRY(GCMD), IOMMU_REGSET_ENTRY(GSTS), - IOMMU_REGSET_ENTRY(RTADDR), - IOMMU_REGSET_ENTRY(CCMD), IOMMU_REGSET_ENTRY(FSTS), IOMMU_REGSET_ENTRY(FECTL), IOMMU_REGSET_ENTRY(FEDATA), IOMMU_REGSET_ENTRY(FEADDR), IOMMU_REGSET_ENTRY(FEUADDR), - IOMMU_REGSET_ENTRY(AFLOG), IOMMU_REGSET_ENTRY(PMEN), IOMMU_REGSET_ENTRY(PLMBASE), IOMMU_REGSET_ENTRY(PLMLIMIT), + IOMMU_REGSET_ENTRY(ICS), + IOMMU_REGSET_ENTRY(PRS), + IOMMU_REGSET_ENTRY(PECTL), + IOMMU_REGSET_ENTRY(PEDATA), + IOMMU_REGSET_ENTRY(PEADDR), + IOMMU_REGSET_ENTRY(PEUADDR), +}; + +static const struct iommu_regset iommu_regs_64[] = { + IOMMU_REGSET_ENTRY(CAP), + IOMMU_REGSET_ENTRY(ECAP), + IOMMU_REGSET_ENTRY(RTADDR), + IOMMU_REGSET_ENTRY(CCMD), + IOMMU_REGSET_ENTRY(AFLOG), IOMMU_REGSET_ENTRY(PHMBASE), IOMMU_REGSET_ENTRY(PHMLIMIT), IOMMU_REGSET_ENTRY(IQH), IOMMU_REGSET_ENTRY(IQT), IOMMU_REGSET_ENTRY(IQA), - IOMMU_REGSET_ENTRY(ICS), IOMMU_REGSET_ENTRY(IRTA), IOMMU_REGSET_ENTRY(PQH), IOMMU_REGSET_ENTRY(PQT), IOMMU_REGSET_ENTRY(PQA), - IOMMU_REGSET_ENTRY(PRS), - IOMMU_REGSET_ENTRY(PECTL), - IOMMU_REGSET_ENTRY(PEDATA), - IOMMU_REGSET_ENTRY(PEADDR), - IOMMU_REGSET_ENTRY(PEUADDR), IOMMU_REGSET_ENTRY(MTRRCAP), IOMMU_REGSET_ENTRY(MTRRDEF), IOMMU_REGSET_ENTRY(MTRR_FIX64K_00000), @@ -127,10 +131,16 @@ static int iommu_regset_show(struct seq_file *m, void *unused) * by adding the offset to the pointer (virtual address). */ raw_spin_lock_irqsave(&iommu->register_lock, flag); - for (i = 0 ; i < ARRAY_SIZE(iommu_regs); i++) { - value = dmar_readq(iommu->reg + iommu_regs[i].offset); + for (i = 0 ; i < ARRAY_SIZE(iommu_regs_32); i++) { + value = dmar_readl(iommu->reg + iommu_regs_32[i].offset); + seq_printf(m, "%-16s\t0x%02x\t\t0x%016llx\n", + iommu_regs_32[i].regs, iommu_regs_32[i].offset, + value); + } + for (i = 0 ; i < ARRAY_SIZE(iommu_regs_64); i++) { + value = dmar_readq(iommu->reg + iommu_regs_64[i].offset); seq_printf(m, "%-16s\t0x%02x\t\t0x%016llx\n", - iommu_regs[i].regs, iommu_regs[i].offset, + iommu_regs_64[i].regs, iommu_regs_64[i].offset, value); } raw_spin_unlock_irqrestore(&iommu->register_lock, flag); diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 4a16b39ae353..980234ae0312 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -123,6 +123,8 @@ #define dmar_readq(a) readq(a) #define dmar_writeq(a,v) writeq(v,a) +#define dmar_readl(a) readl(a) +#define dmar_writel(a, v) writel(v, a) #define DMAR_VER_MAJOR(v) (((v) & 0xf0) >> 4) #define DMAR_VER_MINOR(v) ((v) & 0x0f) -- cgit v1.2.3 From a4f994a05926c96845ad40fb661d566a4eacf2b9 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Tue, 10 Mar 2020 11:14:32 +0200 Subject: RDMA/cm: Delete not implemented CM peer to peer communication Peer to peer support was never implemented, so delete it to make code less clutter. Link: https://lore.kernel.org/r/20200310091438.248429-6-leon@kernel.org Signed-off-by: Leon Romanovsky Reviewed-by: Mark Zhang Signed-off-by: Jason Gunthorpe --- drivers/infiniband/core/cm.c | 7 ------- include/rdma/ib_cm.h | 1 - 2 files changed, 8 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 5c58619b2399..21ede1c08eea 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -261,7 +261,6 @@ struct cm_id_private { __be16 pkey; u8 private_data_len; u8 max_cm_retries; - u8 peer_to_peer; u8 responder_resources; u8 initiator_depth; u8 retry_count; @@ -1381,10 +1380,6 @@ static void cm_format_req(struct cm_req_msg *req_msg, static int cm_validate_req_param(struct ib_cm_req_param *param) { - /* peer-to-peer not supported */ - if (param->peer_to_peer) - return -EINVAL; - if (!param->primary_path) return -EINVAL; @@ -2437,8 +2432,6 @@ static int cm_rep_handler(struct cm_work *work) cm_ack_timeout(cm_id_priv->target_ack_delay, cm_id_priv->alt_av.timeout - 1); - /* todo: handle peer_to_peer */ - ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg); ret = atomic_inc_and_test(&cm_id_priv->work_count); if (!ret) diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h index 8ec482e391aa..058cfbc2b37f 100644 --- a/include/rdma/ib_cm.h +++ b/include/rdma/ib_cm.h @@ -360,7 +360,6 @@ struct ib_cm_req_param { u32 starting_psn; const void *private_data; u8 private_data_len; - u8 peer_to_peer; u8 responder_resources; u8 initiator_depth; u8 remote_cm_response_timeout; -- cgit v1.2.3 From fc6a9f86f08acd3665f788619afae0d2b2d5a480 Mon Sep 17 00:00:00 2001 From: Saeed Mahameed Date: Tue, 10 Mar 2020 10:22:28 +0200 Subject: {IB,net}/mlx5: Assign mkey variant in mlx5_ib only mkey variant is not required for mlx5_core use, move the mkey variant counter to mlx5_ib. Signed-off-by: Saeed Mahameed Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/main.c | 1 + drivers/infiniband/hw/mlx5/mlx5_ib.h | 5 +++ drivers/infiniband/hw/mlx5/mr.c | 58 +++++++++++++++++++++----- drivers/net/ethernet/mellanox/mlx5/core/main.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/mr.c | 8 +--- include/linux/mlx5/driver.h | 4 -- 6 files changed, 55 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index e4bcfa81b70a..fce863621414 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -6390,6 +6390,7 @@ static int mlx5_ib_stage_init_init(struct mlx5_ib_dev *dev) spin_lock_init(&dev->reset_flow_resource_lock); xa_init(&dev->odp_mkeys); xa_init(&dev->sig_mrs); + spin_lock_init(&dev->mkey_lock); spin_lock_init(&dev->dm.lock); dev->dm.dev = mdev; diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index d9bffcc93587..89a050e516a8 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -992,6 +992,11 @@ struct mlx5_ib_dev { /* sync used page count stats */ struct mlx5_ib_resources devr; + + /* protect mkey key part */ + spinlock_t mkey_lock; + u8 mkey_key; + struct mlx5_mr_cache cache; struct timer_list delay_timer; /* Prevents soft lock on massive reg MRs */ diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 45c3282dd5e1..1b83d00e8ecd 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -47,6 +47,46 @@ enum { #define MLX5_UMR_ALIGN 2048 +static void +create_mkey_callback(int status, struct mlx5_async_work *context); + +static void +assign_mkey_variant(struct mlx5_ib_dev *dev, struct mlx5_core_mkey *mkey, + u32 *in) +{ + void *mkc; + u8 key; + + spin_lock_irq(&dev->mkey_lock); + key = dev->mkey_key++; + spin_unlock_irq(&dev->mkey_lock); + + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + MLX5_SET(mkc, mkc, mkey_7_0, key); + mkey->key = key; +} + +static int +mlx5_ib_create_mkey(struct mlx5_ib_dev *dev, struct mlx5_core_mkey *mkey, + u32 *in, int inlen) +{ + assign_mkey_variant(dev, mkey, in); + return mlx5_core_create_mkey(dev->mdev, mkey, in, inlen); +} + +static int +mlx5_ib_create_mkey_cb(struct mlx5_ib_dev *dev, + struct mlx5_core_mkey *mkey, + struct mlx5_async_ctx *async_ctx, + u32 *in, int inlen, u32 *out, int outlen, + struct mlx5_async_work *context) +{ + assign_mkey_variant(dev, mkey, in); + return mlx5_core_create_mkey_cb(dev->mdev, mkey, async_ctx, + in, inlen, out, outlen, + create_mkey_callback, context); +} + static void clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr); static void dereg_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr); static int mr_cache_max_order(struct mlx5_ib_dev *dev); @@ -79,7 +119,7 @@ static bool use_umr_mtt_update(struct mlx5_ib_mr *mr, u64 start, u64 length) length + (start & (MLX5_ADAPTER_PAGE_SIZE - 1)); } -static void reg_mr_callback(int status, struct mlx5_async_work *context) +static void create_mkey_callback(int status, struct mlx5_async_work *context) { struct mlx5_ib_mr *mr = container_of(context, struct mlx5_ib_mr, cb_work); @@ -160,10 +200,10 @@ static int add_keys(struct mlx5_ib_dev *dev, int c, int num) spin_lock_irq(&ent->lock); ent->pending++; spin_unlock_irq(&ent->lock); - err = mlx5_core_create_mkey_cb(dev->mdev, &mr->mmkey, + err = mlx5_ib_create_mkey_cb(dev, &mr->mmkey, &dev->async_ctx, in, inlen, mr->out, sizeof(mr->out), - reg_mr_callback, &mr->cb_work); + &mr->cb_work); if (err) { spin_lock_irq(&ent->lock); ent->pending--; @@ -682,7 +722,6 @@ struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc) { struct mlx5_ib_dev *dev = to_mdev(pd->device); int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - struct mlx5_core_dev *mdev = dev->mdev; struct mlx5_ib_mr *mr; void *mkc; u32 *in; @@ -704,7 +743,7 @@ struct ib_mr *mlx5_ib_get_dma_mr(struct ib_pd *pd, int acc) MLX5_SET(mkc, mkc, length64, 1); set_mkc_access_pd_addr_fields(mkc, acc, 0, pd); - err = mlx5_core_create_mkey(mdev, &mr->mmkey, in, inlen); + err = mlx5_ib_create_mkey(dev, &mr->mmkey, in, inlen); if (err) goto err_in; @@ -1094,7 +1133,7 @@ static struct mlx5_ib_mr *reg_create(struct ib_mr *ibmr, struct ib_pd *pd, get_octo_len(virt_addr, length, page_shift)); } - err = mlx5_core_create_mkey(dev->mdev, &mr->mmkey, in, inlen); + err = mlx5_ib_create_mkey(dev, &mr->mmkey, in, inlen); if (err) { mlx5_ib_warn(dev, "create mkey failed\n"); goto err_2; @@ -1134,7 +1173,6 @@ static struct ib_mr *mlx5_ib_get_dm_mr(struct ib_pd *pd, u64 start_addr, { struct mlx5_ib_dev *dev = to_mdev(pd->device); int inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - struct mlx5_core_dev *mdev = dev->mdev; struct mlx5_ib_mr *mr; void *mkc; u32 *in; @@ -1157,7 +1195,7 @@ static struct ib_mr *mlx5_ib_get_dm_mr(struct ib_pd *pd, u64 start_addr, MLX5_SET64(mkc, mkc, len, length); set_mkc_access_pd_addr_fields(mkc, acc, start_addr, pd); - err = mlx5_core_create_mkey(mdev, &mr->mmkey, in, inlen); + err = mlx5_ib_create_mkey(dev, &mr->mmkey, in, inlen); if (err) goto err_in; @@ -1635,7 +1673,7 @@ static int _mlx5_alloc_mkey_descs(struct ib_pd *pd, struct mlx5_ib_mr *mr, mlx5_set_umr_free_mkey(pd, in, ndescs, access_mode, page_shift); - err = mlx5_core_create_mkey(dev->mdev, &mr->mmkey, in, inlen); + err = mlx5_ib_create_mkey(dev, &mr->mmkey, in, inlen); if (err) goto err_free_descs; @@ -1902,7 +1940,7 @@ struct ib_mw *mlx5_ib_alloc_mw(struct ib_pd *pd, enum ib_mw_type type, MLX5_SET(mkc, mkc, en_rinval, !!((type == IB_MW_TYPE_2))); MLX5_SET(mkc, mkc, qpn, 0xffffff); - err = mlx5_core_create_mkey(dev->mdev, &mw->mmkey, in, inlen); + err = mlx5_ib_create_mkey(dev, &mw->mmkey, in, inlen); if (err) goto free; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index f554cfddcf4e..6b38ec72215a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -1282,7 +1282,6 @@ static int mlx5_mdev_init(struct mlx5_core_dev *dev, int profile_idx) mutex_init(&priv->alloc_mutex); mutex_init(&priv->pgdir_mutex); INIT_LIST_HEAD(&priv->pgdir_list); - spin_lock_init(&priv->mkey_lock); priv->dbg_root = debugfs_create_dir(dev_name(dev->device), mlx5_debugfs_root); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 770d13bb4f20..51814d023efb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -49,14 +49,7 @@ int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev, int err; u8 key; - spin_lock_irq(&dev->priv.mkey_lock); - key = dev->priv.mkey_key++; - spin_unlock_irq(&dev->priv.mkey_lock); - mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); - MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY); - MLX5_SET(mkc, mkc, mkey_7_0, key); - mkey->key = key; if (callback) return mlx5_cmd_exec_cb(async_ctx, in, inlen, out, outlen, @@ -66,6 +59,7 @@ int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev, if (err) return err; + mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); mkey_index = MLX5_GET(create_mkey_out, lout, mkey_index); mkey->iova = MLX5_GET64(mkc, mkc, start_addr); mkey->size = MLX5_GET64(mkc, mkc, len); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index f2b4225ed650..e044703c056b 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -575,10 +575,6 @@ struct mlx5_priv { /* end: alloc staff */ struct dentry *dbg_root; - /* protect mkey key part */ - spinlock_t mkey_lock; - u8 mkey_key; - struct list_head dev_list; struct list_head ctx_list; spinlock_t ctx_lock; -- cgit v1.2.3 From a3cfdd3928113012d0f2c5353277f4e27878a663 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Tue, 10 Mar 2020 10:22:30 +0200 Subject: {IB,net}/mlx5: Move asynchronous mkey creation to mlx5_ib As mlx5_ib is the only user of the mlx5_core_create_mkey_cb, move the logic inside mlx5_ib and cleanup the code in mlx5_core. Signed-off-by: Michael Guralnik Signed-off-by: Leon Romanovsky --- drivers/infiniband/hw/mlx5/mr.c | 6 +++--- drivers/net/ethernet/mellanox/mlx5/core/mr.c | 22 +++------------------- include/linux/mlx5/driver.h | 6 ------ 3 files changed, 6 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 1b83d00e8ecd..8508af500972 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -81,10 +81,10 @@ mlx5_ib_create_mkey_cb(struct mlx5_ib_dev *dev, u32 *in, int inlen, u32 *out, int outlen, struct mlx5_async_work *context) { + MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY); assign_mkey_variant(dev, mkey, in); - return mlx5_core_create_mkey_cb(dev->mdev, mkey, async_ctx, - in, inlen, out, outlen, - create_mkey_callback, context); + return mlx5_cmd_exec_cb(async_ctx, in, inlen, out, outlen, + create_mkey_callback, context); } static void clean_mr(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mr.c b/drivers/net/ethernet/mellanox/mlx5/core/mr.c index 51814d023efb..fd3e6d217c3b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/mr.c @@ -36,12 +36,9 @@ #include #include "mlx5_core.h" -int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev, - struct mlx5_core_mkey *mkey, - struct mlx5_async_ctx *async_ctx, u32 *in, - int inlen, u32 *out, int outlen, - mlx5_async_cbk_t callback, - struct mlx5_async_work *context) +int mlx5_core_create_mkey(struct mlx5_core_dev *dev, + struct mlx5_core_mkey *mkey, + u32 *in, int inlen) { u32 lout[MLX5_ST_SZ_DW(create_mkey_out)] = {0}; u32 mkey_index; @@ -51,10 +48,6 @@ int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev, MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY); - if (callback) - return mlx5_cmd_exec_cb(async_ctx, in, inlen, out, outlen, - callback, context); - err = mlx5_cmd_exec(dev, in, inlen, lout, sizeof(lout)); if (err) return err; @@ -70,15 +63,6 @@ int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev, mkey_index, key, mkey->key); return 0; } -EXPORT_SYMBOL(mlx5_core_create_mkey_cb); - -int mlx5_core_create_mkey(struct mlx5_core_dev *dev, - struct mlx5_core_mkey *mkey, - u32 *in, int inlen) -{ - return mlx5_core_create_mkey_cb(dev, mkey, NULL, in, inlen, - NULL, 0, NULL, NULL); -} EXPORT_SYMBOL(mlx5_core_create_mkey); int mlx5_core_destroy_mkey(struct mlx5_core_dev *dev, diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index e044703c056b..1de78f001d26 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -943,12 +943,6 @@ struct mlx5_cmd_mailbox *mlx5_alloc_cmd_mailbox_chain(struct mlx5_core_dev *dev, gfp_t flags, int npages); void mlx5_free_cmd_mailbox_chain(struct mlx5_core_dev *dev, struct mlx5_cmd_mailbox *head); -int mlx5_core_create_mkey_cb(struct mlx5_core_dev *dev, - struct mlx5_core_mkey *mkey, - struct mlx5_async_ctx *async_ctx, u32 *in, - int inlen, u32 *out, int outlen, - mlx5_async_cbk_t callback, - struct mlx5_async_work *context); int mlx5_core_create_mkey(struct mlx5_core_dev *dev, struct mlx5_core_mkey *mkey, u32 *in, int inlen); -- cgit v1.2.3 From 4ea25785259a9e7a542a5e8ceb8b208ae6929739 Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Thu, 12 Mar 2020 15:06:18 -0500 Subject: ASoC: SOF: Make sof_ipc_ext_data enum more rigid It's a part of ABI interface, so enum value shouldn't change for example after removing some old enum code. Signed-off-by: Karol Trzcinski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200312200622.24477-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/info.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/sof/info.h b/include/sound/sof/info.h index 1c560144996c..cc3b50b6ae52 100644 --- a/include/sound/sof/info.h +++ b/include/sound/sof/info.h @@ -28,9 +28,9 @@ /* extended data types that can be appended onto end of sof_ipc_fw_ready */ enum sof_ipc_ext_data { - SOF_IPC_EXT_DMA_BUFFER = 0, - SOF_IPC_EXT_WINDOW, - SOF_IPC_EXT_CC_INFO, + SOF_IPC_EXT_DMA_BUFFER = 0, + SOF_IPC_EXT_WINDOW = 1, + SOF_IPC_EXT_CC_INFO = 2, }; /* FW version - SOF_IPC_GLB_VERSION */ -- cgit v1.2.3 From a6096f88a0b344d792606ebfaf1ef1ec2d7e0655 Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Thu, 12 Mar 2020 15:06:19 -0500 Subject: ASoC: SOF: Remove SOF_IPC_EXT_DMA_BUFFER This enum code, and what's more important, related structures is unused in whole source code, so it shouldn't be kept. Signed-off-by: Karol Trzcinski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200312200622.24477-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- include/sound/sof/info.h | 18 +----------------- sound/soc/sof/loader.c | 3 --- sound/soc/sof/sof-priv.h | 1 - 3 files changed, 1 insertion(+), 21 deletions(-) (limited to 'include') diff --git a/include/sound/sof/info.h b/include/sound/sof/info.h index cc3b50b6ae52..438a11fcf272 100644 --- a/include/sound/sof/info.h +++ b/include/sound/sof/info.h @@ -28,7 +28,7 @@ /* extended data types that can be appended onto end of sof_ipc_fw_ready */ enum sof_ipc_ext_data { - SOF_IPC_EXT_DMA_BUFFER = 0, + SOF_IPC_EXT_UNUSED = 0, SOF_IPC_EXT_WINDOW = 1, SOF_IPC_EXT_CC_INFO = 2, }; @@ -83,22 +83,6 @@ struct sof_ipc_ext_data_hdr { uint32_t type; /**< SOF_IPC_EXT_ */ } __packed; -struct sof_ipc_dma_buffer_elem { - struct sof_ipc_hdr hdr; - uint32_t type; /**< SOF_IPC_REGION_ */ - uint32_t id; /**< platform specific - used to map to host memory */ - struct sof_ipc_host_buffer buffer; -} __packed; - -/* extended data DMA buffers for IPC, trace and debug */ -struct sof_ipc_dma_buffer_data { - struct sof_ipc_ext_data_hdr ext_hdr; - uint32_t num_buffers; - - /* host files in buffer[n].buffer */ - struct sof_ipc_dma_buffer_elem buffer[]; -} __packed; - struct sof_ipc_window_elem { struct sof_ipc_hdr hdr; uint32_t type; /**< SOF_IPC_REGION_ */ diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index fc4ab51bacf4..67fc95ace42b 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -95,9 +95,6 @@ int snd_sof_fw_parse_ext_data(struct snd_sof_dev *sdev, u32 bar, u32 offset) /* process structure data */ switch (ext_hdr->type) { - case SOF_IPC_EXT_DMA_BUFFER: - ret = 0; - break; case SOF_IPC_EXT_WINDOW: ret = get_ext_windows(sdev, ext_hdr); break; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 5d16f668d16a..38dce54755a6 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -415,7 +415,6 @@ struct snd_sof_dev { u32 enabled_cores_mask; /* keep track of enabled cores */ /* FW configuration */ - struct sof_ipc_dma_buffer_data *info_buffer; struct sof_ipc_window *info_window; /* IPC timeouts in ms */ -- cgit v1.2.3 From 98868668367b24487c0b0b3298d7ca98409baf07 Mon Sep 17 00:00:00 2001 From: Andrii Nakryiko Date: Thu, 12 Mar 2020 17:21:28 -0700 Subject: bpf: Abstract away entire bpf_link clean up procedure Instead of requiring users to do three steps for cleaning up bpf_link, its anon_inode file, and unused fd, abstract that away into bpf_link_cleanup() helper. bpf_link_defunct() is removed, as it shouldn't be needed as an individual operation anymore. v1->v2: - keep bpf_link_cleanup() static for now (Daniel). Signed-off-by: Andrii Nakryiko Signed-off-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Link: https://lore.kernel.org/bpf/20200313002128.2028680-1-andriin@fb.com Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 1 - kernel/bpf/syscall.c | 18 +++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4ec835334a1f..c2f815e9f7d0 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -1075,7 +1075,6 @@ struct bpf_link_ops { void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, struct bpf_prog *prog); -void bpf_link_defunct(struct bpf_link *link); void bpf_link_inc(struct bpf_link *link); void bpf_link_put(struct bpf_link *link); int bpf_link_new_fd(struct bpf_link *link); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b2f73ecacced..85567a6ea5f9 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -2188,9 +2188,17 @@ void bpf_link_init(struct bpf_link *link, const struct bpf_link_ops *ops, link->prog = prog; } -void bpf_link_defunct(struct bpf_link *link) +/* Clean up bpf_link and corresponding anon_inode file and FD. After + * anon_inode is created, bpf_link can't be just kfree()'d due to deferred + * anon_inode's release() call. This helper manages marking bpf_link as + * defunct, releases anon_inode file and puts reserved FD. + */ +static void bpf_link_cleanup(struct bpf_link *link, struct file *link_file, + int link_fd) { link->prog = NULL; + fput(link_file); + put_unused_fd(link_fd); } void bpf_link_inc(struct bpf_link *link) @@ -2383,9 +2391,7 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog) err = bpf_trampoline_link_prog(prog); if (err) { - bpf_link_defunct(&link->link); - fput(link_file); - put_unused_fd(link_fd); + bpf_link_cleanup(&link->link, link_file, link_fd); goto out_put_prog; } @@ -2498,9 +2504,7 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr) err = bpf_probe_register(link->btp, prog); if (err) { - bpf_link_defunct(&link->link); - fput(link_file); - put_unused_fd(link_fd); + bpf_link_cleanup(&link->link, link_file, link_fd); goto out_put_btp; } -- cgit v1.2.3 From 6a64037d4bf252bb8cf13917320c8e001da8997a Mon Sep 17 00:00:00 2001 From: Björn Töpel Date: Thu, 12 Mar 2020 20:55:57 +0100 Subject: bpf: Add bpf_trampoline_ name prefix for DECLARE_BPF_DISPATCHER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding bpf_trampoline_ name prefix for DECLARE_BPF_DISPATCHER, so all the dispatchers have the common name prefix. And also a small '_' cleanup for bpf_dispatcher_nopfunc function name. Signed-off-by: Björn Töpel Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200312195610.346362-3-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 21 +++++++++++---------- include/linux/filter.h | 9 ++++----- net/core/filter.c | 5 ++--- 3 files changed, 17 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index c2f815e9f7d0..fe1f8b075378 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -522,7 +522,7 @@ struct bpf_dispatcher { u32 image_off; }; -static __always_inline unsigned int bpf_dispatcher_nopfunc( +static __always_inline unsigned int bpf_dispatcher_nop_func( const void *ctx, const struct bpf_insn *insnsi, unsigned int (*bpf_func)(const void *, @@ -537,7 +537,7 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog); void bpf_trampoline_put(struct bpf_trampoline *tr); #define BPF_DISPATCHER_INIT(name) { \ .mutex = __MUTEX_INITIALIZER(name.mutex), \ - .func = &name##func, \ + .func = &name##_func, \ .progs = {}, \ .num_progs = 0, \ .image = NULL, \ @@ -545,7 +545,7 @@ void bpf_trampoline_put(struct bpf_trampoline *tr); } #define DEFINE_BPF_DISPATCHER(name) \ - noinline unsigned int name##func( \ + noinline unsigned int bpf_dispatcher_##name##_func( \ const void *ctx, \ const struct bpf_insn *insnsi, \ unsigned int (*bpf_func)(const void *, \ @@ -553,17 +553,18 @@ void bpf_trampoline_put(struct bpf_trampoline *tr); { \ return bpf_func(ctx, insnsi); \ } \ - EXPORT_SYMBOL(name##func); \ - struct bpf_dispatcher name = BPF_DISPATCHER_INIT(name); + EXPORT_SYMBOL(bpf_dispatcher_##name##_func); \ + struct bpf_dispatcher bpf_dispatcher_##name = \ + BPF_DISPATCHER_INIT(bpf_dispatcher_##name); #define DECLARE_BPF_DISPATCHER(name) \ - unsigned int name##func( \ + unsigned int bpf_dispatcher_##name##_func( \ const void *ctx, \ const struct bpf_insn *insnsi, \ unsigned int (*bpf_func)(const void *, \ const struct bpf_insn *)); \ - extern struct bpf_dispatcher name; -#define BPF_DISPATCHER_FUNC(name) name##func -#define BPF_DISPATCHER_PTR(name) (&name) + extern struct bpf_dispatcher bpf_dispatcher_##name; +#define BPF_DISPATCHER_FUNC(name) bpf_dispatcher_##name##_func +#define BPF_DISPATCHER_PTR(name) (&bpf_dispatcher_##name) void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, struct bpf_prog *to); struct bpf_image { @@ -589,7 +590,7 @@ static inline int bpf_trampoline_unlink_prog(struct bpf_prog *prog) static inline void bpf_trampoline_put(struct bpf_trampoline *tr) {} #define DEFINE_BPF_DISPATCHER(name) #define DECLARE_BPF_DISPATCHER(name) -#define BPF_DISPATCHER_FUNC(name) bpf_dispatcher_nopfunc +#define BPF_DISPATCHER_FUNC(name) bpf_dispatcher_nop_func #define BPF_DISPATCHER_PTR(name) NULL static inline void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, diff --git a/include/linux/filter.h b/include/linux/filter.h index 43b5e455d2f5..6249679275b3 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -577,7 +577,7 @@ DECLARE_STATIC_KEY_FALSE(bpf_stats_enabled_key); ret; }) #define BPF_PROG_RUN(prog, ctx) \ - __BPF_PROG_RUN(prog, ctx, bpf_dispatcher_nopfunc) + __BPF_PROG_RUN(prog, ctx, bpf_dispatcher_nop_func) /* * Use in preemptible and therefore migratable context to make sure that @@ -596,7 +596,7 @@ static inline u32 bpf_prog_run_pin_on_cpu(const struct bpf_prog *prog, u32 ret; migrate_disable(); - ret = __BPF_PROG_RUN(prog, ctx, bpf_dispatcher_nopfunc); + ret = __BPF_PROG_RUN(prog, ctx, bpf_dispatcher_nop_func); migrate_enable(); return ret; } @@ -722,7 +722,7 @@ static inline u32 bpf_prog_run_clear_cb(const struct bpf_prog *prog, return res; } -DECLARE_BPF_DISPATCHER(bpf_dispatcher_xdp) +DECLARE_BPF_DISPATCHER(xdp) static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, struct xdp_buff *xdp) @@ -733,8 +733,7 @@ static __always_inline u32 bpf_prog_run_xdp(const struct bpf_prog *prog, * already takes rcu_read_lock() when fetching the program, so * it's not necessary here anymore. */ - return __BPF_PROG_RUN(prog, xdp, - BPF_DISPATCHER_FUNC(bpf_dispatcher_xdp)); + return __BPF_PROG_RUN(prog, xdp, BPF_DISPATCHER_FUNC(xdp)); } void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog); diff --git a/net/core/filter.c b/net/core/filter.c index 22219544410f..96350a743539 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -8859,10 +8859,9 @@ const struct bpf_prog_ops sk_reuseport_prog_ops = { }; #endif /* CONFIG_INET */ -DEFINE_BPF_DISPATCHER(bpf_dispatcher_xdp) +DEFINE_BPF_DISPATCHER(xdp) void bpf_prog_change_xdp(struct bpf_prog *prev_prog, struct bpf_prog *prog) { - bpf_dispatcher_change_prog(BPF_DISPATCHER_PTR(bpf_dispatcher_xdp), - prev_prog, prog); + bpf_dispatcher_change_prog(BPF_DISPATCHER_PTR(xdp), prev_prog, prog); } -- cgit v1.2.3 From 535911c80ad4f5801700e9d827a1985bbff41519 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:55:58 +0100 Subject: bpf: Add struct bpf_ksym Adding 'struct bpf_ksym' object that will carry the kallsym information for bpf symbol. Adding the start and end address to begin with. It will be used by bpf_prog, bpf_trampoline, bpf_dispatcher objects. The symbol_start/symbol_end values were originally used to sort bpf_prog objects. For the address displayed in /proc/kallsyms we are using prog->bpf_func value. I'm using the bpf_func value for program symbol start instead of the symbol_start, because it makes no difference for sorting bpf_prog objects and we can use it directly as an address to display it in /proc/kallsyms. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200312195610.346362-4-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 6 ++++++ kernel/bpf/core.c | 28 ++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index fe1f8b075378..6ca3d5c8ccf3 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -471,6 +471,11 @@ int arch_prepare_bpf_trampoline(void *image, void *image_end, u64 notrace __bpf_prog_enter(void); void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start); +struct bpf_ksym { + unsigned long start; + unsigned long end; +}; + enum bpf_tramp_prog_type { BPF_TRAMP_FENTRY, BPF_TRAMP_FEXIT, @@ -653,6 +658,7 @@ struct bpf_prog_aux { u32 size_poke_tab; struct latch_tree_node ksym_tnode; struct list_head ksym_lnode; + struct bpf_ksym ksym; const struct bpf_prog_ops *ops; struct bpf_map **used_maps; struct bpf_prog *prog; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 0f9ca46e1978..e587d6306d7c 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -523,18 +523,16 @@ int bpf_jit_kallsyms __read_mostly = IS_BUILTIN(CONFIG_BPF_JIT_DEFAULT_ON); int bpf_jit_harden __read_mostly; long bpf_jit_limit __read_mostly; -static __always_inline void -bpf_get_prog_addr_region(const struct bpf_prog *prog, - unsigned long *symbol_start, - unsigned long *symbol_end) +static void +bpf_prog_ksym_set_addr(struct bpf_prog *prog) { const struct bpf_binary_header *hdr = bpf_jit_binary_hdr(prog); unsigned long addr = (unsigned long)hdr; WARN_ON_ONCE(!bpf_prog_ebpf_jited(prog)); - *symbol_start = addr; - *symbol_end = addr + hdr->pages * PAGE_SIZE; + prog->aux->ksym.start = (unsigned long) prog->bpf_func; + prog->aux->ksym.end = addr + hdr->pages * PAGE_SIZE; } void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) @@ -575,13 +573,10 @@ void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) static __always_inline unsigned long bpf_get_prog_addr_start(struct latch_tree_node *n) { - unsigned long symbol_start, symbol_end; const struct bpf_prog_aux *aux; aux = container_of(n, struct bpf_prog_aux, ksym_tnode); - bpf_get_prog_addr_region(aux->prog, &symbol_start, &symbol_end); - - return symbol_start; + return aux->ksym.start; } static __always_inline bool bpf_tree_less(struct latch_tree_node *a, @@ -593,15 +588,13 @@ static __always_inline bool bpf_tree_less(struct latch_tree_node *a, static __always_inline int bpf_tree_comp(void *key, struct latch_tree_node *n) { unsigned long val = (unsigned long)key; - unsigned long symbol_start, symbol_end; const struct bpf_prog_aux *aux; aux = container_of(n, struct bpf_prog_aux, ksym_tnode); - bpf_get_prog_addr_region(aux->prog, &symbol_start, &symbol_end); - if (val < symbol_start) + if (val < aux->ksym.start) return -1; - if (val >= symbol_end) + if (val >= aux->ksym.end) return 1; return 0; @@ -649,6 +642,8 @@ void bpf_prog_kallsyms_add(struct bpf_prog *fp) !capable(CAP_SYS_ADMIN)) return; + bpf_prog_ksym_set_addr(fp); + spin_lock_bh(&bpf_lock); bpf_prog_ksym_node_add(fp->aux); spin_unlock_bh(&bpf_lock); @@ -677,14 +672,15 @@ static struct bpf_prog *bpf_prog_kallsyms_find(unsigned long addr) const char *__bpf_address_lookup(unsigned long addr, unsigned long *size, unsigned long *off, char *sym) { - unsigned long symbol_start, symbol_end; struct bpf_prog *prog; char *ret = NULL; rcu_read_lock(); prog = bpf_prog_kallsyms_find(addr); if (prog) { - bpf_get_prog_addr_region(prog, &symbol_start, &symbol_end); + unsigned long symbol_start = prog->aux->ksym.start; + unsigned long symbol_end = prog->aux->ksym.end; + bpf_get_prog_name(prog, sym); ret = sym; -- cgit v1.2.3 From bfea9a8574f34597581f74f792d044d38497b775 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:55:59 +0100 Subject: bpf: Add name to struct bpf_ksym Adding name to 'struct bpf_ksym' object to carry the name of the symbol for bpf_prog, bpf_trampoline, bpf_dispatcher objects. The current benefit is that name is now generated only when the symbol is added to the list, so we don't need to generate it every time it's accessed. The future benefit is that we will have all the bpf objects symbols represented by struct bpf_ksym. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200312195610.346362-5-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 2 ++ include/linux/filter.h | 6 ------ kernel/bpf/core.c | 9 ++++++--- kernel/events/core.c | 9 ++++----- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 6ca3d5c8ccf3..047b44deb3c5 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -18,6 +18,7 @@ #include #include #include +#include struct bpf_verifier_env; struct bpf_verifier_log; @@ -474,6 +475,7 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start); struct bpf_ksym { unsigned long start; unsigned long end; + char name[KSYM_NAME_LEN]; }; enum bpf_tramp_prog_type { diff --git a/include/linux/filter.h b/include/linux/filter.h index 6249679275b3..9b5aa5c483cc 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -1083,7 +1083,6 @@ bpf_address_lookup(unsigned long addr, unsigned long *size, void bpf_prog_kallsyms_add(struct bpf_prog *fp); void bpf_prog_kallsyms_del(struct bpf_prog *fp); -void bpf_get_prog_name(const struct bpf_prog *prog, char *sym); #else /* CONFIG_BPF_JIT */ @@ -1152,11 +1151,6 @@ static inline void bpf_prog_kallsyms_del(struct bpf_prog *fp) { } -static inline void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) -{ - sym[0] = '\0'; -} - #endif /* CONFIG_BPF_JIT */ void bpf_prog_kallsyms_del_all(struct bpf_prog *fp); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index e587d6306d7c..f6800c2d4b01 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -535,8 +535,10 @@ bpf_prog_ksym_set_addr(struct bpf_prog *prog) prog->aux->ksym.end = addr + hdr->pages * PAGE_SIZE; } -void bpf_get_prog_name(const struct bpf_prog *prog, char *sym) +static void +bpf_prog_ksym_set_name(struct bpf_prog *prog) { + char *sym = prog->aux->ksym.name; const char *end = sym + KSYM_NAME_LEN; const struct btf_type *type; const char *func_name; @@ -643,6 +645,7 @@ void bpf_prog_kallsyms_add(struct bpf_prog *fp) return; bpf_prog_ksym_set_addr(fp); + bpf_prog_ksym_set_name(fp); spin_lock_bh(&bpf_lock); bpf_prog_ksym_node_add(fp->aux); @@ -681,7 +684,7 @@ const char *__bpf_address_lookup(unsigned long addr, unsigned long *size, unsigned long symbol_start = prog->aux->ksym.start; unsigned long symbol_end = prog->aux->ksym.end; - bpf_get_prog_name(prog, sym); + strncpy(sym, prog->aux->ksym.name, KSYM_NAME_LEN); ret = sym; if (size) @@ -738,7 +741,7 @@ int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type, if (it++ != symnum) continue; - bpf_get_prog_name(aux->prog, sym); + strncpy(sym, aux->ksym.name, KSYM_NAME_LEN); *value = (unsigned long)aux->prog->bpf_func; *type = BPF_SYM_ELF_TYPE; diff --git a/kernel/events/core.c b/kernel/events/core.c index bbdfac0182f4..9b89ef176247 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8255,23 +8255,22 @@ static void perf_event_bpf_emit_ksymbols(struct bpf_prog *prog, enum perf_bpf_event_type type) { bool unregister = type == PERF_BPF_EVENT_PROG_UNLOAD; - char sym[KSYM_NAME_LEN]; int i; if (prog->aux->func_cnt == 0) { - bpf_get_prog_name(prog, sym); perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, (u64)(unsigned long)prog->bpf_func, - prog->jited_len, unregister, sym); + prog->jited_len, unregister, + prog->aux->ksym.name); } else { for (i = 0; i < prog->aux->func_cnt; i++) { struct bpf_prog *subprog = prog->aux->func[i]; - bpf_get_prog_name(subprog, sym); perf_event_ksymbol( PERF_RECORD_KSYMBOL_TYPE_BPF, (u64)(unsigned long)subprog->bpf_func, - subprog->jited_len, unregister, sym); + subprog->jited_len, unregister, + prog->aux->ksym.name); } } } -- cgit v1.2.3 From ecb60d1c670e9b205197d8e4381b19e77bc2d834 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:00 +0100 Subject: bpf: Move lnode list node to struct bpf_ksym Adding lnode list node to 'struct bpf_ksym' object, so the struct bpf_ksym itself can be chained and used in other objects like bpf_trampoline and bpf_dispatcher. Changing iterator to bpf_ksym in bpf_get_kallsym function. The ksym->start is holding the prog->bpf_func value, so it's ok to use it as value in bpf_get_kallsym. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Acked-by: Song Liu Link: https://lore.kernel.org/bpf/20200312195610.346362-6-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 2 +- kernel/bpf/core.c | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 047b44deb3c5..4fad2fa4135c 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -476,6 +476,7 @@ struct bpf_ksym { unsigned long start; unsigned long end; char name[KSYM_NAME_LEN]; + struct list_head lnode; }; enum bpf_tramp_prog_type { @@ -659,7 +660,6 @@ struct bpf_prog_aux { struct bpf_jit_poke_descriptor *poke_tab; u32 size_poke_tab; struct latch_tree_node ksym_tnode; - struct list_head ksym_lnode; struct bpf_ksym ksym; const struct bpf_prog_ops *ops; struct bpf_map **used_maps; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index f6800c2d4b01..5eb5d5bb7a95 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -97,7 +97,7 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag fp->aux->prog = fp; fp->jit_requested = ebpf_jit_enabled(); - INIT_LIST_HEAD_RCU(&fp->aux->ksym_lnode); + INIT_LIST_HEAD_RCU(&fp->aux->ksym.lnode); return fp; } @@ -613,18 +613,18 @@ static struct latch_tree_root bpf_tree __cacheline_aligned; static void bpf_prog_ksym_node_add(struct bpf_prog_aux *aux) { - WARN_ON_ONCE(!list_empty(&aux->ksym_lnode)); - list_add_tail_rcu(&aux->ksym_lnode, &bpf_kallsyms); + WARN_ON_ONCE(!list_empty(&aux->ksym.lnode)); + list_add_tail_rcu(&aux->ksym.lnode, &bpf_kallsyms); latch_tree_insert(&aux->ksym_tnode, &bpf_tree, &bpf_tree_ops); } static void bpf_prog_ksym_node_del(struct bpf_prog_aux *aux) { - if (list_empty(&aux->ksym_lnode)) + if (list_empty(&aux->ksym.lnode)) return; latch_tree_erase(&aux->ksym_tnode, &bpf_tree, &bpf_tree_ops); - list_del_rcu(&aux->ksym_lnode); + list_del_rcu(&aux->ksym.lnode); } static bool bpf_prog_kallsyms_candidate(const struct bpf_prog *fp) @@ -634,8 +634,8 @@ static bool bpf_prog_kallsyms_candidate(const struct bpf_prog *fp) static bool bpf_prog_kallsyms_verify_off(const struct bpf_prog *fp) { - return list_empty(&fp->aux->ksym_lnode) || - fp->aux->ksym_lnode.prev == LIST_POISON2; + return list_empty(&fp->aux->ksym.lnode) || + fp->aux->ksym.lnode.prev == LIST_POISON2; } void bpf_prog_kallsyms_add(struct bpf_prog *fp) @@ -729,7 +729,7 @@ out: int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type, char *sym) { - struct bpf_prog_aux *aux; + struct bpf_ksym *ksym; unsigned int it = 0; int ret = -ERANGE; @@ -737,13 +737,13 @@ int bpf_get_kallsym(unsigned int symnum, unsigned long *value, char *type, return ret; rcu_read_lock(); - list_for_each_entry_rcu(aux, &bpf_kallsyms, ksym_lnode) { + list_for_each_entry_rcu(ksym, &bpf_kallsyms, lnode) { if (it++ != symnum) continue; - strncpy(sym, aux->ksym.name, KSYM_NAME_LEN); + strncpy(sym, ksym->name, KSYM_NAME_LEN); - *value = (unsigned long)aux->prog->bpf_func; + *value = ksym->start; *type = BPF_SYM_ELF_TYPE; ret = 0; -- cgit v1.2.3 From ca4424c920f574b7246ff1b6d83cfdfd709e42c8 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:01 +0100 Subject: bpf: Move ksym_tnode to bpf_ksym Moving ksym_tnode list node to 'struct bpf_ksym' object, so the symbol itself can be chained and used in other objects like bpf_trampoline and bpf_dispatcher. We need bpf_ksym object to be linked both in bpf_kallsyms via lnode for /proc/kallsyms and in bpf_tree via tnode for bpf address lookup functions like __bpf_address_lookup or bpf_prog_kallsyms_find. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200312195610.346362-7-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 2 +- kernel/bpf/core.c | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4fad2fa4135c..68d66b0078df 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -477,6 +477,7 @@ struct bpf_ksym { unsigned long end; char name[KSYM_NAME_LEN]; struct list_head lnode; + struct latch_tree_node tnode; }; enum bpf_tramp_prog_type { @@ -659,7 +660,6 @@ struct bpf_prog_aux { void *jit_data; /* JIT specific data. arch dependent */ struct bpf_jit_poke_descriptor *poke_tab; u32 size_poke_tab; - struct latch_tree_node ksym_tnode; struct bpf_ksym ksym; const struct bpf_prog_ops *ops; struct bpf_map **used_maps; diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 5eb5d5bb7a95..ab1846c34167 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -572,31 +572,27 @@ bpf_prog_ksym_set_name(struct bpf_prog *prog) *sym = 0; } -static __always_inline unsigned long -bpf_get_prog_addr_start(struct latch_tree_node *n) +static unsigned long bpf_get_ksym_start(struct latch_tree_node *n) { - const struct bpf_prog_aux *aux; - - aux = container_of(n, struct bpf_prog_aux, ksym_tnode); - return aux->ksym.start; + return container_of(n, struct bpf_ksym, tnode)->start; } static __always_inline bool bpf_tree_less(struct latch_tree_node *a, struct latch_tree_node *b) { - return bpf_get_prog_addr_start(a) < bpf_get_prog_addr_start(b); + return bpf_get_ksym_start(a) < bpf_get_ksym_start(b); } static __always_inline int bpf_tree_comp(void *key, struct latch_tree_node *n) { unsigned long val = (unsigned long)key; - const struct bpf_prog_aux *aux; + const struct bpf_ksym *ksym; - aux = container_of(n, struct bpf_prog_aux, ksym_tnode); + ksym = container_of(n, struct bpf_ksym, tnode); - if (val < aux->ksym.start) + if (val < ksym->start) return -1; - if (val >= aux->ksym.end) + if (val >= ksym->end) return 1; return 0; @@ -615,7 +611,7 @@ static void bpf_prog_ksym_node_add(struct bpf_prog_aux *aux) { WARN_ON_ONCE(!list_empty(&aux->ksym.lnode)); list_add_tail_rcu(&aux->ksym.lnode, &bpf_kallsyms); - latch_tree_insert(&aux->ksym_tnode, &bpf_tree, &bpf_tree_ops); + latch_tree_insert(&aux->ksym.tnode, &bpf_tree, &bpf_tree_ops); } static void bpf_prog_ksym_node_del(struct bpf_prog_aux *aux) @@ -623,7 +619,7 @@ static void bpf_prog_ksym_node_del(struct bpf_prog_aux *aux) if (list_empty(&aux->ksym.lnode)) return; - latch_tree_erase(&aux->ksym_tnode, &bpf_tree, &bpf_tree_ops); + latch_tree_erase(&aux->ksym.tnode, &bpf_tree, &bpf_tree_ops); list_del_rcu(&aux->ksym.lnode); } @@ -668,7 +664,7 @@ static struct bpf_prog *bpf_prog_kallsyms_find(unsigned long addr) n = latch_tree_find((void *)addr, &bpf_tree, &bpf_tree_ops); return n ? - container_of(n, struct bpf_prog_aux, ksym_tnode)->prog : + container_of(n, struct bpf_prog_aux, ksym.tnode)->prog : NULL; } -- cgit v1.2.3 From cbd76f8d5ac9c4e99c4ffe5e39a1e907cdf5a76f Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:03 +0100 Subject: bpf: Add prog flag to struct bpf_ksym object Adding 'prog' bool flag to 'struct bpf_ksym' to mark that this object belongs to bpf_prog object. This change allows having bpf_prog objects together with other types (trampolines and dispatchers) in the single bpf_tree. It's used when searching for bpf_prog exception tables by the bpf_prog_ksym_find function, where we need to get the bpf_prog pointer. >From now we can safely add bpf_ksym support for trampoline or dispatcher objects, because we can differentiate them from bpf_prog objects. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200312195610.346362-9-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 1 + kernel/bpf/core.c | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 68d66b0078df..a0cef664c1a9 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -478,6 +478,7 @@ struct bpf_ksym { char name[KSYM_NAME_LEN]; struct list_head lnode; struct latch_tree_node tnode; + bool prog; }; enum bpf_tramp_prog_type { diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index cd380f7f015c..7516cbc65996 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -642,6 +642,7 @@ void bpf_prog_kallsyms_add(struct bpf_prog *fp) bpf_prog_ksym_set_addr(fp); bpf_prog_ksym_set_name(fp); + fp->aux->ksym.prog = true; spin_lock_bh(&bpf_lock); bpf_prog_ksym_node_add(fp->aux); @@ -658,16 +659,6 @@ void bpf_prog_kallsyms_del(struct bpf_prog *fp) spin_unlock_bh(&bpf_lock); } -static struct bpf_prog *bpf_prog_kallsyms_find(unsigned long addr) -{ - struct latch_tree_node *n; - - n = latch_tree_find((void *)addr, &bpf_tree, &bpf_tree_ops); - return n ? - container_of(n, struct bpf_prog_aux, ksym.tnode)->prog : - NULL; -} - static struct bpf_ksym *bpf_ksym_find(unsigned long addr) { struct latch_tree_node *n; @@ -712,13 +703,22 @@ bool is_bpf_text_address(unsigned long addr) return ret; } +static struct bpf_prog *bpf_prog_ksym_find(unsigned long addr) +{ + struct bpf_ksym *ksym = bpf_ksym_find(addr); + + return ksym && ksym->prog ? + container_of(ksym, struct bpf_prog_aux, ksym)->prog : + NULL; +} + const struct exception_table_entry *search_bpf_extables(unsigned long addr) { const struct exception_table_entry *e = NULL; struct bpf_prog *prog; rcu_read_lock(); - prog = bpf_prog_kallsyms_find(addr); + prog = bpf_prog_ksym_find(addr); if (!prog) goto out; if (!prog->aux->num_exentries) -- cgit v1.2.3 From dba122fb5e122e8e07e2f11cdebc10ba4f425cf7 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:04 +0100 Subject: bpf: Add bpf_ksym_add/del functions Separating /proc/kallsyms add/del code and adding bpf_ksym_add/del functions for that. Moving bpf_prog_ksym_node_add/del functions to __bpf_ksym_add/del and changing their argument to 'struct bpf_ksym' object. This way we can call them for other bpf objects types like trampoline and dispatcher. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200312195610.346362-10-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 3 +++ kernel/bpf/core.c | 33 +++++++++++++++++++-------------- 2 files changed, 22 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a0cef664c1a9..ec1de88b8487 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -584,6 +584,9 @@ struct bpf_image { #define BPF_IMAGE_SIZE (PAGE_SIZE - sizeof(struct bpf_image)) bool is_bpf_image_address(unsigned long address); void *bpf_image_alloc(void); +/* Called only from JIT-enabled code, so there's no need for stubs. */ +void bpf_ksym_add(struct bpf_ksym *ksym); +void bpf_ksym_del(struct bpf_ksym *ksym); #else static inline struct bpf_trampoline *bpf_trampoline_lookup(u64 key) { diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 7516cbc65996..914f3463aa41 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -607,20 +607,29 @@ static DEFINE_SPINLOCK(bpf_lock); static LIST_HEAD(bpf_kallsyms); static struct latch_tree_root bpf_tree __cacheline_aligned; -static void bpf_prog_ksym_node_add(struct bpf_prog_aux *aux) +void bpf_ksym_add(struct bpf_ksym *ksym) { - WARN_ON_ONCE(!list_empty(&aux->ksym.lnode)); - list_add_tail_rcu(&aux->ksym.lnode, &bpf_kallsyms); - latch_tree_insert(&aux->ksym.tnode, &bpf_tree, &bpf_tree_ops); + spin_lock_bh(&bpf_lock); + WARN_ON_ONCE(!list_empty(&ksym->lnode)); + list_add_tail_rcu(&ksym->lnode, &bpf_kallsyms); + latch_tree_insert(&ksym->tnode, &bpf_tree, &bpf_tree_ops); + spin_unlock_bh(&bpf_lock); } -static void bpf_prog_ksym_node_del(struct bpf_prog_aux *aux) +static void __bpf_ksym_del(struct bpf_ksym *ksym) { - if (list_empty(&aux->ksym.lnode)) + if (list_empty(&ksym->lnode)) return; - latch_tree_erase(&aux->ksym.tnode, &bpf_tree, &bpf_tree_ops); - list_del_rcu(&aux->ksym.lnode); + latch_tree_erase(&ksym->tnode, &bpf_tree, &bpf_tree_ops); + list_del_rcu(&ksym->lnode); +} + +void bpf_ksym_del(struct bpf_ksym *ksym) +{ + spin_lock_bh(&bpf_lock); + __bpf_ksym_del(ksym); + spin_unlock_bh(&bpf_lock); } static bool bpf_prog_kallsyms_candidate(const struct bpf_prog *fp) @@ -644,9 +653,7 @@ void bpf_prog_kallsyms_add(struct bpf_prog *fp) bpf_prog_ksym_set_name(fp); fp->aux->ksym.prog = true; - spin_lock_bh(&bpf_lock); - bpf_prog_ksym_node_add(fp->aux); - spin_unlock_bh(&bpf_lock); + bpf_ksym_add(&fp->aux->ksym); } void bpf_prog_kallsyms_del(struct bpf_prog *fp) @@ -654,9 +661,7 @@ void bpf_prog_kallsyms_del(struct bpf_prog *fp) if (!bpf_prog_kallsyms_candidate(fp)) return; - spin_lock_bh(&bpf_lock); - bpf_prog_ksym_node_del(fp->aux); - spin_unlock_bh(&bpf_lock); + bpf_ksym_del(&fp->aux->ksym); } static struct bpf_ksym *bpf_ksym_find(unsigned long addr) -- cgit v1.2.3 From a108f7dcfa010e3da825af90d77ac0a6a0240992 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:05 +0100 Subject: bpf: Add trampolines to kallsyms Adding trampolines to kallsyms. It's displayed as bpf_trampoline_ [bpf] where ID is the BTF id of the trampoline function. Adding bpf_image_ksym_add/del functions that setup the start/end values and call KSYMBOL perf events handlers. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200312195610.346362-11-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 3 +++ kernel/bpf/trampoline.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index ec1de88b8487..083860be1944 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -513,6 +513,7 @@ struct bpf_trampoline { /* Executable image of trampoline */ void *image; u64 selector; + struct bpf_ksym ksym; }; #define BPF_DISPATCHER_MAX 48 /* Fits in 2048B */ @@ -585,6 +586,8 @@ struct bpf_image { bool is_bpf_image_address(unsigned long address); void *bpf_image_alloc(void); /* Called only from JIT-enabled code, so there's no need for stubs. */ +void bpf_image_ksym_add(void *data, struct bpf_ksym *ksym); +void bpf_image_ksym_del(struct bpf_ksym *ksym); void bpf_ksym_add(struct bpf_ksym *ksym); void bpf_ksym_del(struct bpf_ksym *ksym); #else diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 221a17af1f81..36549c9afec4 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -5,6 +5,7 @@ #include #include #include +#include /* dummy _ops. The verifier will operate on target program's ops. */ const struct bpf_verifier_ops bpf_extension_verifier_ops = { @@ -96,6 +97,30 @@ bool is_bpf_image_address(unsigned long addr) return ret; } +void bpf_image_ksym_add(void *data, struct bpf_ksym *ksym) +{ + ksym->start = (unsigned long) data; + ksym->end = ksym->start + BPF_IMAGE_SIZE; + bpf_ksym_add(ksym); + perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start, + BPF_IMAGE_SIZE, false, ksym->name); +} + +void bpf_image_ksym_del(struct bpf_ksym *ksym) +{ + bpf_ksym_del(ksym); + perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start, + BPF_IMAGE_SIZE, true, ksym->name); +} + +static void bpf_trampoline_ksym_add(struct bpf_trampoline *tr) +{ + struct bpf_ksym *ksym = &tr->ksym; + + snprintf(ksym->name, KSYM_NAME_LEN, "bpf_trampoline_%llu", tr->key); + bpf_image_ksym_add(tr->image, ksym); +} + struct bpf_trampoline *bpf_trampoline_lookup(u64 key) { struct bpf_trampoline *tr; @@ -131,6 +156,8 @@ struct bpf_trampoline *bpf_trampoline_lookup(u64 key) for (i = 0; i < BPF_TRAMP_MAX; i++) INIT_HLIST_HEAD(&tr->progs_hlist[i]); tr->image = image; + INIT_LIST_HEAD_RCU(&tr->ksym.lnode); + bpf_trampoline_ksym_add(tr); out: mutex_unlock(&trampoline_mutex); return tr; @@ -368,6 +395,7 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) goto out; if (WARN_ON_ONCE(!hlist_empty(&tr->progs_hlist[BPF_TRAMP_FEXIT]))) goto out; + bpf_image_ksym_del(&tr->ksym); image = container_of(tr->image, struct bpf_image, data); latch_tree_erase(&image->tnode, &image_tree, &image_tree_ops); /* wait for tasks to get out of trampoline before freeing it */ -- cgit v1.2.3 From 517b75e44c7be9c776aa5f7beaa85baff3868f80 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:06 +0100 Subject: bpf: Add dispatchers to kallsyms Adding dispatchers to kallsyms. It's displayed as bpf_dispatcher_ where NAME is the name of dispatcher. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200312195610.346362-12-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 19 ++++++++++++------- kernel/bpf/dispatcher.c | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 083860be1944..86cacb54ba23 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -531,6 +531,7 @@ struct bpf_dispatcher { int num_progs; void *image; u32 image_off; + struct bpf_ksym ksym; }; static __always_inline unsigned int bpf_dispatcher_nop_func( @@ -546,13 +547,17 @@ struct bpf_trampoline *bpf_trampoline_lookup(u64 key); int bpf_trampoline_link_prog(struct bpf_prog *prog); int bpf_trampoline_unlink_prog(struct bpf_prog *prog); void bpf_trampoline_put(struct bpf_trampoline *tr); -#define BPF_DISPATCHER_INIT(name) { \ - .mutex = __MUTEX_INITIALIZER(name.mutex), \ - .func = &name##_func, \ - .progs = {}, \ - .num_progs = 0, \ - .image = NULL, \ - .image_off = 0 \ +#define BPF_DISPATCHER_INIT(_name) { \ + .mutex = __MUTEX_INITIALIZER(_name.mutex), \ + .func = &_name##_func, \ + .progs = {}, \ + .num_progs = 0, \ + .image = NULL, \ + .image_off = 0, \ + .ksym = { \ + .name = #_name, \ + .lnode = LIST_HEAD_INIT(_name.ksym.lnode), \ + }, \ } #define DEFINE_BPF_DISPATCHER(name) \ diff --git a/kernel/bpf/dispatcher.c b/kernel/bpf/dispatcher.c index b3e5b214fed8..a2679bae9e73 100644 --- a/kernel/bpf/dispatcher.c +++ b/kernel/bpf/dispatcher.c @@ -143,6 +143,7 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, d->image = bpf_image_alloc(); if (!d->image) goto out; + bpf_image_ksym_add(d->image, &d->ksym); } prev_num_progs = d->num_progs; -- cgit v1.2.3 From 7ac88eba185b4d0e06a71678e54bc092edcd3af3 Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 12 Mar 2020 20:56:07 +0100 Subject: bpf: Remove bpf_image tree Now that we have all the objects (bpf_prog, bpf_trampoline, bpf_dispatcher) linked in bpf_tree, there's no need to have separate bpf_image tree for images. Reverting the bpf_image tree together with struct bpf_image, because it's no longer needed. Also removing bpf_image_alloc function and adding the original bpf_jit_alloc_exec_page interface instead. The kernel_text_address function can now rely only on is_bpf_text_address, because it checks the bpf_tree that contains all the objects. Keeping bpf_image_ksym_add and bpf_image_ksym_del because they are useful wrappers with perf's ksymbol interface calls. Signed-off-by: Jiri Olsa Signed-off-by: Alexei Starovoitov Link: https://lore.kernel.org/bpf/20200312195610.346362-13-jolsa@kernel.org Signed-off-by: Alexei Starovoitov --- include/linux/bpf.h | 8 +---- kernel/bpf/dispatcher.c | 4 +-- kernel/bpf/trampoline.c | 83 ++++++------------------------------------------- kernel/extable.c | 2 -- 4 files changed, 13 insertions(+), 84 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 86cacb54ba23..bdb981c204fa 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -583,14 +583,8 @@ void bpf_trampoline_put(struct bpf_trampoline *tr); #define BPF_DISPATCHER_PTR(name) (&bpf_dispatcher_##name) void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, struct bpf_prog *to); -struct bpf_image { - struct latch_tree_node tnode; - unsigned char data[]; -}; -#define BPF_IMAGE_SIZE (PAGE_SIZE - sizeof(struct bpf_image)) -bool is_bpf_image_address(unsigned long address); -void *bpf_image_alloc(void); /* Called only from JIT-enabled code, so there's no need for stubs. */ +void *bpf_jit_alloc_exec_page(void); void bpf_image_ksym_add(void *data, struct bpf_ksym *ksym); void bpf_image_ksym_del(struct bpf_ksym *ksym); void bpf_ksym_add(struct bpf_ksym *ksym); diff --git a/kernel/bpf/dispatcher.c b/kernel/bpf/dispatcher.c index a2679bae9e73..2444bd15cc2d 100644 --- a/kernel/bpf/dispatcher.c +++ b/kernel/bpf/dispatcher.c @@ -113,7 +113,7 @@ static void bpf_dispatcher_update(struct bpf_dispatcher *d, int prev_num_progs) noff = 0; } else { old = d->image + d->image_off; - noff = d->image_off ^ (BPF_IMAGE_SIZE / 2); + noff = d->image_off ^ (PAGE_SIZE / 2); } new = d->num_progs ? d->image + noff : NULL; @@ -140,7 +140,7 @@ void bpf_dispatcher_change_prog(struct bpf_dispatcher *d, struct bpf_prog *from, mutex_lock(&d->mutex); if (!d->image) { - d->image = bpf_image_alloc(); + d->image = bpf_jit_alloc_exec_page(); if (!d->image) goto out; bpf_image_ksym_add(d->image, &d->ksym); diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c index 36549c9afec4..f42f700c1d28 100644 --- a/kernel/bpf/trampoline.c +++ b/kernel/bpf/trampoline.c @@ -18,12 +18,11 @@ const struct bpf_prog_ops bpf_extension_prog_ops = { #define TRAMPOLINE_TABLE_SIZE (1 << TRAMPOLINE_HASH_BITS) static struct hlist_head trampoline_table[TRAMPOLINE_TABLE_SIZE]; -static struct latch_tree_root image_tree __cacheline_aligned; -/* serializes access to trampoline_table and image_tree */ +/* serializes access to trampoline_table */ static DEFINE_MUTEX(trampoline_mutex); -static void *bpf_jit_alloc_exec_page(void) +void *bpf_jit_alloc_exec_page(void) { void *image; @@ -39,78 +38,20 @@ static void *bpf_jit_alloc_exec_page(void) return image; } -static __always_inline bool image_tree_less(struct latch_tree_node *a, - struct latch_tree_node *b) -{ - struct bpf_image *ia = container_of(a, struct bpf_image, tnode); - struct bpf_image *ib = container_of(b, struct bpf_image, tnode); - - return ia < ib; -} - -static __always_inline int image_tree_comp(void *addr, struct latch_tree_node *n) -{ - void *image = container_of(n, struct bpf_image, tnode); - - if (addr < image) - return -1; - if (addr >= image + PAGE_SIZE) - return 1; - - return 0; -} - -static const struct latch_tree_ops image_tree_ops = { - .less = image_tree_less, - .comp = image_tree_comp, -}; - -static void *__bpf_image_alloc(bool lock) -{ - struct bpf_image *image; - - image = bpf_jit_alloc_exec_page(); - if (!image) - return NULL; - - if (lock) - mutex_lock(&trampoline_mutex); - latch_tree_insert(&image->tnode, &image_tree, &image_tree_ops); - if (lock) - mutex_unlock(&trampoline_mutex); - return image->data; -} - -void *bpf_image_alloc(void) -{ - return __bpf_image_alloc(true); -} - -bool is_bpf_image_address(unsigned long addr) -{ - bool ret; - - rcu_read_lock(); - ret = latch_tree_find((void *) addr, &image_tree, &image_tree_ops) != NULL; - rcu_read_unlock(); - - return ret; -} - void bpf_image_ksym_add(void *data, struct bpf_ksym *ksym) { ksym->start = (unsigned long) data; - ksym->end = ksym->start + BPF_IMAGE_SIZE; + ksym->end = ksym->start + PAGE_SIZE; bpf_ksym_add(ksym); perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start, - BPF_IMAGE_SIZE, false, ksym->name); + PAGE_SIZE, false, ksym->name); } void bpf_image_ksym_del(struct bpf_ksym *ksym) { bpf_ksym_del(ksym); perf_event_ksymbol(PERF_RECORD_KSYMBOL_TYPE_BPF, ksym->start, - BPF_IMAGE_SIZE, true, ksym->name); + PAGE_SIZE, true, ksym->name); } static void bpf_trampoline_ksym_add(struct bpf_trampoline *tr) @@ -141,7 +82,7 @@ struct bpf_trampoline *bpf_trampoline_lookup(u64 key) goto out; /* is_root was checked earlier. No need for bpf_jit_charge_modmem() */ - image = __bpf_image_alloc(false); + image = bpf_jit_alloc_exec_page(); if (!image) { kfree(tr); tr = NULL; @@ -243,8 +184,8 @@ bpf_trampoline_get_progs(const struct bpf_trampoline *tr, int *total) static int bpf_trampoline_update(struct bpf_trampoline *tr) { - void *old_image = tr->image + ((tr->selector + 1) & 1) * BPF_IMAGE_SIZE/2; - void *new_image = tr->image + (tr->selector & 1) * BPF_IMAGE_SIZE/2; + void *old_image = tr->image + ((tr->selector + 1) & 1) * PAGE_SIZE/2; + void *new_image = tr->image + (tr->selector & 1) * PAGE_SIZE/2; struct bpf_tramp_progs *tprogs; u32 flags = BPF_TRAMP_F_RESTORE_REGS; int err, total; @@ -272,7 +213,7 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr) synchronize_rcu_tasks(); - err = arch_prepare_bpf_trampoline(new_image, new_image + BPF_IMAGE_SIZE / 2, + err = arch_prepare_bpf_trampoline(new_image, new_image + PAGE_SIZE / 2, &tr->func.model, flags, tprogs, tr->func.addr); if (err < 0) @@ -383,8 +324,6 @@ out: void bpf_trampoline_put(struct bpf_trampoline *tr) { - struct bpf_image *image; - if (!tr) return; mutex_lock(&trampoline_mutex); @@ -396,11 +335,9 @@ void bpf_trampoline_put(struct bpf_trampoline *tr) if (WARN_ON_ONCE(!hlist_empty(&tr->progs_hlist[BPF_TRAMP_FEXIT]))) goto out; bpf_image_ksym_del(&tr->ksym); - image = container_of(tr->image, struct bpf_image, data); - latch_tree_erase(&image->tnode, &image_tree, &image_tree_ops); /* wait for tasks to get out of trampoline before freeing it */ synchronize_rcu_tasks(); - bpf_jit_free_exec(image); + bpf_jit_free_exec(tr->image); hlist_del(&tr->hlist); kfree(tr); out: diff --git a/kernel/extable.c b/kernel/extable.c index a0024f27d3a1..7681f87e89dd 100644 --- a/kernel/extable.c +++ b/kernel/extable.c @@ -149,8 +149,6 @@ int kernel_text_address(unsigned long addr) goto out; if (is_bpf_text_address(addr)) goto out; - if (is_bpf_image_address(addr)) - goto out; ret = 0; out: if (no_rcu) -- cgit v1.2.3 From eec152734be10c72d2d413a27ca9d282c28cdb61 Mon Sep 17 00:00:00 2001 From: Ansuel Smith Date: Tue, 10 Mar 2020 15:37:56 +0100 Subject: clk: qcom: clk-rpm: add missing rpm clk for ipq806x Add missing definition of rpm clk for ipq806x soc Signed-off-by: John Crispin Signed-off-by: Ansuel Smith Acked-by: John Crispin Reviewed-by: Rob Herring Link: https://lkml.kernel.org/r/20200310143756.244-1-ansuelsmth@gmail.com Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + drivers/clk/qcom/clk-rpm.c | 35 ++++++++++++++++++++++ include/dt-bindings/clock/qcom,rpmcc.h | 4 +++ 3 files changed, 40 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 356cabcd844d..90a1349bc713 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -16,6 +16,7 @@ Required properties : "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-msm8976", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" + "qcom,rpmcc-ipq806x", "qcom,rpmcc" "qcom,rpmcc-msm8996", "qcom,rpmcc" "qcom,rpmcc-msm8998", "qcom,rpmcc" "qcom,rpmcc-qcs404", "qcom,rpmcc" diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index 9e3110a71f12..f71d228fd6bd 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -543,10 +543,45 @@ static const struct rpm_clk_desc rpm_clk_apq8064 = { .num_clks = ARRAY_SIZE(apq8064_clks), }; +/* ipq806x */ +DEFINE_CLK_RPM(ipq806x, afab_clk, afab_a_clk, QCOM_RPM_APPS_FABRIC_CLK); +DEFINE_CLK_RPM(ipq806x, cfpb_clk, cfpb_a_clk, QCOM_RPM_CFPB_CLK); +DEFINE_CLK_RPM(ipq806x, daytona_clk, daytona_a_clk, QCOM_RPM_DAYTONA_FABRIC_CLK); +DEFINE_CLK_RPM(ipq806x, ebi1_clk, ebi1_a_clk, QCOM_RPM_EBI1_CLK); +DEFINE_CLK_RPM(ipq806x, sfab_clk, sfab_a_clk, QCOM_RPM_SYS_FABRIC_CLK); +DEFINE_CLK_RPM(ipq806x, sfpb_clk, sfpb_a_clk, QCOM_RPM_SFPB_CLK); +DEFINE_CLK_RPM(ipq806x, nss_fabric_0_clk, nss_fabric_0_a_clk, QCOM_RPM_NSS_FABRIC_0_CLK); +DEFINE_CLK_RPM(ipq806x, nss_fabric_1_clk, nss_fabric_1_a_clk, QCOM_RPM_NSS_FABRIC_1_CLK); + +static struct clk_rpm *ipq806x_clks[] = { + [RPM_APPS_FABRIC_CLK] = &ipq806x_afab_clk, + [RPM_APPS_FABRIC_A_CLK] = &ipq806x_afab_a_clk, + [RPM_CFPB_CLK] = &ipq806x_cfpb_clk, + [RPM_CFPB_A_CLK] = &ipq806x_cfpb_a_clk, + [RPM_DAYTONA_FABRIC_CLK] = &ipq806x_daytona_clk, + [RPM_DAYTONA_FABRIC_A_CLK] = &ipq806x_daytona_a_clk, + [RPM_EBI1_CLK] = &ipq806x_ebi1_clk, + [RPM_EBI1_A_CLK] = &ipq806x_ebi1_a_clk, + [RPM_SYS_FABRIC_CLK] = &ipq806x_sfab_clk, + [RPM_SYS_FABRIC_A_CLK] = &ipq806x_sfab_a_clk, + [RPM_SFPB_CLK] = &ipq806x_sfpb_clk, + [RPM_SFPB_A_CLK] = &ipq806x_sfpb_a_clk, + [RPM_NSS_FABRIC_0_CLK] = &ipq806x_nss_fabric_0_clk, + [RPM_NSS_FABRIC_0_A_CLK] = &ipq806x_nss_fabric_0_a_clk, + [RPM_NSS_FABRIC_1_CLK] = &ipq806x_nss_fabric_1_clk, + [RPM_NSS_FABRIC_1_A_CLK] = &ipq806x_nss_fabric_1_a_clk, +}; + +static const struct rpm_clk_desc rpm_clk_ipq806x = { + .clks = ipq806x_clks, + .num_clks = ARRAY_SIZE(ipq806x_clks), +}; + static const struct of_device_id rpm_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8660", .data = &rpm_clk_msm8660 }, { .compatible = "qcom,rpmcc-apq8060", .data = &rpm_clk_msm8660 }, { .compatible = "qcom,rpmcc-apq8064", .data = &rpm_clk_apq8064 }, + { .compatible = "qcom,rpmcc-ipq806x", .data = &rpm_clk_ipq806x }, { } }; MODULE_DEVICE_TABLE(of, rpm_clk_match_table); diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index 8e3095720552..ae74c43c485d 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -37,6 +37,10 @@ #define RPM_XO_A0 27 #define RPM_XO_A1 28 #define RPM_XO_A2 29 +#define RPM_NSS_FABRIC_0_CLK 30 +#define RPM_NSS_FABRIC_0_A_CLK 31 +#define RPM_NSS_FABRIC_1_CLK 32 +#define RPM_NSS_FABRIC_1_A_CLK 33 /* SMD RPM clocks */ #define RPM_SMD_XO_CLK_SRC 0 -- cgit v1.2.3 From e138aa7d3271ac1b0690ae2c9b04d51468dce1d6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 13 Mar 2020 09:22:09 +0000 Subject: rxrpc: Fix call interruptibility handling Fix the interruptibility of kernel-initiated client calls so that they're either only interruptible when they're waiting for a call slot to come available or they're not interruptible at all. Either way, they're not interruptible during transmission. This should help prevent StoreData calls from being interrupted when writeback is in progress. It doesn't, however, handle interruption during the receive phase. Userspace-initiated calls are still interruptable. After the signal has been handled, sendmsg() will return the amount of data copied out of the buffer and userspace can perform another sendmsg() call to continue transmission. Fixes: bc5e3a546d55 ("rxrpc: Use MSG_WAITALL to tell sendmsg() to temporarily ignore signals") Signed-off-by: David Howells --- fs/afs/rxrpc.c | 3 ++- include/net/af_rxrpc.h | 8 +++++++- net/rxrpc/af_rxrpc.c | 4 ++-- net/rxrpc/ar-internal.h | 4 ++-- net/rxrpc/call_object.c | 3 +-- net/rxrpc/conn_client.c | 13 ++++++++++--- net/rxrpc/sendmsg.c | 44 ++++++++++++++++++++++++++++++++++++-------- 7 files changed, 60 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 58d396592250..4c28712bb7f6 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -413,7 +413,8 @@ void afs_make_call(struct afs_addr_cursor *ac, struct afs_call *call, gfp_t gfp) afs_wake_up_async_call : afs_wake_up_call_waiter), call->upgrade, - call->intr, + (call->intr ? RXRPC_PREINTERRUPTIBLE : + RXRPC_UNINTERRUPTIBLE), call->debug_id); if (IS_ERR(rxcall)) { ret = PTR_ERR(rxcall); diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 1abae3c340a5..8e547b4d88c8 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -16,6 +16,12 @@ struct sock; struct socket; struct rxrpc_call; +enum rxrpc_interruptibility { + RXRPC_INTERRUPTIBLE, /* Call is interruptible */ + RXRPC_PREINTERRUPTIBLE, /* Call can be cancelled whilst waiting for a slot */ + RXRPC_UNINTERRUPTIBLE, /* Call should not be interruptible at all */ +}; + /* * Debug ID counter for tracing. */ @@ -41,7 +47,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, gfp_t, rxrpc_notify_rx_t, bool, - bool, + enum rxrpc_interruptibility, unsigned int); int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *, struct msghdr *, size_t, diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index fe42f986cd94..7603cf811f75 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -285,7 +285,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, gfp_t gfp, rxrpc_notify_rx_t notify_rx, bool upgrade, - bool intr, + enum rxrpc_interruptibility interruptibility, unsigned int debug_id) { struct rxrpc_conn_parameters cp; @@ -310,7 +310,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, memset(&p, 0, sizeof(p)); p.user_call_ID = user_call_ID; p.tx_total_len = tx_total_len; - p.intr = intr; + p.interruptibility = interruptibility; memset(&cp, 0, sizeof(cp)); cp.local = rx->local; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 7d730c438404..1f72f43b082d 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -489,7 +489,6 @@ enum rxrpc_call_flag { RXRPC_CALL_BEGAN_RX_TIMER, /* We began the expect_rx_by timer */ RXRPC_CALL_RX_HEARD, /* The peer responded at least once to this call */ RXRPC_CALL_RX_UNDERRUN, /* Got data underrun */ - RXRPC_CALL_IS_INTR, /* The call is interruptible */ RXRPC_CALL_DISCONNECTED, /* The call has been disconnected */ }; @@ -598,6 +597,7 @@ struct rxrpc_call { atomic_t usage; u16 service_id; /* service ID */ u8 security_ix; /* Security type */ + enum rxrpc_interruptibility interruptibility; /* At what point call may be interrupted */ u32 call_id; /* call ID on connection */ u32 cid; /* connection ID plus channel index */ int debug_id; /* debug ID for printks */ @@ -721,7 +721,7 @@ struct rxrpc_call_params { u32 normal; /* Max time since last call packet (msec) */ } timeouts; u8 nr_timeouts; /* Number of timeouts specified */ - bool intr; /* The call is interruptible */ + enum rxrpc_interruptibility interruptibility; /* How is interruptible is the call? */ }; struct rxrpc_send_params { diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index c9f34b0a11df..f07970207b54 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -237,8 +237,7 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, return call; } - if (p->intr) - __set_bit(RXRPC_CALL_IS_INTR, &call->flags); + call->interruptibility = p->interruptibility; call->tx_total_len = p->tx_total_len; trace_rxrpc_call(call->debug_id, rxrpc_call_new_client, atomic_read(&call->usage), diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index ea7d4c21f889..f2a1a5dbb5a7 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -655,13 +655,20 @@ static int rxrpc_wait_for_channel(struct rxrpc_call *call, gfp_t gfp) add_wait_queue_exclusive(&call->waitq, &myself); for (;;) { - if (test_bit(RXRPC_CALL_IS_INTR, &call->flags)) + switch (call->interruptibility) { + case RXRPC_INTERRUPTIBLE: + case RXRPC_PREINTERRUPTIBLE: set_current_state(TASK_INTERRUPTIBLE); - else + break; + case RXRPC_UNINTERRUPTIBLE: + default: set_current_state(TASK_UNINTERRUPTIBLE); + break; + } if (call->call_id) break; - if (test_bit(RXRPC_CALL_IS_INTR, &call->flags) && + if ((call->interruptibility == RXRPC_INTERRUPTIBLE || + call->interruptibility == RXRPC_PREINTERRUPTIBLE) && signal_pending(current)) { ret = -ERESTARTSYS; break; diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index a13051d38097..1eccfb92c9e1 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -62,7 +62,7 @@ static int rxrpc_wait_for_tx_window_intr(struct rxrpc_sock *rx, * Wait for space to appear in the Tx queue uninterruptibly, but with * a timeout of 2*RTT if no progress was made and a signal occurred. */ -static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx, +static int rxrpc_wait_for_tx_window_waitall(struct rxrpc_sock *rx, struct rxrpc_call *call) { rxrpc_seq_t tx_start, tx_win; @@ -87,8 +87,7 @@ static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx, if (call->state >= RXRPC_CALL_COMPLETE) return call->error; - if (test_bit(RXRPC_CALL_IS_INTR, &call->flags) && - timeout == 0 && + if (timeout == 0 && tx_win == tx_start && signal_pending(current)) return -EINTR; @@ -102,6 +101,26 @@ static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx, } } +/* + * Wait for space to appear in the Tx queue uninterruptibly. + */ +static int rxrpc_wait_for_tx_window_nonintr(struct rxrpc_sock *rx, + struct rxrpc_call *call, + long *timeo) +{ + for (;;) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (rxrpc_check_tx_space(call, NULL)) + return 0; + + if (call->state >= RXRPC_CALL_COMPLETE) + return call->error; + + trace_rxrpc_transmit(call, rxrpc_transmit_wait); + *timeo = schedule_timeout(*timeo); + } +} + /* * wait for space to appear in the transmit/ACK window * - caller holds the socket locked @@ -119,10 +138,19 @@ static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx, add_wait_queue(&call->waitq, &myself); - if (waitall) - ret = rxrpc_wait_for_tx_window_nonintr(rx, call); - else - ret = rxrpc_wait_for_tx_window_intr(rx, call, timeo); + switch (call->interruptibility) { + case RXRPC_INTERRUPTIBLE: + if (waitall) + ret = rxrpc_wait_for_tx_window_waitall(rx, call); + else + ret = rxrpc_wait_for_tx_window_intr(rx, call, timeo); + break; + case RXRPC_PREINTERRUPTIBLE: + case RXRPC_UNINTERRUPTIBLE: + default: + ret = rxrpc_wait_for_tx_window_nonintr(rx, call, timeo); + break; + } remove_wait_queue(&call->waitq, &myself); set_current_state(TASK_RUNNING); @@ -628,7 +656,7 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) .call.tx_total_len = -1, .call.user_call_ID = 0, .call.nr_timeouts = 0, - .call.intr = true, + .call.interruptibility = RXRPC_INTERRUPTIBLE, .abort_code = 0, .command = RXRPC_CMD_SEND_DATA, .exclusive = false, -- cgit v1.2.3 From 4636cf184d6d9a92a56c2554681ea520dd4fe49a Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 13 Mar 2020 13:36:01 +0000 Subject: afs: Fix some tracing details Fix a couple of tracelines to indicate the usage count after the atomic op, not the usage count before it to be consistent with other afs and rxrpc trace lines. Change the wording of the afs_call_trace_work trace ID label from "WORK" to "QUEUE" to reflect the fact that it's queueing work, not doing work. Fixes: 341f741f04be ("afs: Refcount the afs_call struct") Signed-off-by: David Howells --- fs/afs/rxrpc.c | 4 ++-- include/trace/events/afs.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 4c28712bb7f6..907d5948564a 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -169,7 +169,7 @@ void afs_put_call(struct afs_call *call) int n = atomic_dec_return(&call->usage); int o = atomic_read(&net->nr_outstanding_calls); - trace_afs_call(call, afs_call_trace_put, n + 1, o, + trace_afs_call(call, afs_call_trace_put, n, o, __builtin_return_address(0)); ASSERTCMP(n, >=, 0); @@ -736,7 +736,7 @@ static void afs_wake_up_async_call(struct sock *sk, struct rxrpc_call *rxcall, u = atomic_fetch_add_unless(&call->usage, 1, 0); if (u != 0) { - trace_afs_call(call, afs_call_trace_wake, u, + trace_afs_call(call, afs_call_trace_wake, u + 1, atomic_read(&call->net->nr_outstanding_calls), __builtin_return_address(0)); diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h index 564ba1b5cf57..c612cabbc378 100644 --- a/include/trace/events/afs.h +++ b/include/trace/events/afs.h @@ -233,7 +233,7 @@ enum afs_cb_break_reason { EM(afs_call_trace_get, "GET ") \ EM(afs_call_trace_put, "PUT ") \ EM(afs_call_trace_wake, "WAKE ") \ - E_(afs_call_trace_work, "WORK ") + E_(afs_call_trace_work, "QUEUE") #define afs_server_traces \ EM(afs_server_trace_alloc, "ALLOC ") \ -- cgit v1.2.3 From 7d7587db0d7fd1138f2afcffdc46a8e15630b944 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 12 Mar 2020 21:40:06 +0000 Subject: afs: Fix client call Rx-phase signal handling Fix the handling of signals in client rxrpc calls made by the afs filesystem. Ignore signals completely, leaving call abandonment or connection loss to be detected by timeouts inside AF_RXRPC. Allowing a filesystem call to be interrupted after the entire request has been transmitted and an abort sent means that the server may or may not have done the action - and we don't know. It may even be worse than that for older servers. Fixes: bc5e3a546d55 ("rxrpc: Use MSG_WAITALL to tell sendmsg() to temporarily ignore signals") Signed-off-by: David Howells --- fs/afs/rxrpc.c | 34 ++-------------------------------- include/net/af_rxrpc.h | 4 +--- net/rxrpc/af_rxrpc.c | 33 +++------------------------------ net/rxrpc/ar-internal.h | 1 - net/rxrpc/input.c | 1 - 5 files changed, 6 insertions(+), 67 deletions(-) (limited to 'include') diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 972e3aafa361..1ecc67da6c1a 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -604,11 +604,7 @@ call_complete: long afs_wait_for_call_to_complete(struct afs_call *call, struct afs_addr_cursor *ac) { - signed long rtt2, timeout; long ret; - bool stalled = false; - u64 rtt; - u32 life, last_life; bool rxrpc_complete = false; DECLARE_WAITQUEUE(myself, current); @@ -619,14 +615,6 @@ long afs_wait_for_call_to_complete(struct afs_call *call, if (ret < 0) goto out; - rtt = rxrpc_kernel_get_rtt(call->net->socket, call->rxcall); - rtt2 = nsecs_to_jiffies64(rtt) * 2; - if (rtt2 < 2) - rtt2 = 2; - - timeout = rtt2; - rxrpc_kernel_check_life(call->net->socket, call->rxcall, &last_life); - add_wait_queue(&call->waitq, &myself); for (;;) { set_current_state(TASK_UNINTERRUPTIBLE); @@ -637,37 +625,19 @@ long afs_wait_for_call_to_complete(struct afs_call *call, call->need_attention = false; __set_current_state(TASK_RUNNING); afs_deliver_to_call(call); - timeout = rtt2; continue; } if (afs_check_call_state(call, AFS_CALL_COMPLETE)) break; - if (!rxrpc_kernel_check_life(call->net->socket, call->rxcall, &life)) { + if (!rxrpc_kernel_check_life(call->net->socket, call->rxcall)) { /* rxrpc terminated the call. */ rxrpc_complete = true; break; } - if (call->intr && timeout == 0 && - life == last_life && signal_pending(current)) { - if (stalled) - break; - __set_current_state(TASK_RUNNING); - rxrpc_kernel_probe_life(call->net->socket, call->rxcall); - timeout = rtt2; - stalled = true; - continue; - } - - if (life != last_life) { - timeout = rtt2; - last_life = life; - stalled = false; - } - - timeout = schedule_timeout(timeout); + schedule(); } remove_wait_queue(&call->waitq, &myself); diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 8e547b4d88c8..04e97bab6f28 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -64,9 +64,7 @@ int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t, rxrpc_user_attach_call_t, unsigned long, gfp_t, unsigned int); void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64); -bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *, - u32 *); -void rxrpc_kernel_probe_life(struct socket *, struct rxrpc_call *); +bool rxrpc_kernel_check_life(const struct socket *, const struct rxrpc_call *); u32 rxrpc_kernel_get_epoch(struct socket *, struct rxrpc_call *); bool rxrpc_kernel_get_reply_time(struct socket *, struct rxrpc_call *, ktime_t *); diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 7603cf811f75..15ee92d79581 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -371,44 +371,17 @@ EXPORT_SYMBOL(rxrpc_kernel_end_call); * rxrpc_kernel_check_life - Check to see whether a call is still alive * @sock: The socket the call is on * @call: The call to check - * @_life: Where to store the life value * - * Allow a kernel service to find out whether a call is still alive - ie. we're - * getting ACKs from the server. Passes back in *_life a number representing - * the life state which can be compared to that returned by a previous call and - * return true if the call is still alive. - * - * If the life state stalls, rxrpc_kernel_probe_life() should be called and - * then 2RTT waited. + * Allow a kernel service to find out whether a call is still alive - + * ie. whether it has completed. */ bool rxrpc_kernel_check_life(const struct socket *sock, - const struct rxrpc_call *call, - u32 *_life) + const struct rxrpc_call *call) { - *_life = call->acks_latest; return call->state != RXRPC_CALL_COMPLETE; } EXPORT_SYMBOL(rxrpc_kernel_check_life); -/** - * rxrpc_kernel_probe_life - Poke the peer to see if it's still alive - * @sock: The socket the call is on - * @call: The call to check - * - * In conjunction with rxrpc_kernel_check_life(), allow a kernel service to - * find out whether a call is still alive by pinging it. This should cause the - * life state to be bumped in about 2*RTT. - * - * The must be called in TASK_RUNNING state on pain of might_sleep() objecting. - */ -void rxrpc_kernel_probe_life(struct socket *sock, struct rxrpc_call *call) -{ - rxrpc_propose_ACK(call, RXRPC_ACK_PING, 0, true, false, - rxrpc_propose_ack_ping_for_check_life); - rxrpc_send_ack_packet(call, true, NULL); -} -EXPORT_SYMBOL(rxrpc_kernel_probe_life); - /** * rxrpc_kernel_get_epoch - Retrieve the epoch value from a call. * @sock: The socket the call is on diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 1f72f43b082d..3eb1ab40ca5c 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -675,7 +675,6 @@ struct rxrpc_call { /* transmission-phase ACK management */ ktime_t acks_latest_ts; /* Timestamp of latest ACK received */ - rxrpc_serial_t acks_latest; /* serial number of latest ACK received */ rxrpc_seq_t acks_lowest_nak; /* Lowest NACK in the buffer (or ==tx_hard_ack) */ rxrpc_seq_t acks_lost_top; /* tx_top at the time lost-ack ping sent */ rxrpc_serial_t acks_lost_ping; /* Serial number of probe ACK */ diff --git a/net/rxrpc/input.c b/net/rxrpc/input.c index ef10fbf71b15..69e09d69c896 100644 --- a/net/rxrpc/input.c +++ b/net/rxrpc/input.c @@ -882,7 +882,6 @@ static void rxrpc_input_ack(struct rxrpc_call *call, struct sk_buff *skb) before(prev_pkt, call->ackr_prev_seq)) goto out; call->acks_latest_ts = skb->tstamp; - call->acks_latest = sp->hdr.serial; call->ackr_first_seq = first_soft_ack; call->ackr_prev_seq = prev_pkt; -- cgit v1.2.3 From 172e7890406d6183b9b39271ffb434ff0a97ce72 Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Mon, 17 Feb 2020 14:44:05 +0100 Subject: tty/serial: cleanup after ioc*_serial driver removal Commit 9c860e4cf708 ("tty/serial: remove the ioc3_serial driver") and commit a017ef17cfd8 ("tty/serial: remove the ioc4_serial driver") removed the ioc{3,4}_serial driver, but missed some files. Fortunately, ./scripts/get_maintainer.pl --self-test complains: warning: no file matches F: drivers/tty/serial/ioc?_serial.c The driver is gone, so remove the header and maintainer entry as well. The serial.rst Documentation might be useful, so we keep it and update the maintainer entry to the document's actual maintainers. Signed-off-by: Lukas Bulwahn Reviewed-by: Christoph Hellwig Signed-off-by: Tony Luck --- MAINTAINERS | 9 +---- include/linux/ioc3.h | 93 ---------------------------------------------------- 2 files changed, 1 insertion(+), 101 deletions(-) delete mode 100644 include/linux/ioc3.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index 6158a143a13e..c59dc23065fc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7946,6 +7946,7 @@ L: linux-ia64@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/aegl/linux.git S: Maintained F: arch/ia64/ +F: Documentation/ia64/ IBM Power 842 compression accelerator M: Haren Myneni @@ -15042,14 +15043,6 @@ M: Dimitri Sivanich S: Maintained F: drivers/misc/sgi-gru/ -SGI SN-IA64 (Altix) SERIAL CONSOLE DRIVER -M: Pat Gefre -L: linux-ia64@vger.kernel.org -S: Supported -F: Documentation/ia64/serial.rst -F: drivers/tty/serial/ioc?_serial.c -F: include/linux/ioc?.h - SGI XP/XPC/XPNET DRIVER M: Cliff Whickman M: Robin Holt diff --git a/include/linux/ioc3.h b/include/linux/ioc3.h deleted file mode 100644 index 38b286e9a46c..000000000000 --- a/include/linux/ioc3.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (c) 2005 Stanislaw Skowronek - */ - -#ifndef _LINUX_IOC3_H -#define _LINUX_IOC3_H - -#include - -#define IOC3_MAX_SUBMODULES 32 - -#define IOC3_CLASS_NONE 0 -#define IOC3_CLASS_BASE_IP27 1 -#define IOC3_CLASS_BASE_IP30 2 -#define IOC3_CLASS_MENET_123 3 -#define IOC3_CLASS_MENET_4 4 -#define IOC3_CLASS_CADDUO 5 -#define IOC3_CLASS_SERIAL 6 - -/* One of these per IOC3 */ -struct ioc3_driver_data { - struct list_head list; - int id; /* IOC3 sequence number */ - /* PCI mapping */ - unsigned long pma; /* physical address */ - struct ioc3 __iomem *vma; /* pointer to registers */ - struct pci_dev *pdev; /* PCI device */ - /* IRQ stuff */ - int dual_irq; /* set if separate IRQs are used */ - int irq_io, irq_eth; /* IRQ numbers */ - /* GPIO magic */ - spinlock_t gpio_lock; - unsigned int gpdr_shadow; - /* NIC identifiers */ - char nic_part[32]; - char nic_serial[16]; - char nic_mac[6]; - /* submodule set */ - int class; - void *data[IOC3_MAX_SUBMODULES]; /* for submodule use */ - int active[IOC3_MAX_SUBMODULES]; /* set if probe succeeds */ - /* is_ir_lock must be held while - * modifying sio_ie values, so - * we can be sure that sio_ie is - * not changing when we read it - * along with sio_ir. - */ - spinlock_t ir_lock; /* SIO_IE[SC] mod lock */ -}; - -/* One per submodule */ -struct ioc3_submodule { - char *name; /* descriptive submodule name */ - struct module *owner; /* owning kernel module */ - int ethernet; /* set for ethernet drivers */ - int (*probe) (struct ioc3_submodule *, struct ioc3_driver_data *); - int (*remove) (struct ioc3_submodule *, struct ioc3_driver_data *); - int id; /* assigned by IOC3, index for the "data" array */ - /* IRQ stuff */ - unsigned int irq_mask; /* IOC3 IRQ mask, leave clear for Ethernet */ - int reset_mask; /* non-zero if you want the ioc3.c module to reset interrupts */ - int (*intr) (struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int); - /* private submodule data */ - void *data; /* assigned by submodule */ -}; - -/********************************** - * Functions needed by submodules * - **********************************/ - -#define IOC3_W_IES 0 -#define IOC3_W_IEC 1 - -/* registers a submodule for all existing and future IOC3 chips */ -extern int ioc3_register_submodule(struct ioc3_submodule *); -/* unregisters a submodule */ -extern void ioc3_unregister_submodule(struct ioc3_submodule *); -/* enables IRQs indicated by irq_mask for a specified IOC3 chip */ -extern void ioc3_enable(struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int); -/* ackowledges specified IRQs */ -extern void ioc3_ack(struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int); -/* disables IRQs indicated by irq_mask for a specified IOC3 chip */ -extern void ioc3_disable(struct ioc3_submodule *, struct ioc3_driver_data *, unsigned int); -/* atomically sets GPCR bits */ -extern void ioc3_gpcr_set(struct ioc3_driver_data *, unsigned int); -/* general ireg writer */ -extern void ioc3_write_ireg(struct ioc3_driver_data *idd, uint32_t value, int reg); - -#endif -- cgit v1.2.3 From 161aff1d93abf0e5b5e9dbca88928998c155f677 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 11 Jan 2020 22:52:26 -0500 Subject: LOOKUP_MOUNTPOINT: fold path_mountpointat() into path_lookupat() New LOOKUP flag, telling path_lookupat() to act as path_mountpointat(). IOW, traverse mounts at the final point and skip revalidation of the location where it ends up. Signed-off-by: Al Viro --- fs/autofs/dev-ioctl.c | 6 ++-- fs/internal.h | 1 - fs/namei.c | 89 ++++----------------------------------------------- fs/namespace.c | 4 +-- include/linux/namei.h | 2 +- 5 files changed, 12 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/fs/autofs/dev-ioctl.c b/fs/autofs/dev-ioctl.c index a3cdb0036c5d..f3a0f412b43b 100644 --- a/fs/autofs/dev-ioctl.c +++ b/fs/autofs/dev-ioctl.c @@ -186,7 +186,7 @@ static int find_autofs_mount(const char *pathname, struct path path; int err; - err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0); + err = kern_path(pathname, LOOKUP_MOUNTPOINT, &path); if (err) return err; err = -ENOENT; @@ -519,8 +519,8 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, if (!fp || param->ioctlfd == -1) { if (autofs_type_any(type)) - err = kern_path_mountpoint(AT_FDCWD, - name, &path, LOOKUP_FOLLOW); + err = kern_path(name, LOOKUP_FOLLOW | LOOKUP_MOUNTPOINT, + &path); else err = find_autofs_mount(name, &path, test_by_type, &type); diff --git a/fs/internal.h b/fs/internal.h index f3f280b952a3..b108a8eb75ca 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -61,7 +61,6 @@ extern int finish_clean_context(struct fs_context *fc); */ extern int filename_lookup(int dfd, struct filename *name, unsigned flags, struct path *path, struct path *root); -extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *); extern int vfs_path_lookup(struct dentry *, struct vfsmount *, const char *, unsigned int, struct path *); long do_mknodat(int dfd, const char __user *filename, umode_t mode, diff --git a/fs/namei.c b/fs/namei.c index 8a673f44ffc2..0fcd738b95b9 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2382,6 +2382,10 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path if (!err && nd->flags & LOOKUP_DIRECTORY) if (!d_can_lookup(nd->path.dentry)) err = -ENOTDIR; + if (!err && unlikely(nd->flags & LOOKUP_MOUNTPOINT)) { + err = handle_lookup_down(nd); + nd->flags &= ~LOOKUP_JUMPED; // no d_weak_revalidate(), please... + } if (!err) { *path = nd->path; nd->path.mnt = NULL; @@ -2410,7 +2414,8 @@ int filename_lookup(int dfd, struct filename *name, unsigned flags, retval = path_lookupat(&nd, flags | LOOKUP_REVAL, path); if (likely(!retval)) - audit_inode(name, path->dentry, 0); + audit_inode(name, path->dentry, + flags & LOOKUP_MOUNTPOINT ? AUDIT_INODE_NOEVAL : 0); restore_nameidata(); putname(name); return retval; @@ -2688,88 +2693,6 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags, } EXPORT_SYMBOL(user_path_at_empty); -/** - * path_mountpoint - look up a path to be umounted - * @nd: lookup context - * @flags: lookup flags - * @path: pointer to container for result - * - * Look up the given name, but don't attempt to revalidate the last component. - * Returns 0 and "path" will be valid on success; Returns error otherwise. - */ -static int -path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path) -{ - const char *s = path_init(nd, flags); - int err; - - while (!(err = link_path_walk(s, nd)) && - (err = lookup_last(nd)) > 0) { - s = trailing_symlink(nd); - } - if (!err && (nd->flags & LOOKUP_RCU)) - err = unlazy_walk(nd); - if (!err) - err = handle_lookup_down(nd); - if (!err) { - *path = nd->path; - nd->path.mnt = NULL; - nd->path.dentry = NULL; - } - terminate_walk(nd); - return err; -} - -static int -filename_mountpoint(int dfd, struct filename *name, struct path *path, - unsigned int flags) -{ - struct nameidata nd; - int error; - if (IS_ERR(name)) - return PTR_ERR(name); - set_nameidata(&nd, dfd, name); - error = path_mountpoint(&nd, flags | LOOKUP_RCU, path); - if (unlikely(error == -ECHILD)) - error = path_mountpoint(&nd, flags, path); - if (unlikely(error == -ESTALE)) - error = path_mountpoint(&nd, flags | LOOKUP_REVAL, path); - if (likely(!error)) - audit_inode(name, path->dentry, AUDIT_INODE_NOEVAL); - restore_nameidata(); - putname(name); - return error; -} - -/** - * user_path_mountpoint_at - lookup a path from userland in order to umount it - * @dfd: directory file descriptor - * @name: pathname from userland - * @flags: lookup flags - * @path: pointer to container to hold result - * - * A umount is a special case for path walking. We're not actually interested - * in the inode in this situation, and ESTALE errors can be a problem. We - * simply want track down the dentry and vfsmount attached at the mountpoint - * and avoid revalidating the last component. - * - * Returns 0 and populates "path" on success. - */ -int -user_path_mountpoint_at(int dfd, const char __user *name, unsigned int flags, - struct path *path) -{ - return filename_mountpoint(dfd, getname(name), path, flags); -} - -int -kern_path_mountpoint(int dfd, const char *name, struct path *path, - unsigned int flags) -{ - return filename_mountpoint(dfd, getname_kernel(name), path, flags); -} -EXPORT_SYMBOL(kern_path_mountpoint); - int __check_sticky(struct inode *dir, struct inode *inode) { kuid_t fsuid = current_fsuid(); diff --git a/fs/namespace.c b/fs/namespace.c index 743980380a8f..a28e4db075ed 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1669,7 +1669,7 @@ int ksys_umount(char __user *name, int flags) struct path path; struct mount *mnt; int retval; - int lookup_flags = 0; + int lookup_flags = LOOKUP_MOUNTPOINT; if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) return -EINVAL; @@ -1680,7 +1680,7 @@ int ksys_umount(char __user *name, int flags) if (!(flags & UMOUNT_NOFOLLOW)) lookup_flags |= LOOKUP_FOLLOW; - retval = user_path_mountpoint_at(AT_FDCWD, name, lookup_flags, &path); + retval = user_path_at(AT_FDCWD, name, lookup_flags, &path); if (retval) goto out; mnt = real_mount(path.mnt); diff --git a/include/linux/namei.h b/include/linux/namei.h index 0dd980d7318f..d9576a051808 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -23,6 +23,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; #define LOOKUP_AUTOMOUNT 0x0004 /* force terminal automount */ #define LOOKUP_EMPTY 0x4000 /* accept empty path [user_... only] */ #define LOOKUP_DOWN 0x8000 /* follow mounts in the starting point */ +#define LOOKUP_MOUNTPOINT 0x0080 /* follow mounts in the end */ #define LOOKUP_REVAL 0x0020 /* tell ->d_revalidate() to trust no cache */ #define LOOKUP_RCU 0x0040 /* RCU pathwalk mode; semi-internal */ @@ -64,7 +65,6 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); extern void done_path_create(struct path *, struct dentry *); extern struct dentry *kern_path_locked(const char *, struct path *); -extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int); -- cgit v1.2.3 From b4c0353693d22f9dcfa4757262dab0aab8376d19 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 19 Jan 2020 11:44:51 -0500 Subject: sanitize handling of nd->last_type, kill LAST_BIND ->last_type values are set in 3 places: path_init() (sets to LAST_ROOT), link_path_walk (LAST_NORM/DOT/DOTDOT) and pick_link (LAST_BIND). The are checked in walk_component(), lookup_last() and do_last(). They also get copied to the caller by filename_parentat(). In the last 3 cases the value is what we had at the return from link_path_walk(). In case of walk_component() it's either directly downstream from assignment in link_path_walk() or, when called by lookup_last(), the value we have at the return from link_path_walk(). The value at the entry into link_path_walk() can survive to return only if the pathname contains nothing but slashes. Note that pick_link() never returns such - pure jumps are handled directly. So for the calls of link_path_walk() for trailing symlinks it does not matter what value had been there at the entry; the value at the return won't depend upon it. There are 3 call chains that might have pick_link() storing LAST_BIND: 1) pick_link() from step_into() from walk_component() from link_path_walk(). In that case we will either be parsing the next component immediately after return into link_path_walk(), which will overwrite the ->last_type before anyone has a chance to look at it, or we'll fail, in which case nobody will be looking at ->last_type at all. 2) pick_link() from step_into() from walk_component() from lookup_last(). The value is never looked at due to the above; it won't affect the value seen at return from any link_path_walk(). 3) pick_link() from step_into() from do_last(). Ditto. In other words, assignemnt in pick_link() is pointless, and so is LAST_BIND itself; nothing ever looks at that value. Kill it off. And make link_path_walk() _always_ assign ->last_type - in the only case when the value at the entry might survive to the return that value is always LAST_ROOT, inherited from path_init(). Move that assignment from path_init() into the beginning of link_path_walk(), to consolidate the things. Historical note: LAST_BIND used to be used for the kludge with trailing pure jump symlinks (extra iteration through the top-level loop). No point keeping it anymore... Signed-off-by: Al Viro --- Documentation/filesystems/path-lookup.rst | 7 ++----- fs/namei.c | 3 +-- include/linux/namei.h | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/filesystems/path-lookup.rst b/Documentation/filesystems/path-lookup.rst index a3216979298b..f46b05e9b96c 100644 --- a/Documentation/filesystems/path-lookup.rst +++ b/Documentation/filesystems/path-lookup.rst @@ -404,11 +404,8 @@ that is the "next" component in the pathname. ``int last_type`` ~~~~~~~~~~~~~~~~~ -This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT``, ``LAST_DOTDOT``, or -``LAST_BIND``. The ``last`` field is only valid if the type is -``LAST_NORM``. ``LAST_BIND`` is used when following a symlink and no -components of the symlink have been processed yet. Others should be -fairly self-explanatory. +This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT`` or ``LAST_DOTDOT``. +The ``last`` field is only valid if the type is ``LAST_NORM``. ``struct path root`` ~~~~~~~~~~~~~~~~~~~~ diff --git a/fs/namei.c b/fs/namei.c index 02f148124831..1a83641e95e6 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1785,7 +1785,6 @@ static const char *pick_link(struct nameidata *nd, struct path *link, if (unlikely(error)) return ERR_PTR(error); - nd->last_type = LAST_BIND; res = READ_ONCE(inode->i_link); if (!res) { const char * (*get)(struct dentry *, struct inode *, @@ -2123,6 +2122,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) { int err; + nd->last_type = LAST_ROOT; if (IS_ERR(name)) return PTR_ERR(name); while (*name=='/') @@ -2224,7 +2224,6 @@ static const char *path_init(struct nameidata *nd, unsigned flags) if (flags & LOOKUP_RCU) rcu_read_lock(); - nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->flags = flags | LOOKUP_JUMPED | LOOKUP_PARENT; nd->depth = 0; diff --git a/include/linux/namei.h b/include/linux/namei.h index d9576a051808..a4bb992623c4 100644 --- a/include/linux/namei.h +++ b/include/linux/namei.h @@ -15,7 +15,7 @@ enum { MAX_NESTED_LINKS = 8 }; /* * Type of the last component on LOOKUP_PARENT */ -enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; +enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT}; /* pathwalk mode */ #define LOOKUP_FOLLOW 0x0001 /* follow links at the end */ -- cgit v1.2.3 From 58b05e58d155fd5a9a181d51b4c9c8a69a0816d3 Mon Sep 17 00:00:00 2001 From: Jose Abreu Date: Thu, 12 Mar 2020 18:10:09 +0100 Subject: net: phy: Add XLGMII interface define Add a define for XLGMII interface. Signed-off-by: Jose Abreu Signed-off-by: David S. Miller --- include/linux/phy.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/phy.h b/include/linux/phy.h index 7a08023bdbc5..6b872aed8ba6 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -94,6 +94,7 @@ typedef enum { PHY_INTERFACE_MODE_RTBI, PHY_INTERFACE_MODE_SMII, PHY_INTERFACE_MODE_XGMII, + PHY_INTERFACE_MODE_XLGMII, PHY_INTERFACE_MODE_MOCA, PHY_INTERFACE_MODE_QSGMII, PHY_INTERFACE_MODE_TRGMII, @@ -165,6 +166,8 @@ static inline const char *phy_modes(phy_interface_t interface) return "smii"; case PHY_INTERFACE_MODE_XGMII: return "xgmii"; + case PHY_INTERFACE_MODE_XLGMII: + return "xlgmii"; case PHY_INTERFACE_MODE_MOCA: return "moca"; case PHY_INTERFACE_MODE_QSGMII: -- cgit v1.2.3 From 14bc175d9c885c86239de3d730eea85ad67bfe7b Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Fri, 13 Mar 2020 01:10:56 +0200 Subject: net: sched: Allow extending set of supported RED flags The qdiscs RED, GRED, SFQ and CHOKE use different subsets of the same pool of global RED flags. These are passed in tc_red_qopt.flags. However none of these qdiscs validate the flag field, and just copy it over wholesale to internal structures, and later dump it back. (An exception is GRED, which does validate for VQs -- however not for the main setup.) A broken userspace can therefore configure a qdisc with arbitrary unsupported flags, and later expect to see the flags on qdisc dump. The current ABI therefore allows storage of several bits of custom data to qdisc instances of the types mentioned above. How many bits, depends on which flags are meaningful for the qdisc in question. E.g. SFQ recognizes flags ECN and HARDDROP, and the rest is not interpreted. If SFQ ever needs to support ADAPTATIVE, it needs another way of doing it, and at the same time it needs to retain the possibility to store 6 bits of uninterpreted data. Likewise RED, which adds a new flag later in this patchset. To that end, this patch adds a new function, red_get_flags(), to split the passed flags of RED-like qdiscs to flags and user bits, and red_validate_flags() to validate the resulting configuration. It further adds a new attribute, TCA_RED_FLAGS, to pass arbitrary flags. Signed-off-by: Petr Machata Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/red.h | 33 ++++++++++++++++++++++++++++++++ include/uapi/linux/pkt_sched.h | 16 ++++++++++++++++ net/sched/sch_red.c | 43 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 89 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/red.h b/include/net/red.h index 9665582c4687..6a2aaa6c7c41 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -179,6 +179,39 @@ static inline bool red_check_params(u32 qth_min, u32 qth_max, u8 Wlog) return true; } +static inline int red_get_flags(unsigned char qopt_flags, + unsigned char historic_mask, + struct nlattr *flags_attr, + unsigned char supported_mask, + struct nla_bitfield32 *p_flags, + unsigned char *p_userbits, + struct netlink_ext_ack *extack) +{ + struct nla_bitfield32 flags; + + if (qopt_flags && flags_attr) { + NL_SET_ERR_MSG_MOD(extack, "flags should be passed either through qopt, or through a dedicated attribute"); + return -EINVAL; + } + + if (flags_attr) { + flags = nla_get_bitfield32(flags_attr); + } else { + flags.selector = historic_mask; + flags.value = qopt_flags & historic_mask; + } + + *p_flags = flags; + *p_userbits = qopt_flags & ~historic_mask; + return 0; +} + +static inline int red_validate_flags(unsigned char flags, + struct netlink_ext_ack *extack) +{ + return 0; +} + static inline void red_set_parms(struct red_parms *p, u32 qth_min, u32 qth_max, u8 Wlog, u8 Plog, u8 Scell_log, u8 *stab, u32 max_P) diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index bbe791b24168..6325507935ea 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -256,6 +256,7 @@ enum { TCA_RED_PARMS, TCA_RED_STAB, TCA_RED_MAX_P, + TCA_RED_FLAGS, /* bitfield32 */ __TCA_RED_MAX, }; @@ -268,12 +269,27 @@ struct tc_red_qopt { unsigned char Wlog; /* log(W) */ unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ unsigned char Scell_log; /* cell size for idle damping */ + + /* This field can be used for flags that a RED-like qdisc has + * historically supported. E.g. when configuring RED, it can be used for + * ECN, HARDDROP and ADAPTATIVE. For SFQ it can be used for ECN, + * HARDDROP. Etc. Because this field has not been validated, and is + * copied back on dump, any bits besides those to which a given qdisc + * has assigned a historical meaning need to be considered for free use + * by userspace tools. + * + * Any further flags need to be passed differently, e.g. through an + * attribute (such as TCA_RED_FLAGS above). Such attribute should allow + * passing both recent and historic flags in one value. + */ unsigned char flags; #define TC_RED_ECN 1 #define TC_RED_HARDDROP 2 #define TC_RED_ADAPTATIVE 4 }; +#define TC_RED_HISTORIC_FLAGS (TC_RED_ECN | TC_RED_HARDDROP | TC_RED_ADAPTATIVE) + struct tc_red_xstats { __u32 early; /* Early drops */ __u32 pdrop; /* Drops due to queue limits */ diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 1695421333e3..d4ce111704dc 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -35,7 +35,11 @@ struct red_sched_data { u32 limit; /* HARD maximal queue length */ + unsigned char flags; + /* Non-flags in tc_red_qopt.flags. */ + unsigned char userbits; + struct timer_list adapt_timer; struct Qdisc *sch; struct red_parms parms; @@ -44,6 +48,8 @@ struct red_sched_data { struct Qdisc *qdisc; }; +static const u32 red_supported_flags = TC_RED_HISTORIC_FLAGS; + static inline int red_use_ecn(struct red_sched_data *q) { return q->flags & TC_RED_ECN; @@ -183,9 +189,12 @@ static void red_destroy(struct Qdisc *sch) } static const struct nla_policy red_policy[TCA_RED_MAX + 1] = { + [TCA_RED_UNSPEC] = { .strict_start_type = TCA_RED_FLAGS }, [TCA_RED_PARMS] = { .len = sizeof(struct tc_red_qopt) }, [TCA_RED_STAB] = { .len = RED_STAB_SIZE }, [TCA_RED_MAX_P] = { .type = NLA_U32 }, + [TCA_RED_FLAGS] = { .type = NLA_BITFIELD32, + .validation_data = &red_supported_flags }, }; static int red_change(struct Qdisc *sch, struct nlattr *opt, @@ -194,7 +203,10 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt, struct Qdisc *old_child = NULL, *child = NULL; struct red_sched_data *q = qdisc_priv(sch); struct nlattr *tb[TCA_RED_MAX + 1]; + struct nla_bitfield32 flags_bf; struct tc_red_qopt *ctl; + unsigned char userbits; + unsigned char flags; int err; u32 max_P; @@ -216,6 +228,12 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt, if (!red_check_params(ctl->qth_min, ctl->qth_max, ctl->Wlog)) return -EINVAL; + err = red_get_flags(ctl->flags, TC_RED_HISTORIC_FLAGS, + tb[TCA_RED_FLAGS], red_supported_flags, + &flags_bf, &userbits, extack); + if (err) + return err; + if (ctl->limit > 0) { child = fifo_create_dflt(sch, &bfifo_qdisc_ops, ctl->limit, extack); @@ -227,7 +245,14 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt, } sch_tree_lock(sch); - q->flags = ctl->flags; + + flags = (q->flags & ~flags_bf.selector) | flags_bf.value; + err = red_validate_flags(flags, extack); + if (err) + goto unlock_out; + + q->flags = flags; + q->userbits = userbits; q->limit = ctl->limit; if (child) { qdisc_tree_flush_backlog(q->qdisc); @@ -256,6 +281,12 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt, if (old_child) qdisc_put(old_child); return 0; + +unlock_out: + sch_tree_unlock(sch); + if (child) + qdisc_put(child); + return err; } static inline void red_adaptative_timer(struct timer_list *t) @@ -299,10 +330,15 @@ static int red_dump_offload_stats(struct Qdisc *sch) static int red_dump(struct Qdisc *sch, struct sk_buff *skb) { struct red_sched_data *q = qdisc_priv(sch); + struct nla_bitfield32 flags_bf = { + .selector = red_supported_flags, + .value = q->flags, + }; struct nlattr *opts = NULL; struct tc_red_qopt opt = { .limit = q->limit, - .flags = q->flags, + .flags = (q->flags & TC_RED_HISTORIC_FLAGS) | + q->userbits, .qth_min = q->parms.qth_min >> q->parms.Wlog, .qth_max = q->parms.qth_max >> q->parms.Wlog, .Wlog = q->parms.Wlog, @@ -319,7 +355,8 @@ static int red_dump(struct Qdisc *sch, struct sk_buff *skb) if (opts == NULL) goto nla_put_failure; if (nla_put(skb, TCA_RED_PARMS, sizeof(opt), &opt) || - nla_put_u32(skb, TCA_RED_MAX_P, q->parms.max_P)) + nla_put_u32(skb, TCA_RED_MAX_P, q->parms.max_P) || + nla_put(skb, TCA_RED_FLAGS, sizeof(flags_bf), &flags_bf)) goto nla_put_failure; return nla_nest_end(skb, opts); -- cgit v1.2.3 From 0a7fad2376ba6b37c6b1a1072ed2a2381d82cd18 Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Fri, 13 Mar 2020 01:10:57 +0200 Subject: net: sched: RED: Introduce an ECN nodrop mode When the RED Qdisc is currently configured to enable ECN, the RED algorithm is used to decide whether a certain SKB should be marked. If that SKB is not ECN-capable, it is early-dropped. It is also possible to keep all traffic in the queue, and just mark the ECN-capable subset of it, as appropriate under the RED algorithm. Some switches support this mode, and some installations make use of it. To that end, add a new RED flag, TC_RED_NODROP. When the Qdisc is configured with this flag, non-ECT traffic is enqueued instead of being early-dropped. Signed-off-by: Petr Machata Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 1 + include/net/red.h | 5 +++++ include/uapi/linux/pkt_sched.h | 1 + net/sched/sch_red.c | 31 +++++++++++++++++++++++++------ 4 files changed, 32 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index dbc89452f90b..1db8b27d4515 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -740,6 +740,7 @@ struct tc_red_qopt_offload_params { u32 limit; bool is_ecn; bool is_harddrop; + bool is_nodrop; struct gnet_stats_queue *qstats; }; diff --git a/include/net/red.h b/include/net/red.h index 6a2aaa6c7c41..fc455445f4b2 100644 --- a/include/net/red.h +++ b/include/net/red.h @@ -209,6 +209,11 @@ static inline int red_get_flags(unsigned char qopt_flags, static inline int red_validate_flags(unsigned char flags, struct netlink_ext_ack *extack) { + if ((flags & TC_RED_NODROP) && !(flags & TC_RED_ECN)) { + NL_SET_ERR_MSG_MOD(extack, "nodrop mode is only meaningful with ECN"); + return -EINVAL; + } + return 0; } diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 6325507935ea..ea39287d59c8 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -286,6 +286,7 @@ struct tc_red_qopt { #define TC_RED_ECN 1 #define TC_RED_HARDDROP 2 #define TC_RED_ADAPTATIVE 4 +#define TC_RED_NODROP 8 }; #define TC_RED_HISTORIC_FLAGS (TC_RED_ECN | TC_RED_HARDDROP | TC_RED_ADAPTATIVE) diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index d4ce111704dc..3ef0a4f7399b 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -48,7 +48,7 @@ struct red_sched_data { struct Qdisc *qdisc; }; -static const u32 red_supported_flags = TC_RED_HISTORIC_FLAGS; +static const u32 red_supported_flags = TC_RED_HISTORIC_FLAGS | TC_RED_NODROP; static inline int red_use_ecn(struct red_sched_data *q) { @@ -60,6 +60,11 @@ static inline int red_use_harddrop(struct red_sched_data *q) return q->flags & TC_RED_HARDDROP; } +static int red_use_nodrop(struct red_sched_data *q) +{ + return q->flags & TC_RED_NODROP; +} + static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) { @@ -80,23 +85,36 @@ static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch, case RED_PROB_MARK: qdisc_qstats_overlimit(sch); - if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) { + if (!red_use_ecn(q)) { q->stats.prob_drop++; goto congestion_drop; } - q->stats.prob_mark++; + if (INET_ECN_set_ce(skb)) { + q->stats.prob_mark++; + } else if (!red_use_nodrop(q)) { + q->stats.prob_drop++; + goto congestion_drop; + } + + /* Non-ECT packet in ECN nodrop mode: queue it. */ break; case RED_HARD_MARK: qdisc_qstats_overlimit(sch); - if (red_use_harddrop(q) || !red_use_ecn(q) || - !INET_ECN_set_ce(skb)) { + if (red_use_harddrop(q) || !red_use_ecn(q)) { + q->stats.forced_drop++; + goto congestion_drop; + } + + if (INET_ECN_set_ce(skb)) { + q->stats.forced_mark++; + } else if (!red_use_nodrop(q)) { q->stats.forced_drop++; goto congestion_drop; } - q->stats.forced_mark++; + /* Non-ECT packet in ECN nodrop mode: queue it. */ break; } @@ -171,6 +189,7 @@ static int red_offload(struct Qdisc *sch, bool enable) opt.set.limit = q->limit; opt.set.is_ecn = red_use_ecn(q); opt.set.is_harddrop = red_use_harddrop(q); + opt.set.is_nodrop = red_use_nodrop(q); opt.set.qstats = &sch->qstats; } else { opt.command = TC_RED_DESTROY; -- cgit v1.2.3 From fbe639b44a82755d639df1c5d147c93f02ac5a0f Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Thu, 12 Mar 2020 17:38:40 +0530 Subject: soc: qcom: Introduce Protection Domain Restart helpers Qualcomm SoCs (starting with MSM8998) allow for multiple protection domains to run on the same Q6 sub-system. This allows for services like ATH10K WLAN FW to have their own separate address space and crash/recover without disrupting the modem and other PDs running on the same sub-system. The PDR helpers introduces an abstraction that allows for tracking/controlling the life cycle of protection domains running on various Q6 sub-systems. Signed-off-by: Sibi Sankar Link: https://lore.kernel.org/r/20200312120842.21991-2-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 4 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/pdr_interface.c | 757 +++++++++++++++++++++++++++++++++++++++ drivers/soc/qcom/pdr_internal.h | 379 ++++++++++++++++++++ include/linux/soc/qcom/pdr.h | 29 ++ include/linux/soc/qcom/qmi.h | 1 + 6 files changed, 1171 insertions(+) create mode 100644 drivers/soc/qcom/pdr_interface.c create mode 100644 drivers/soc/qcom/pdr_internal.h create mode 100644 include/linux/soc/qcom/pdr.h (limited to 'include') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 80aa8b6c56e0..48501f0245b0 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -76,6 +76,10 @@ config QCOM_OCMEM requirements. This is typically used by the GPU, camera/video, and audio components on some Snapdragon SoCs. +config QCOM_PDR_HELPERS + tristate + select QCOM_QMI_HELPERS + config QCOM_PM bool "Qualcomm Power Management" depends on ARCH_QCOM && !ARM64 diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 9fb35c8a495e..5d6b83dc58e8 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o obj-$(CONFIG_QCOM_OCMEM) += ocmem.o +obj-$(CONFIG_QCOM_PDR_HELPERS) += pdr_interface.o obj-$(CONFIG_QCOM_PM) += spm.o obj-$(CONFIG_QCOM_QMI_HELPERS) += qmi_helpers.o qmi_helpers-y += qmi_encdec.o qmi_interface.o diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c new file mode 100644 index 000000000000..7ee088b9cc7c --- /dev/null +++ b/drivers/soc/qcom/pdr_interface.c @@ -0,0 +1,757 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include + +#include "pdr_internal.h" + +struct pdr_service { + char service_name[SERVREG_NAME_LENGTH + 1]; + char service_path[SERVREG_NAME_LENGTH + 1]; + + struct sockaddr_qrtr addr; + + unsigned int instance; + unsigned int service; + u8 service_data_valid; + u32 service_data; + int state; + + bool need_notifier_register; + bool need_notifier_remove; + bool need_locator_lookup; + bool service_connected; + + struct list_head node; +}; + +struct pdr_handle { + struct qmi_handle locator_hdl; + struct qmi_handle notifier_hdl; + + struct sockaddr_qrtr locator_addr; + + struct list_head lookups; + struct list_head indack_list; + + /* control access to pdr lookup/indack lists */ + struct mutex list_lock; + + /* serialize pd status invocation */ + struct mutex status_lock; + + /* control access to the locator state */ + struct mutex lock; + + bool locator_init_complete; + + struct work_struct locator_work; + struct work_struct notifier_work; + struct work_struct indack_work; + + struct workqueue_struct *notifier_wq; + struct workqueue_struct *indack_wq; + + void (*status)(int state, char *service_path, void *priv); + void *priv; +}; + +struct pdr_list_node { + enum servreg_service_state curr_state; + u16 transaction_id; + struct pdr_service *pds; + struct list_head node; +}; + +static int pdr_locator_new_server(struct qmi_handle *qmi, + struct qmi_service *svc) +{ + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, + locator_hdl); + struct pdr_service *pds; + + /* Create a local client port for QMI communication */ + pdr->locator_addr.sq_family = AF_QIPCRTR; + pdr->locator_addr.sq_node = svc->node; + pdr->locator_addr.sq_port = svc->port; + + mutex_lock(&pdr->lock); + pdr->locator_init_complete = true; + mutex_unlock(&pdr->lock); + + /* Service pending lookup requests */ + mutex_lock(&pdr->list_lock); + list_for_each_entry(pds, &pdr->lookups, node) { + if (pds->need_locator_lookup) + schedule_work(&pdr->locator_work); + } + mutex_unlock(&pdr->list_lock); + + return 0; +} + +static void pdr_locator_del_server(struct qmi_handle *qmi, + struct qmi_service *svc) +{ + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, + locator_hdl); + + mutex_lock(&pdr->lock); + pdr->locator_init_complete = false; + mutex_unlock(&pdr->lock); + + pdr->locator_addr.sq_node = 0; + pdr->locator_addr.sq_port = 0; +} + +static struct qmi_ops pdr_locator_ops = { + .new_server = pdr_locator_new_server, + .del_server = pdr_locator_del_server, +}; + +static int pdr_register_listener(struct pdr_handle *pdr, + struct pdr_service *pds, + bool enable) +{ + struct servreg_register_listener_resp resp; + struct servreg_register_listener_req req; + struct qmi_txn txn; + int ret; + + ret = qmi_txn_init(&pdr->notifier_hdl, &txn, + servreg_register_listener_resp_ei, + &resp); + if (ret < 0) + return ret; + + req.enable = enable; + strcpy(req.service_path, pds->service_path); + + ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr, + &txn, SERVREG_REGISTER_LISTENER_REQ, + SERVREG_REGISTER_LISTENER_REQ_LEN, + servreg_register_listener_req_ei, + &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + return ret; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) { + pr_err("PDR: %s register listener txn wait failed: %d\n", + pds->service_path, ret); + return ret; + } + + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + pr_err("PDR: %s register listener failed: 0x%x\n", + pds->service_path, resp.resp.error); + return ret; + } + + if ((int)resp.curr_state < INT_MIN || (int)resp.curr_state > INT_MAX) + pr_err("PDR: %s notification state invalid: 0x%x\n", + pds->service_path, resp.curr_state); + + pds->state = resp.curr_state; + + return 0; +} + +static void pdr_notifier_work(struct work_struct *work) +{ + struct pdr_handle *pdr = container_of(work, struct pdr_handle, + notifier_work); + struct pdr_service *pds; + int ret; + + mutex_lock(&pdr->list_lock); + list_for_each_entry(pds, &pdr->lookups, node) { + if (pds->service_connected) { + if (!pds->need_notifier_register) + continue; + + pds->need_notifier_register = false; + ret = pdr_register_listener(pdr, pds, true); + if (ret < 0) + pds->state = SERVREG_SERVICE_STATE_DOWN; + } else { + if (!pds->need_notifier_remove) + continue; + + pds->need_notifier_remove = false; + pds->state = SERVREG_SERVICE_STATE_DOWN; + } + + mutex_lock(&pdr->status_lock); + pdr->status(pds->state, pds->service_path, pdr->priv); + mutex_unlock(&pdr->status_lock); + } + mutex_unlock(&pdr->list_lock); +} + +static int pdr_notifier_new_server(struct qmi_handle *qmi, + struct qmi_service *svc) +{ + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, + notifier_hdl); + struct pdr_service *pds; + + mutex_lock(&pdr->list_lock); + list_for_each_entry(pds, &pdr->lookups, node) { + if (pds->service == svc->service && + pds->instance == svc->instance) { + pds->service_connected = true; + pds->need_notifier_register = true; + pds->addr.sq_family = AF_QIPCRTR; + pds->addr.sq_node = svc->node; + pds->addr.sq_port = svc->port; + queue_work(pdr->notifier_wq, &pdr->notifier_work); + } + } + mutex_unlock(&pdr->list_lock); + + return 0; +} + +static void pdr_notifier_del_server(struct qmi_handle *qmi, + struct qmi_service *svc) +{ + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, + notifier_hdl); + struct pdr_service *pds; + + mutex_lock(&pdr->list_lock); + list_for_each_entry(pds, &pdr->lookups, node) { + if (pds->service == svc->service && + pds->instance == svc->instance) { + pds->service_connected = false; + pds->need_notifier_remove = true; + pds->addr.sq_node = 0; + pds->addr.sq_port = 0; + queue_work(pdr->notifier_wq, &pdr->notifier_work); + } + } + mutex_unlock(&pdr->list_lock); +} + +static struct qmi_ops pdr_notifier_ops = { + .new_server = pdr_notifier_new_server, + .del_server = pdr_notifier_del_server, +}; + +static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds, + u16 tid) +{ + struct servreg_set_ack_resp resp; + struct servreg_set_ack_req req; + struct qmi_txn txn; + int ret; + + ret = qmi_txn_init(&pdr->notifier_hdl, &txn, servreg_set_ack_resp_ei, + &resp); + if (ret < 0) + return ret; + + req.transaction_id = tid; + strcpy(req.service_path, pds->service_path); + + ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr, + &txn, SERVREG_SET_ACK_REQ, + SERVREG_SET_ACK_REQ_LEN, + servreg_set_ack_req_ei, + &req); + + /* Skip waiting for response */ + qmi_txn_cancel(&txn); + return ret; +} + +static void pdr_indack_work(struct work_struct *work) +{ + struct pdr_handle *pdr = container_of(work, struct pdr_handle, + indack_work); + struct pdr_list_node *ind, *tmp; + struct pdr_service *pds; + + list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) { + pds = ind->pds; + pdr_send_indack_msg(pdr, pds, ind->transaction_id); + + mutex_lock(&pdr->status_lock); + pds->state = ind->curr_state; + pdr->status(pds->state, pds->service_path, pdr->priv); + mutex_unlock(&pdr->status_lock); + + mutex_lock(&pdr->list_lock); + list_del(&ind->node); + mutex_unlock(&pdr->list_lock); + + kfree(ind); + } +} + +static void pdr_indication_cb(struct qmi_handle *qmi, + struct sockaddr_qrtr *sq, + struct qmi_txn *txn, const void *data) +{ + struct pdr_handle *pdr = container_of(qmi, struct pdr_handle, + notifier_hdl); + const struct servreg_state_updated_ind *ind_msg = data; + struct pdr_list_node *ind; + struct pdr_service *pds; + bool found; + + if (!ind_msg || !ind_msg->service_path[0] || + strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH) + return; + + mutex_lock(&pdr->list_lock); + list_for_each_entry(pds, &pdr->lookups, node) { + if (strcmp(pds->service_path, ind_msg->service_path)) + continue; + + found = true; + break; + } + mutex_unlock(&pdr->list_lock); + + if (!found) + return; + + pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n", + ind_msg->service_path, ind_msg->curr_state, + ind_msg->transaction_id); + + ind = kzalloc(sizeof(*ind), GFP_KERNEL); + if (!ind) + return; + + ind->transaction_id = ind_msg->transaction_id; + ind->curr_state = ind_msg->curr_state; + ind->pds = pds; + + mutex_lock(&pdr->list_lock); + list_add_tail(&ind->node, &pdr->indack_list); + mutex_unlock(&pdr->list_lock); + + queue_work(pdr->indack_wq, &pdr->indack_work); +} + +static struct qmi_msg_handler qmi_indication_handler[] = { + { + .type = QMI_INDICATION, + .msg_id = SERVREG_STATE_UPDATED_IND_ID, + .ei = servreg_state_updated_ind_ei, + .decoded_size = sizeof(struct servreg_state_updated_ind), + .fn = pdr_indication_cb, + }, + {} +}; + +static int pdr_get_domain_list(struct servreg_get_domain_list_req *req, + struct servreg_get_domain_list_resp *resp, + struct pdr_handle *pdr) +{ + struct qmi_txn txn; + int ret; + + ret = qmi_txn_init(&pdr->locator_hdl, &txn, + servreg_get_domain_list_resp_ei, resp); + if (ret < 0) + return ret; + + ret = qmi_send_request(&pdr->locator_hdl, + &pdr->locator_addr, + &txn, SERVREG_GET_DOMAIN_LIST_REQ, + SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN, + servreg_get_domain_list_req_ei, + req); + if (ret < 0) { + qmi_txn_cancel(&txn); + return ret; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) { + pr_err("PDR: %s get domain list txn wait failed: %d\n", + req->service_name, ret); + return ret; + } + + if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { + pr_err("PDR: %s get domain list failed: 0x%x\n", + req->service_name, resp->resp.error); + return -EREMOTEIO; + } + + return 0; +} + +static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds) +{ + struct servreg_get_domain_list_resp *resp; + struct servreg_get_domain_list_req req; + struct servreg_location_entry *entry; + int domains_read = 0; + int ret, i; + + resp = kzalloc(sizeof(*resp), GFP_KERNEL); + if (!resp) + return -ENOMEM; + + /* Prepare req message */ + strcpy(req.service_name, pds->service_name); + req.domain_offset_valid = true; + req.domain_offset = 0; + + do { + req.domain_offset = domains_read; + ret = pdr_get_domain_list(&req, resp, pdr); + if (ret < 0) + goto out; + + for (i = domains_read; i < resp->domain_list_len; i++) { + entry = &resp->domain_list[i]; + + if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name)) + continue; + + if (!strcmp(entry->name, pds->service_path)) { + pds->service_data_valid = entry->service_data_valid; + pds->service_data = entry->service_data; + pds->instance = entry->instance; + goto out; + } + } + + /* Update ret to indicate that the service is not yet found */ + ret = -ENXIO; + + /* Always read total_domains from the response msg */ + if (resp->domain_list_len > resp->total_domains) + resp->domain_list_len = resp->total_domains; + + domains_read += resp->domain_list_len; + } while (domains_read < resp->total_domains); +out: + kfree(resp); + return ret; +} + +static void pdr_notify_lookup_failure(struct pdr_handle *pdr, + struct pdr_service *pds, + int err) +{ + pr_err("PDR: service lookup for %s failed: %d\n", + pds->service_name, err); + + if (err == -ENXIO) + return; + + list_del(&pds->node); + pds->state = SERVREG_LOCATOR_ERR; + mutex_lock(&pdr->status_lock); + pdr->status(pds->state, pds->service_path, pdr->priv); + mutex_unlock(&pdr->status_lock); + kfree(pds); +} + +static void pdr_locator_work(struct work_struct *work) +{ + struct pdr_handle *pdr = container_of(work, struct pdr_handle, + locator_work); + struct pdr_service *pds, *tmp; + int ret = 0; + + /* Bail out early if the SERVREG LOCATOR QMI service is not up */ + mutex_lock(&pdr->lock); + if (!pdr->locator_init_complete) { + mutex_unlock(&pdr->lock); + pr_debug("PDR: SERVICE LOCATOR service not available\n"); + return; + } + mutex_unlock(&pdr->lock); + + mutex_lock(&pdr->list_lock); + list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) { + if (!pds->need_locator_lookup) + continue; + + ret = pdr_locate_service(pdr, pds); + if (ret < 0) { + pdr_notify_lookup_failure(pdr, pds, ret); + continue; + } + + ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1, + pds->instance); + if (ret < 0) { + pdr_notify_lookup_failure(pdr, pds, ret); + continue; + } + + pds->need_locator_lookup = false; + } + mutex_unlock(&pdr->list_lock); +} + +/** + * pdr_add_lookup() - register a tracking request for a PD + * @pdr: PDR client handle + * @service_name: service name of the tracking request + * @service_path: service path of the tracking request + * + * Registering a pdr lookup allows for tracking the life cycle of the PD. + * + * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is + * returned if a lookup is already in progress for the given service path. + */ +struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, + const char *service_name, + const char *service_path) +{ + struct pdr_service *pds, *tmp; + int ret; + + if (IS_ERR_OR_NULL(pdr)) + return ERR_PTR(-EINVAL); + + if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH || + !service_path || strlen(service_path) > SERVREG_NAME_LENGTH) + return ERR_PTR(-EINVAL); + + pds = kzalloc(sizeof(*pds), GFP_KERNEL); + if (!pds) + return ERR_PTR(-ENOMEM); + + pds->service = SERVREG_NOTIFIER_SERVICE; + strcpy(pds->service_name, service_name); + strcpy(pds->service_path, service_path); + pds->need_locator_lookup = true; + + mutex_lock(&pdr->list_lock); + list_for_each_entry(tmp, &pdr->lookups, node) { + if (strcmp(tmp->service_path, service_path)) + continue; + + mutex_unlock(&pdr->list_lock); + ret = -EALREADY; + goto err; + } + + list_add(&pds->node, &pdr->lookups); + mutex_unlock(&pdr->list_lock); + + schedule_work(&pdr->locator_work); + + return pds; +err: + kfree(pds); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(pdr_add_lookup); + +/** + * pdr_restart_pd() - restart PD + * @pdr: PDR client handle + * @pds: PD service handle + * + * Restarts the PD tracked by the PDR client handle for a given service path. + * + * Return: 0 on success, negative errno on failure. + */ +int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds) +{ + struct servreg_restart_pd_resp resp; + struct servreg_restart_pd_req req; + struct sockaddr_qrtr addr; + struct pdr_service *tmp; + struct qmi_txn txn; + int ret; + + if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds)) + return -EINVAL; + + mutex_lock(&pdr->list_lock); + list_for_each_entry(tmp, &pdr->lookups, node) { + if (tmp != pds) + continue; + + if (!pds->service_connected) + break; + + /* Prepare req message */ + strcpy(req.service_path, pds->service_path); + addr = pds->addr; + break; + } + mutex_unlock(&pdr->list_lock); + + if (!req.service_path[0]) + return -EINVAL; + + ret = qmi_txn_init(&pdr->notifier_hdl, &txn, + servreg_restart_pd_resp_ei, + &resp); + if (ret < 0) + return ret; + + ret = qmi_send_request(&pdr->notifier_hdl, &addr, + &txn, SERVREG_RESTART_PD_REQ, + SERVREG_RESTART_PD_REQ_MAX_LEN, + servreg_restart_pd_req_ei, &req); + if (ret < 0) { + qmi_txn_cancel(&txn); + return ret; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) { + pr_err("PDR: %s PD restart txn wait failed: %d\n", + req.service_path, ret); + return ret; + } + + /* Check response if PDR is disabled */ + if (resp.resp.result == QMI_RESULT_FAILURE_V01 && + resp.resp.error == QMI_ERR_DISABLED_V01) { + pr_err("PDR: %s PD restart is disabled: 0x%x\n", + req.service_path, resp.resp.error); + return -EOPNOTSUPP; + } + + /* Check the response for other error case*/ + if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { + pr_err("PDR: %s request for PD restart failed: 0x%x\n", + req.service_path, resp.resp.error); + return -EREMOTEIO; + } + + return 0; +} +EXPORT_SYMBOL(pdr_restart_pd); + +/** + * pdr_handle_alloc() - initialize the PDR client handle + * @status: function to be called on PD state change + * @priv: handle for client's use + * + * Initializes the PDR client handle to allow for tracking/restart of PDs. + * + * Return: pdr_handle object on success, ERR_PTR on failure. + */ +struct pdr_handle *pdr_handle_alloc(void (*status)(int state, + char *service_path, + void *priv), void *priv) +{ + struct pdr_handle *pdr; + int ret; + + if (!status) + return ERR_PTR(-EINVAL); + + pdr = kzalloc(sizeof(*pdr), GFP_KERNEL); + if (!pdr) + return ERR_PTR(-ENOMEM); + + pdr->status = status; + pdr->priv = priv; + + mutex_init(&pdr->status_lock); + mutex_init(&pdr->list_lock); + mutex_init(&pdr->lock); + + INIT_LIST_HEAD(&pdr->lookups); + INIT_LIST_HEAD(&pdr->indack_list); + + INIT_WORK(&pdr->locator_work, pdr_locator_work); + INIT_WORK(&pdr->notifier_work, pdr_notifier_work); + INIT_WORK(&pdr->indack_work, pdr_indack_work); + + pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq"); + if (!pdr->notifier_wq) { + ret = -ENOMEM; + goto free_pdr_handle; + } + + pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI); + if (!pdr->indack_wq) { + ret = -ENOMEM; + goto destroy_notifier; + } + + ret = qmi_handle_init(&pdr->locator_hdl, + SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN, + &pdr_locator_ops, NULL); + if (ret < 0) + goto destroy_indack; + + ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1); + if (ret < 0) + goto release_qmi_handle; + + ret = qmi_handle_init(&pdr->notifier_hdl, + SERVREG_STATE_UPDATED_IND_MAX_LEN, + &pdr_notifier_ops, + qmi_indication_handler); + if (ret < 0) + goto release_qmi_handle; + + return pdr; + +release_qmi_handle: + qmi_handle_release(&pdr->locator_hdl); +destroy_indack: + destroy_workqueue(pdr->indack_wq); +destroy_notifier: + destroy_workqueue(pdr->notifier_wq); +free_pdr_handle: + kfree(pdr); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL(pdr_handle_alloc); + +/** + * pdr_handle_release() - release the PDR client handle + * @pdr: PDR client handle + * + * Cleans up pending tracking requests and releases the underlying qmi handles. + */ +void pdr_handle_release(struct pdr_handle *pdr) +{ + struct pdr_service *pds, *tmp; + + if (IS_ERR_OR_NULL(pdr)) + return; + + mutex_lock(&pdr->list_lock); + list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) { + list_del(&pds->node); + kfree(pds); + } + mutex_unlock(&pdr->list_lock); + + cancel_work_sync(&pdr->locator_work); + cancel_work_sync(&pdr->notifier_work); + cancel_work_sync(&pdr->indack_work); + + destroy_workqueue(pdr->notifier_wq); + destroy_workqueue(pdr->indack_wq); + + qmi_handle_release(&pdr->locator_hdl); + qmi_handle_release(&pdr->notifier_hdl); + + kfree(pdr); +} +EXPORT_SYMBOL(pdr_handle_release); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers"); diff --git a/drivers/soc/qcom/pdr_internal.h b/drivers/soc/qcom/pdr_internal.h new file mode 100644 index 000000000000..15b5002e4127 --- /dev/null +++ b/drivers/soc/qcom/pdr_internal.h @@ -0,0 +1,379 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __QCOM_PDR_HELPER_INTERNAL__ +#define __QCOM_PDR_HELPER_INTERNAL__ + +#include + +#define SERVREG_LOCATOR_SERVICE 0x40 +#define SERVREG_NOTIFIER_SERVICE 0x42 + +#define SERVREG_REGISTER_LISTENER_REQ 0x20 +#define SERVREG_GET_DOMAIN_LIST_REQ 0x21 +#define SERVREG_STATE_UPDATED_IND_ID 0x22 +#define SERVREG_SET_ACK_REQ 0x23 +#define SERVREG_RESTART_PD_REQ 0x24 + +#define SERVREG_DOMAIN_LIST_LENGTH 32 +#define SERVREG_RESTART_PD_REQ_MAX_LEN 67 +#define SERVREG_REGISTER_LISTENER_REQ_LEN 71 +#define SERVREG_SET_ACK_REQ_LEN 72 +#define SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN 74 +#define SERVREG_STATE_UPDATED_IND_MAX_LEN 79 +#define SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN 2389 + +struct servreg_location_entry { + char name[SERVREG_NAME_LENGTH + 1]; + u8 service_data_valid; + u32 service_data; + u32 instance; +}; + +struct qmi_elem_info servreg_location_entry_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + name), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + instance), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + service_data_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct servreg_location_entry, + service_data), + }, + {} +}; + +struct servreg_get_domain_list_req { + char service_name[SERVREG_NAME_LENGTH + 1]; + u8 domain_offset_valid; + u32 domain_offset; +}; + +struct qmi_elem_info servreg_get_domain_list_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_get_domain_list_req, + service_name), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_req, + domain_offset_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_req, + domain_offset), + }, + {} +}; + +struct servreg_get_domain_list_resp { + struct qmi_response_type_v01 resp; + u8 total_domains_valid; + u16 total_domains; + u8 db_rev_count_valid; + u16 db_rev_count; + u8 domain_list_valid; + u32 domain_list_len; + struct servreg_location_entry domain_list[SERVREG_DOMAIN_LIST_LENGTH]; +}; + +struct qmi_elem_info servreg_get_domain_list_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_get_domain_list_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_resp, + total_domains_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_get_domain_list_resp, + total_domains), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct servreg_get_domain_list_resp, + db_rev_count_valid), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct servreg_get_domain_list_resp, + db_rev_count), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct servreg_get_domain_list_resp, + domain_list_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct servreg_get_domain_list_resp, + domain_list_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = SERVREG_DOMAIN_LIST_LENGTH, + .elem_size = sizeof(struct servreg_location_entry), + .array_type = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct servreg_get_domain_list_resp, + domain_list), + .ei_array = servreg_location_entry_ei, + }, + {} +}; + +struct servreg_register_listener_req { + u8 enable; + char service_path[SERVREG_NAME_LENGTH + 1]; +}; + +struct qmi_elem_info servreg_register_listener_req_ei[] = { + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_register_listener_req, + enable), + }, + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_register_listener_req, + service_path), + }, + {} +}; + +struct servreg_register_listener_resp { + struct qmi_response_type_v01 resp; + u8 curr_state_valid; + enum servreg_service_state curr_state; +}; + +struct qmi_elem_info servreg_register_listener_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_register_listener_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_register_listener_resp, + curr_state_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum servreg_service_state), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct servreg_register_listener_resp, + curr_state), + }, + {} +}; + +struct servreg_restart_pd_req { + char service_path[SERVREG_NAME_LENGTH + 1]; +}; + +struct qmi_elem_info servreg_restart_pd_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_restart_pd_req, + service_path), + }, + {} +}; + +struct servreg_restart_pd_resp { + struct qmi_response_type_v01 resp; +}; + +struct qmi_elem_info servreg_restart_pd_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_restart_pd_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; + +struct servreg_state_updated_ind { + enum servreg_service_state curr_state; + char service_path[SERVREG_NAME_LENGTH + 1]; + u16 transaction_id; +}; + +struct qmi_elem_info servreg_state_updated_ind_ei[] = { + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(u32), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_state_updated_ind, + curr_state), + }, + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_state_updated_ind, + service_path), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct servreg_state_updated_ind, + transaction_id), + }, + {} +}; + +struct servreg_set_ack_req { + char service_path[SERVREG_NAME_LENGTH + 1]; + u16 transaction_id; +}; + +struct qmi_elem_info servreg_set_ack_req_ei[] = { + { + .data_type = QMI_STRING, + .elem_len = SERVREG_NAME_LENGTH + 1, + .elem_size = sizeof(char), + .array_type = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct servreg_set_ack_req, + service_path), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_set_ack_req, + transaction_id), + }, + {} +}; + +struct servreg_set_ack_resp { + struct qmi_response_type_v01 resp; +}; + +struct qmi_elem_info servreg_set_ack_resp_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct servreg_set_ack_resp, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + {} +}; + +#endif diff --git a/include/linux/soc/qcom/pdr.h b/include/linux/soc/qcom/pdr.h new file mode 100644 index 000000000000..83a8ea612e69 --- /dev/null +++ b/include/linux/soc/qcom/pdr.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __QCOM_PDR_HELPER__ +#define __QCOM_PDR_HELPER__ + +#include + +#define SERVREG_NAME_LENGTH 64 + +struct pdr_service; +struct pdr_handle; + +enum servreg_service_state { + SERVREG_LOCATOR_ERR = 0x1, + SERVREG_SERVICE_STATE_DOWN = 0x0FFFFFFF, + SERVREG_SERVICE_STATE_UP = 0x1FFFFFFF, + SERVREG_SERVICE_STATE_EARLY_DOWN = 0x2FFFFFFF, + SERVREG_SERVICE_STATE_UNINIT = 0x7FFFFFFF, +}; + +struct pdr_handle *pdr_handle_alloc(void (*status)(int state, + char *service_path, + void *priv), void *priv); +struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr, + const char *service_name, + const char *service_path); +int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds); +void pdr_handle_release(struct pdr_handle *pdr); + +#endif diff --git a/include/linux/soc/qcom/qmi.h b/include/linux/soc/qcom/qmi.h index 5efa2b67fa55..e712f94b89fc 100644 --- a/include/linux/soc/qcom/qmi.h +++ b/include/linux/soc/qcom/qmi.h @@ -88,6 +88,7 @@ struct qmi_elem_info { #define QMI_ERR_CLIENT_IDS_EXHAUSTED_V01 5 #define QMI_ERR_INVALID_ID_V01 41 #define QMI_ERR_ENCODING_V01 58 +#define QMI_ERR_DISABLED_V01 69 #define QMI_ERR_INCOMPATIBLE_STATE_V01 90 #define QMI_ERR_NOT_SUPPORTED_V01 94 -- cgit v1.2.3 From 83473566260288c560e5443ea4cc40a458aa9e6a Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Thu, 12 Mar 2020 17:38:42 +0530 Subject: soc: qcom: apr: Add avs/audio tracking functionality Use PDR helper functions to track the protection domains that the apr services are dependent upon on SDM845 SoC, specifically the "avs/audio" service running on ADSP Q6. Reviewed-by: Bjorn Andersson Signed-off-by: Sibi Sankar Link: https://lore.kernel.org/r/20200312120842.21991-4-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 1 + drivers/soc/qcom/apr.c | 123 +++++++++++++++++++++++++++++++++++++++---- include/linux/soc/qcom/apr.h | 1 + 3 files changed, 116 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 48501f0245b0..9ac6b0072e8c 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -200,6 +200,7 @@ config QCOM_APR tristate "Qualcomm APR Bus (Asynchronous Packet Router)" depends on ARCH_QCOM || COMPILE_TEST depends on RPMSG + select QCOM_PDR_HELPERS help Enable APR IPC protocol support between application processor and QDSP6. APR is diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 4fcc32420c47..1f35b097c635 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -21,6 +22,7 @@ struct apr { spinlock_t rx_lock; struct idr svcs_idr; int dest_domain_id; + struct pdr_handle *pdr; struct workqueue_struct *rxwq; struct work_struct rx_work; struct list_head rx_list; @@ -289,6 +291,9 @@ static int apr_add_device(struct device *dev, struct device_node *np, id->svc_id + 1, GFP_ATOMIC); spin_unlock(&apr->svcs_lock); + of_property_read_string_index(np, "qcom,protection-domain", + 1, &adev->service_path); + dev_info(dev, "Adding APR dev: %s\n", dev_name(&adev->dev)); ret = device_register(&adev->dev); @@ -300,14 +305,75 @@ static int apr_add_device(struct device *dev, struct device_node *np, return ret; } -static void of_register_apr_devices(struct device *dev) +static int of_apr_add_pd_lookups(struct device *dev) +{ + const char *service_name, *service_path; + struct apr *apr = dev_get_drvdata(dev); + struct device_node *node; + struct pdr_service *pds; + int ret; + + for_each_child_of_node(dev->of_node, node) { + ret = of_property_read_string_index(node, "qcom,protection-domain", + 0, &service_name); + if (ret < 0) + continue; + + ret = of_property_read_string_index(node, "qcom,protection-domain", + 1, &service_path); + if (ret < 0) { + dev_err(dev, "pdr service path missing: %d\n", ret); + return ret; + } + + pds = pdr_add_lookup(apr->pdr, service_name, service_path); + if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) { + dev_err(dev, "pdr add lookup failed: %d\n", ret); + return PTR_ERR(pds); + } + } + + return 0; +} + +static void of_register_apr_devices(struct device *dev, const char *svc_path) { struct apr *apr = dev_get_drvdata(dev); struct device_node *node; + const char *service_path; + int ret; for_each_child_of_node(dev->of_node, node) { struct apr_device_id id = { {0} }; + /* + * This function is called with svc_path NULL during + * apr_probe(), in which case we register any apr devices + * without a qcom,protection-domain specified. + * + * Then as the protection domains becomes available + * (if applicable) this function is again called, but with + * svc_path representing the service becoming available. In + * this case we register any apr devices with a matching + * qcom,protection-domain. + */ + + ret = of_property_read_string_index(node, "qcom,protection-domain", + 1, &service_path); + if (svc_path) { + /* skip APR services that are PD independent */ + if (ret) + continue; + + /* skip APR services whose PD paths don't match */ + if (strcmp(service_path, svc_path)) + continue; + } else { + /* skip APR services whose PD lookups are registered */ + if (ret == 0) + continue; + } + if (of_property_read_u32(node, "reg", &id.svc_id)) continue; @@ -318,6 +384,34 @@ static void of_register_apr_devices(struct device *dev) } } +static int apr_remove_device(struct device *dev, void *svc_path) +{ + struct apr_device *adev = to_apr_device(dev); + + if (svc_path && adev->service_path) { + if (!strcmp(adev->service_path, (char *)svc_path)) + device_unregister(&adev->dev); + } else { + device_unregister(&adev->dev); + } + + return 0; +} + +static void apr_pd_status(int state, char *svc_path, void *priv) +{ + struct apr *apr = (struct apr *)priv; + + switch (state) { + case SERVREG_SERVICE_STATE_UP: + of_register_apr_devices(apr->dev, svc_path); + break; + case SERVREG_SERVICE_STATE_DOWN: + device_for_each_child(apr->dev, svc_path, apr_remove_device); + break; + } +} + static int apr_probe(struct rpmsg_device *rpdev) { struct device *dev = &rpdev->dev; @@ -343,28 +437,39 @@ static int apr_probe(struct rpmsg_device *rpdev) return -ENOMEM; } INIT_WORK(&apr->rx_work, apr_rxwq); + + apr->pdr = pdr_handle_alloc(apr_pd_status, apr); + if (IS_ERR(apr->pdr)) { + dev_err(dev, "Failed to init PDR handle\n"); + ret = PTR_ERR(apr->pdr); + goto destroy_wq; + } + INIT_LIST_HEAD(&apr->rx_list); spin_lock_init(&apr->rx_lock); spin_lock_init(&apr->svcs_lock); idr_init(&apr->svcs_idr); - of_register_apr_devices(dev); - - return 0; -} -static int apr_remove_device(struct device *dev, void *null) -{ - struct apr_device *adev = to_apr_device(dev); + ret = of_apr_add_pd_lookups(dev); + if (ret) + goto handle_release; - device_unregister(&adev->dev); + of_register_apr_devices(dev, NULL); return 0; + +handle_release: + pdr_handle_release(apr->pdr); +destroy_wq: + destroy_workqueue(apr->rxwq); + return ret; } static void apr_remove(struct rpmsg_device *rpdev) { struct apr *apr = dev_get_drvdata(&rpdev->dev); + pdr_handle_release(apr->pdr); device_for_each_child(&rpdev->dev, NULL, apr_remove_device); flush_workqueue(apr->rxwq); destroy_workqueue(apr->rxwq); diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index c5d52e2cb275..7f0bc3cf4d61 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -85,6 +85,7 @@ struct apr_device { uint16_t domain_id; uint32_t version; char name[APR_NAME_SIZE]; + const char *service_path; spinlock_t lock; struct list_head node; }; -- cgit v1.2.3 From f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10 Mon Sep 17 00:00:00 2001 From: Andrey Konovalov Date: Mon, 24 Feb 2020 17:13:03 +0100 Subject: usb: gadget: add raw-gadget interface USB Raw Gadget is a kernel module that provides a userspace interface for the USB Gadget subsystem. Essentially it allows to emulate USB devices from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is currently a strictly debugging feature and shouldn't be used in production. Raw Gadget is similar to GadgetFS, but provides a more low-level and direct access to the USB Gadget layer for the userspace. The key differences are: 1. Every USB request is passed to the userspace to get a response, while GadgetFS responds to some USB requests internally based on the provided descriptors. However note, that the UDC driver might respond to some requests on its own and never forward them to the Gadget layer. 2. GadgetFS performs some sanity checks on the provided USB descriptors, while Raw Gadget allows you to provide arbitrary data as responses to USB requests. 3. Raw Gadget provides a way to select a UDC device/driver to bind to, while GadgetFS currently binds to the first available UDC. 4. Raw Gadget uses predictable endpoint names (handles) across different UDCs (as long as UDCs have enough endpoints of each required transfer type). 5. Raw Gadget has ioctl-based interface instead of a filesystem-based one. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Andrey Konovalov Signed-off-by: Felipe Balbi --- Documentation/usb/index.rst | 1 + Documentation/usb/raw-gadget.rst | 61 ++ drivers/usb/gadget/legacy/Kconfig | 11 + drivers/usb/gadget/legacy/Makefile | 1 + drivers/usb/gadget/legacy/raw_gadget.c | 1078 ++++++++++++++++++++++++++++++++ include/uapi/linux/usb/raw_gadget.h | 167 +++++ 6 files changed, 1319 insertions(+) create mode 100644 Documentation/usb/raw-gadget.rst create mode 100644 drivers/usb/gadget/legacy/raw_gadget.c create mode 100644 include/uapi/linux/usb/raw_gadget.h (limited to 'include') diff --git a/Documentation/usb/index.rst b/Documentation/usb/index.rst index 36b6ebd9a9d9..b656c9be23ed 100644 --- a/Documentation/usb/index.rst +++ b/Documentation/usb/index.rst @@ -22,6 +22,7 @@ USB support misc_usbsevseg mtouchusb ohci + raw-gadget usbip_protocol usbmon usb-serial diff --git a/Documentation/usb/raw-gadget.rst b/Documentation/usb/raw-gadget.rst new file mode 100644 index 000000000000..9e78cb858f86 --- /dev/null +++ b/Documentation/usb/raw-gadget.rst @@ -0,0 +1,61 @@ +============== +USB Raw Gadget +============== + +USB Raw Gadget is a kernel module that provides a userspace interface for +the USB Gadget subsystem. Essentially it allows to emulate USB devices +from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is +currently a strictly debugging feature and shouldn't be used in +production, use GadgetFS instead. + +Comparison to GadgetFS +~~~~~~~~~~~~~~~~~~~~~~ + +Raw Gadget is similar to GadgetFS, but provides a more low-level and +direct access to the USB Gadget layer for the userspace. The key +differences are: + +1. Every USB request is passed to the userspace to get a response, while + GadgetFS responds to some USB requests internally based on the provided + descriptors. However note, that the UDC driver might respond to some + requests on its own and never forward them to the Gadget layer. + +2. GadgetFS performs some sanity checks on the provided USB descriptors, + while Raw Gadget allows you to provide arbitrary data as responses to + USB requests. + +3. Raw Gadget provides a way to select a UDC device/driver to bind to, + while GadgetFS currently binds to the first available UDC. + +4. Raw Gadget uses predictable endpoint names (handles) across different + UDCs (as long as UDCs have enough endpoints of each required transfer + type). + +5. Raw Gadget has ioctl-based interface instead of a filesystem-based one. + +Userspace interface +~~~~~~~~~~~~~~~~~~~ + +To create a Raw Gadget instance open /dev/raw-gadget. Multiple raw-gadget +instances (bound to different UDCs) can be used at the same time. The +interaction with the opened file happens through the ioctl() calls, see +comments in include/uapi/linux/usb/raw_gadget.h for details. + +The typical usage of Raw Gadget looks like: + +1. Open Raw Gadget instance via /dev/raw-gadget. +2. Initialize the instance via USB_RAW_IOCTL_INIT. +3. Launch the instance with USB_RAW_IOCTL_RUN. +4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from + Raw Gadget and react to those depending on what kind of USB device + needs to be emulated. + +Potential future improvements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Implement ioctl's for setting/clearing halt status on endpoints. + +- Reporting more events (suspend, resume, etc.) through + USB_RAW_IOCTL_EVENT_FETCH. + +- Support O_NONBLOCK I/O. diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig index 6e7e1a9202e6..a7064e21d9f2 100644 --- a/drivers/usb/gadget/legacy/Kconfig +++ b/drivers/usb/gadget/legacy/Kconfig @@ -516,4 +516,15 @@ config USB_G_WEBCAM Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_webcam". +config USB_RAW_GADGET + tristate "USB Raw Gadget" + help + USB Raw Gadget is a kernel module that provides a userspace interface + for the USB Gadget subsystem. Essentially it allows to emulate USB + devices from userspace. See Documentation/usb/raw-gadget.rst for + details. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "raw_gadget". + endchoice diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile index abd0c3e66a05..4d864bf82799 100644 --- a/drivers/usb/gadget/legacy/Makefile +++ b/drivers/usb/gadget/legacy/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o +obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c new file mode 100644 index 000000000000..76406343fbe5 --- /dev/null +++ b/drivers/usb/gadget/legacy/raw_gadget.c @@ -0,0 +1,1078 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Raw Gadget driver. + * See Documentation/usb/raw-gadget.rst for more details. + * + * Andrey Konovalov + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define DRIVER_DESC "USB Raw Gadget" +#define DRIVER_NAME "raw-gadget" + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR("Andrey Konovalov"); +MODULE_LICENSE("GPL"); + +/*----------------------------------------------------------------------*/ + +#define RAW_EVENT_QUEUE_SIZE 16 + +struct raw_event_queue { + /* See the comment in raw_event_queue_fetch() for locking details. */ + spinlock_t lock; + struct semaphore sema; + struct usb_raw_event *events[RAW_EVENT_QUEUE_SIZE]; + int size; +}; + +static void raw_event_queue_init(struct raw_event_queue *queue) +{ + spin_lock_init(&queue->lock); + sema_init(&queue->sema, 0); + queue->size = 0; +} + +static int raw_event_queue_add(struct raw_event_queue *queue, + enum usb_raw_event_type type, size_t length, const void *data) +{ + unsigned long flags; + struct usb_raw_event *event; + + spin_lock_irqsave(&queue->lock, flags); + if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) { + spin_unlock_irqrestore(&queue->lock, flags); + return -ENOMEM; + } + event = kmalloc(sizeof(*event) + length, GFP_ATOMIC); + if (!event) { + spin_unlock_irqrestore(&queue->lock, flags); + return -ENOMEM; + } + event->type = type; + event->length = length; + if (event->length) + memcpy(&event->data[0], data, length); + queue->events[queue->size] = event; + queue->size++; + up(&queue->sema); + spin_unlock_irqrestore(&queue->lock, flags); + return 0; +} + +static struct usb_raw_event *raw_event_queue_fetch( + struct raw_event_queue *queue) +{ + unsigned long flags; + struct usb_raw_event *event; + + /* + * This function can be called concurrently. We first check that + * there's at least one event queued by decrementing the semaphore, + * and then take the lock to protect queue struct fields. + */ + if (down_interruptible(&queue->sema)) + return NULL; + spin_lock_irqsave(&queue->lock, flags); + if (WARN_ON(!queue->size)) + return NULL; + event = queue->events[0]; + queue->size--; + memmove(&queue->events[0], &queue->events[1], + queue->size * sizeof(queue->events[0])); + spin_unlock_irqrestore(&queue->lock, flags); + return event; +} + +static void raw_event_queue_destroy(struct raw_event_queue *queue) +{ + int i; + + for (i = 0; i < queue->size; i++) + kfree(queue->events[i]); + queue->size = 0; +} + +/*----------------------------------------------------------------------*/ + +struct raw_dev; + +#define USB_RAW_MAX_ENDPOINTS 32 + +enum ep_state { + STATE_EP_DISABLED, + STATE_EP_ENABLED, +}; + +struct raw_ep { + struct raw_dev *dev; + enum ep_state state; + struct usb_ep *ep; + struct usb_request *req; + bool urb_queued; + bool disabling; + ssize_t status; +}; + +enum dev_state { + STATE_DEV_INVALID = 0, + STATE_DEV_OPENED, + STATE_DEV_INITIALIZED, + STATE_DEV_RUNNING, + STATE_DEV_CLOSED, + STATE_DEV_FAILED +}; + +struct raw_dev { + struct kref count; + spinlock_t lock; + + const char *udc_name; + struct usb_gadget_driver driver; + + /* Reference to misc device: */ + struct device *dev; + + /* Protected by lock: */ + enum dev_state state; + bool gadget_registered; + struct usb_gadget *gadget; + struct usb_request *req; + bool ep0_in_pending; + bool ep0_out_pending; + bool ep0_urb_queued; + ssize_t ep0_status; + struct raw_ep eps[USB_RAW_MAX_ENDPOINTS]; + + struct completion ep0_done; + struct raw_event_queue queue; +}; + +static struct raw_dev *dev_new(void) +{ + struct raw_dev *dev; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + /* Matches kref_put() in raw_release(). */ + kref_init(&dev->count); + spin_lock_init(&dev->lock); + init_completion(&dev->ep0_done); + raw_event_queue_init(&dev->queue); + return dev; +} + +static void dev_free(struct kref *kref) +{ + struct raw_dev *dev = container_of(kref, struct raw_dev, count); + int i; + + kfree(dev->udc_name); + kfree(dev->driver.udc_name); + if (dev->req) { + if (dev->ep0_urb_queued) + usb_ep_dequeue(dev->gadget->ep0, dev->req); + usb_ep_free_request(dev->gadget->ep0, dev->req); + } + raw_event_queue_destroy(&dev->queue); + for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) { + if (dev->eps[i].state != STATE_EP_ENABLED) + continue; + usb_ep_disable(dev->eps[i].ep); + usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req); + kfree(dev->eps[i].ep->desc); + dev->eps[i].state = STATE_EP_DISABLED; + } + kfree(dev); +} + +/*----------------------------------------------------------------------*/ + +static int raw_queue_event(struct raw_dev *dev, + enum usb_raw_event_type type, size_t length, const void *data) +{ + int ret = 0; + unsigned long flags; + + ret = raw_event_queue_add(&dev->queue, type, length, data); + if (ret < 0) { + spin_lock_irqsave(&dev->lock, flags); + dev->state = STATE_DEV_FAILED; + spin_unlock_irqrestore(&dev->lock, flags); + } + return ret; +} + +static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct raw_dev *dev = req->context; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (req->status) + dev->ep0_status = req->status; + else + dev->ep0_status = req->actual; + if (dev->ep0_in_pending) + dev->ep0_in_pending = false; + else + dev->ep0_out_pending = false; + spin_unlock_irqrestore(&dev->lock, flags); + + complete(&dev->ep0_done); +} + +static int gadget_bind(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + int ret = 0; + struct raw_dev *dev = container_of(driver, struct raw_dev, driver); + struct usb_request *req; + unsigned long flags; + + if (strcmp(gadget->name, dev->udc_name) != 0) + return -ENODEV; + + set_gadget_data(gadget, dev); + req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (!req) { + dev_err(&gadget->dev, "usb_ep_alloc_request failed\n"); + set_gadget_data(gadget, NULL); + return -ENOMEM; + } + + spin_lock_irqsave(&dev->lock, flags); + dev->req = req; + dev->req->context = dev; + dev->req->complete = gadget_ep0_complete; + dev->gadget = gadget; + spin_unlock_irqrestore(&dev->lock, flags); + + /* Matches kref_put() in gadget_unbind(). */ + kref_get(&dev->count); + + ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL); + if (ret < 0) + dev_err(&gadget->dev, "failed to queue event\n"); + + return ret; +} + +static void gadget_unbind(struct usb_gadget *gadget) +{ + struct raw_dev *dev = get_gadget_data(gadget); + + set_gadget_data(gadget, NULL); + /* Matches kref_get() in gadget_bind(). */ + kref_put(&dev->count, dev_free); +} + +static int gadget_setup(struct usb_gadget *gadget, + const struct usb_ctrlrequest *ctrl) +{ + int ret = 0; + struct raw_dev *dev = get_gadget_data(gadget); + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_err(&gadget->dev, "ignoring, device is not running\n"); + ret = -ENODEV; + goto out_unlock; + } + if (dev->ep0_in_pending || dev->ep0_out_pending) { + dev_dbg(&gadget->dev, "stalling, request already pending\n"); + ret = -EBUSY; + goto out_unlock; + } + if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength) + dev->ep0_in_pending = true; + else + dev->ep0_out_pending = true; + spin_unlock_irqrestore(&dev->lock, flags); + + ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl); + if (ret < 0) + dev_err(&gadget->dev, "failed to queue event\n"); + goto out; + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); +out: + return ret; +} + +/* These are currently unused but present in case UDC driver requires them. */ +static void gadget_disconnect(struct usb_gadget *gadget) { } +static void gadget_suspend(struct usb_gadget *gadget) { } +static void gadget_resume(struct usb_gadget *gadget) { } +static void gadget_reset(struct usb_gadget *gadget) { } + +/*----------------------------------------------------------------------*/ + +static struct miscdevice raw_misc_device; + +static int raw_open(struct inode *inode, struct file *fd) +{ + struct raw_dev *dev; + + /* Nonblocking I/O is not supported yet. */ + if (fd->f_flags & O_NONBLOCK) + return -EINVAL; + + dev = dev_new(); + if (!dev) + return -ENOMEM; + fd->private_data = dev; + dev->state = STATE_DEV_OPENED; + dev->dev = raw_misc_device.this_device; + return 0; +} + +static int raw_release(struct inode *inode, struct file *fd) +{ + int ret = 0; + struct raw_dev *dev = fd->private_data; + unsigned long flags; + bool unregister = false; + + spin_lock_irqsave(&dev->lock, flags); + dev->state = STATE_DEV_CLOSED; + if (!dev->gadget) { + spin_unlock_irqrestore(&dev->lock, flags); + goto out_put; + } + if (dev->gadget_registered) + unregister = true; + dev->gadget_registered = false; + spin_unlock_irqrestore(&dev->lock, flags); + + if (unregister) { + ret = usb_gadget_unregister_driver(&dev->driver); + if (ret != 0) + dev_err(dev->dev, + "usb_gadget_unregister_driver() failed with %d\n", + ret); + /* Matches kref_get() in raw_ioctl_run(). */ + kref_put(&dev->count, dev_free); + } + +out_put: + /* Matches dev_new() in raw_open(). */ + kref_put(&dev->count, dev_free); + return ret; +} + +/*----------------------------------------------------------------------*/ + +static int raw_ioctl_init(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + struct usb_raw_init arg; + char *udc_driver_name; + char *udc_device_name; + unsigned long flags; + + ret = copy_from_user(&arg, (void __user *)value, sizeof(arg)); + if (ret) + return ret; + + switch (arg.speed) { + case USB_SPEED_UNKNOWN: + arg.speed = USB_SPEED_HIGH; + break; + case USB_SPEED_LOW: + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + case USB_SPEED_SUPER: + break; + default: + return -EINVAL; + } + + udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL); + if (!udc_driver_name) + return -ENOMEM; + ret = strscpy(udc_driver_name, &arg.driver_name[0], + UDC_NAME_LENGTH_MAX); + if (ret < 0) { + kfree(udc_driver_name); + return ret; + } + ret = 0; + + udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL); + if (!udc_device_name) { + kfree(udc_driver_name); + return -ENOMEM; + } + ret = strscpy(udc_device_name, &arg.device_name[0], + UDC_NAME_LENGTH_MAX); + if (ret < 0) { + kfree(udc_driver_name); + kfree(udc_device_name); + return ret; + } + ret = 0; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_OPENED) { + dev_dbg(dev->dev, "fail, device is not opened\n"); + kfree(udc_driver_name); + kfree(udc_device_name); + ret = -EINVAL; + goto out_unlock; + } + dev->udc_name = udc_driver_name; + + dev->driver.function = DRIVER_DESC; + dev->driver.max_speed = arg.speed; + dev->driver.setup = gadget_setup; + dev->driver.disconnect = gadget_disconnect; + dev->driver.bind = gadget_bind; + dev->driver.unbind = gadget_unbind; + dev->driver.suspend = gadget_suspend; + dev->driver.resume = gadget_resume; + dev->driver.reset = gadget_reset; + dev->driver.driver.name = DRIVER_NAME; + dev->driver.udc_name = udc_device_name; + dev->driver.match_existing_only = 1; + + dev->state = STATE_DEV_INITIALIZED; + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int raw_ioctl_run(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + unsigned long flags; + + if (value) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_INITIALIZED) { + dev_dbg(dev->dev, "fail, device is not initialized\n"); + ret = -EINVAL; + goto out_unlock; + } + spin_unlock_irqrestore(&dev->lock, flags); + + ret = usb_gadget_probe_driver(&dev->driver); + + spin_lock_irqsave(&dev->lock, flags); + if (ret) { + dev_err(dev->dev, + "fail, usb_gadget_probe_driver returned %d\n", ret); + dev->state = STATE_DEV_FAILED; + goto out_unlock; + } + dev->gadget_registered = true; + dev->state = STATE_DEV_RUNNING; + /* Matches kref_put() in raw_release(). */ + kref_get(&dev->count); + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + struct usb_raw_event arg; + unsigned long flags; + struct usb_raw_event *event; + uint32_t length; + + ret = copy_from_user(&arg, (void __user *)value, sizeof(arg)); + if (ret) + return ret; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + spin_unlock_irqrestore(&dev->lock, flags); + return -EINVAL; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + spin_unlock_irqrestore(&dev->lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&dev->lock, flags); + + event = raw_event_queue_fetch(&dev->queue); + if (!event) { + dev_dbg(&dev->gadget->dev, "event fetching interrupted\n"); + return -EINTR; + } + length = min(arg.length, event->length); + ret = copy_to_user((void __user *)value, event, + sizeof(*event) + length); + return ret; +} + +static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr, + bool get_from_user) +{ + int ret; + void *data; + + ret = copy_from_user(io, ptr, sizeof(*io)); + if (ret) + return ERR_PTR(ret); + if (io->ep >= USB_RAW_MAX_ENDPOINTS) + return ERR_PTR(-EINVAL); + if (!usb_raw_io_flags_valid(io->flags)) + return ERR_PTR(-EINVAL); + if (io->length > PAGE_SIZE) + return ERR_PTR(-EINVAL); + if (get_from_user) + data = memdup_user(ptr + sizeof(*io), io->length); + else { + data = kmalloc(io->length, GFP_KERNEL); + if (!data) + data = ERR_PTR(-ENOMEM); + } + return data; +} + +static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io, + void *data, bool in) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + if (dev->ep0_urb_queued) { + dev_dbg(&dev->gadget->dev, "fail, urb already queued\n"); + ret = -EBUSY; + goto out_unlock; + } + if ((in && !dev->ep0_in_pending) || + (!in && !dev->ep0_out_pending)) { + dev_dbg(&dev->gadget->dev, "fail, wrong direction\n"); + ret = -EBUSY; + goto out_unlock; + } + if (WARN_ON(in && dev->ep0_out_pending)) { + ret = -ENODEV; + dev->state = STATE_DEV_FAILED; + goto out_done; + } + if (WARN_ON(!in && dev->ep0_in_pending)) { + ret = -ENODEV; + dev->state = STATE_DEV_FAILED; + goto out_done; + } + + dev->req->buf = data; + dev->req->length = io->length; + dev->req->zero = usb_raw_io_flags_zero(io->flags); + dev->ep0_urb_queued = true; + spin_unlock_irqrestore(&dev->lock, flags); + + ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL); + if (ret) { + dev_err(&dev->gadget->dev, + "fail, usb_ep_queue returned %d\n", ret); + spin_lock_irqsave(&dev->lock, flags); + dev->state = STATE_DEV_FAILED; + goto out_done; + } + + ret = wait_for_completion_interruptible(&dev->ep0_done); + if (ret) { + dev_dbg(&dev->gadget->dev, "wait interrupted\n"); + usb_ep_dequeue(dev->gadget->ep0, dev->req); + wait_for_completion(&dev->ep0_done); + spin_lock_irqsave(&dev->lock, flags); + goto out_done; + } + + spin_lock_irqsave(&dev->lock, flags); + ret = dev->ep0_status; + +out_done: + dev->ep0_urb_queued = false; +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + void *data; + struct usb_raw_ep_io io; + + data = raw_alloc_io_data(&io, (void __user *)value, true); + if (IS_ERR(data)) + return PTR_ERR(data); + ret = raw_process_ep0_io(dev, &io, data, true); + kfree(data); + return ret; +} + +static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + void *data; + struct usb_raw_ep_io io; + unsigned int length; + + data = raw_alloc_io_data(&io, (void __user *)value, false); + if (IS_ERR(data)) + return PTR_ERR(data); + ret = raw_process_ep0_io(dev, &io, data, false); + if (ret < 0) { + kfree(data); + return ret; + } + length = min(io.length, (unsigned int)ret); + ret = copy_to_user((void __user *)(value + sizeof(io)), data, length); + kfree(data); + return ret; +} + +static bool check_ep_caps(struct usb_ep *ep, + struct usb_endpoint_descriptor *desc) +{ + switch (usb_endpoint_type(desc)) { + case USB_ENDPOINT_XFER_ISOC: + if (!ep->caps.type_iso) + return false; + break; + case USB_ENDPOINT_XFER_BULK: + if (!ep->caps.type_bulk) + return false; + break; + case USB_ENDPOINT_XFER_INT: + if (!ep->caps.type_int) + return false; + break; + default: + return false; + } + + if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) + return false; + if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) + return false; + + return true; +} + +static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value) +{ + int ret = 0, i; + unsigned long flags; + struct usb_endpoint_descriptor *desc; + struct usb_ep *ep = NULL; + + desc = memdup_user((void __user *)value, sizeof(*desc)); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + /* + * Endpoints with a maxpacket length of 0 can cause crashes in UDC + * drivers. + */ + if (usb_endpoint_maxp(desc) == 0) { + dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n"); + kfree(desc); + return -EINVAL; + } + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_free; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_free; + } + + for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) { + if (dev->eps[i].state == STATE_EP_ENABLED) + continue; + break; + } + if (i == USB_RAW_MAX_ENDPOINTS) { + dev_dbg(&dev->gadget->dev, + "fail, no device endpoints available\n"); + ret = -EBUSY; + goto out_free; + } + + gadget_for_each_ep(ep, dev->gadget) { + if (ep->enabled) + continue; + if (!check_ep_caps(ep, desc)) + continue; + ep->desc = desc; + ret = usb_ep_enable(ep); + if (ret < 0) { + dev_err(&dev->gadget->dev, + "fail, usb_ep_enable returned %d\n", ret); + goto out_free; + } + dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (!dev->eps[i].req) { + dev_err(&dev->gadget->dev, + "fail, usb_ep_alloc_request failed\n"); + usb_ep_disable(ep); + ret = -ENOMEM; + goto out_free; + } + dev->eps[i].ep = ep; + dev->eps[i].state = STATE_EP_ENABLED; + ep->driver_data = &dev->eps[i]; + ret = i; + goto out_unlock; + } + + dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n"); + ret = -EBUSY; + +out_free: + kfree(desc); +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value) +{ + int ret = 0, i = value; + unsigned long flags; + const void *desc; + + if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS) + return -EINVAL; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + if (dev->eps[i].state != STATE_EP_ENABLED) { + dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); + ret = -EINVAL; + goto out_unlock; + } + if (dev->eps[i].disabling) { + dev_dbg(&dev->gadget->dev, + "fail, disable already in progress\n"); + ret = -EINVAL; + goto out_unlock; + } + if (dev->eps[i].urb_queued) { + dev_dbg(&dev->gadget->dev, + "fail, waiting for urb completion\n"); + ret = -EINVAL; + goto out_unlock; + } + dev->eps[i].disabling = true; + spin_unlock_irqrestore(&dev->lock, flags); + + usb_ep_disable(dev->eps[i].ep); + + spin_lock_irqsave(&dev->lock, flags); + usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req); + desc = dev->eps[i].ep->desc; + dev->eps[i].ep = NULL; + dev->eps[i].state = STATE_EP_DISABLED; + kfree(desc); + dev->eps[i].disabling = false; + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data; + struct raw_dev *dev = r_ep->dev; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (req->status) + r_ep->status = req->status; + else + r_ep->status = req->actual; + spin_unlock_irqrestore(&dev->lock, flags); + + complete((struct completion *)req->context); +} + +static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io, + void *data, bool in) +{ + int ret = 0; + unsigned long flags; + struct raw_ep *ep = &dev->eps[io->ep]; + DECLARE_COMPLETION_ONSTACK(done); + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + if (ep->state != STATE_EP_ENABLED) { + dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n"); + ret = -EBUSY; + goto out_unlock; + } + if (ep->disabling) { + dev_dbg(&dev->gadget->dev, + "fail, endpoint is already being disabled\n"); + ret = -EBUSY; + goto out_unlock; + } + if (ep->urb_queued) { + dev_dbg(&dev->gadget->dev, "fail, urb already queued\n"); + ret = -EBUSY; + goto out_unlock; + } + if ((in && !ep->ep->caps.dir_in) || (!in && ep->ep->caps.dir_in)) { + dev_dbg(&dev->gadget->dev, "fail, wrong direction\n"); + ret = -EINVAL; + goto out_unlock; + } + + ep->dev = dev; + ep->req->context = &done; + ep->req->complete = gadget_ep_complete; + ep->req->buf = data; + ep->req->length = io->length; + ep->req->zero = usb_raw_io_flags_zero(io->flags); + ep->urb_queued = true; + spin_unlock_irqrestore(&dev->lock, flags); + + ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL); + if (ret) { + dev_err(&dev->gadget->dev, + "fail, usb_ep_queue returned %d\n", ret); + spin_lock_irqsave(&dev->lock, flags); + dev->state = STATE_DEV_FAILED; + goto out_done; + } + + ret = wait_for_completion_interruptible(&done); + if (ret) { + dev_dbg(&dev->gadget->dev, "wait interrupted\n"); + usb_ep_dequeue(ep->ep, ep->req); + wait_for_completion(&done); + spin_lock_irqsave(&dev->lock, flags); + goto out_done; + } + + spin_lock_irqsave(&dev->lock, flags); + ret = ep->status; + +out_done: + ep->urb_queued = false; +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + char *data; + struct usb_raw_ep_io io; + + data = raw_alloc_io_data(&io, (void __user *)value, true); + if (IS_ERR(data)) + return PTR_ERR(data); + ret = raw_process_ep_io(dev, &io, data, true); + kfree(data); + return ret; +} + +static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + char *data; + struct usb_raw_ep_io io; + unsigned int length; + + data = raw_alloc_io_data(&io, (void __user *)value, false); + if (IS_ERR(data)) + return PTR_ERR(data); + ret = raw_process_ep_io(dev, &io, data, false); + if (ret < 0) { + kfree(data); + return ret; + } + length = min(io.length, (unsigned int)ret); + ret = copy_to_user((void __user *)(value + sizeof(io)), data, length); + kfree(data); + return ret; +} + +static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + unsigned long flags; + + if (value) + return -EINVAL; + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED); + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&dev->lock, flags); + if (dev->state != STATE_DEV_RUNNING) { + dev_dbg(dev->dev, "fail, device is not running\n"); + ret = -EINVAL; + goto out_unlock; + } + if (!dev->gadget) { + dev_dbg(dev->dev, "fail, gadget is not bound\n"); + ret = -EBUSY; + goto out_unlock; + } + usb_gadget_vbus_draw(dev->gadget, 2 * value); + +out_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + return ret; +} + +static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value) +{ + struct raw_dev *dev = fd->private_data; + int ret = 0; + + if (!dev) + return -EBUSY; + + switch (cmd) { + case USB_RAW_IOCTL_INIT: + ret = raw_ioctl_init(dev, value); + break; + case USB_RAW_IOCTL_RUN: + ret = raw_ioctl_run(dev, value); + break; + case USB_RAW_IOCTL_EVENT_FETCH: + ret = raw_ioctl_event_fetch(dev, value); + break; + case USB_RAW_IOCTL_EP0_WRITE: + ret = raw_ioctl_ep0_write(dev, value); + break; + case USB_RAW_IOCTL_EP0_READ: + ret = raw_ioctl_ep0_read(dev, value); + break; + case USB_RAW_IOCTL_EP_ENABLE: + ret = raw_ioctl_ep_enable(dev, value); + break; + case USB_RAW_IOCTL_EP_DISABLE: + ret = raw_ioctl_ep_disable(dev, value); + break; + case USB_RAW_IOCTL_EP_WRITE: + ret = raw_ioctl_ep_write(dev, value); + break; + case USB_RAW_IOCTL_EP_READ: + ret = raw_ioctl_ep_read(dev, value); + break; + case USB_RAW_IOCTL_CONFIGURE: + ret = raw_ioctl_configure(dev, value); + break; + case USB_RAW_IOCTL_VBUS_DRAW: + ret = raw_ioctl_vbus_draw(dev, value); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/*----------------------------------------------------------------------*/ + +static const struct file_operations raw_fops = { + .open = raw_open, + .unlocked_ioctl = raw_ioctl, + .compat_ioctl = raw_ioctl, + .release = raw_release, + .llseek = no_llseek, +}; + +static struct miscdevice raw_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = DRIVER_NAME, + .fops = &raw_fops, +}; + +module_misc_device(raw_misc_device); diff --git a/include/uapi/linux/usb/raw_gadget.h b/include/uapi/linux/usb/raw_gadget.h new file mode 100644 index 000000000000..00cbded71061 --- /dev/null +++ b/include/uapi/linux/usb/raw_gadget.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * USB Raw Gadget driver. + * + * See Documentation/usb/raw-gadget.rst for more details. + */ + +#ifndef _UAPI__LINUX_USB_RAW_GADGET_H +#define _UAPI__LINUX_USB_RAW_GADGET_H + +#include +#include +#include + +/* Maximum length of driver_name/device_name in the usb_raw_init struct. */ +#define UDC_NAME_LENGTH_MAX 128 + +/* + * struct usb_raw_init - argument for USB_RAW_IOCTL_INIT ioctl. + * @speed: The speed of the emulated USB device, takes the same values as + * the usb_device_speed enum: USB_SPEED_FULL, USB_SPEED_HIGH, etc. + * @driver_name: The name of the UDC driver. + * @device_name: The name of a UDC instance. + * + * The last two fields identify a UDC the gadget driver should bind to. + * For example, Dummy UDC has "dummy_udc" as its driver_name and "dummy_udc.N" + * as its device_name, where N in the index of the Dummy UDC instance. + * At the same time the dwc2 driver that is used on Raspberry Pi Zero, has + * "20980000.usb" as both driver_name and device_name. + */ +struct usb_raw_init { + __u8 driver_name[UDC_NAME_LENGTH_MAX]; + __u8 device_name[UDC_NAME_LENGTH_MAX]; + __u8 speed; +}; + +/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */ +enum usb_raw_event_type { + USB_RAW_EVENT_INVALID = 0, + + /* This event is queued when the driver has bound to a UDC. */ + USB_RAW_EVENT_CONNECT = 1, + + /* This event is queued when a new control request arrived to ep0. */ + USB_RAW_EVENT_CONTROL = 2, + + /* The list might grow in the future. */ +}; + +/* + * struct usb_raw_event - argument for USB_RAW_IOCTL_EVENT_FETCH ioctl. + * @type: The type of the fetched event. + * @length: Length of the data buffer. Updated by the driver and set to the + * actual length of the fetched event data. + * @data: A buffer to store the fetched event data. + * + * Currently the fetched data buffer is empty for USB_RAW_EVENT_CONNECT, + * and contains struct usb_ctrlrequest for USB_RAW_EVENT_CONTROL. + */ +struct usb_raw_event { + __u32 type; + __u32 length; + __u8 data[0]; +}; + +#define USB_RAW_IO_FLAGS_ZERO 0x0001 +#define USB_RAW_IO_FLAGS_MASK 0x0001 + +static int usb_raw_io_flags_valid(__u16 flags) +{ + return (flags & ~USB_RAW_IO_FLAGS_MASK) == 0; +} + +static int usb_raw_io_flags_zero(__u16 flags) +{ + return (flags & USB_RAW_IO_FLAGS_ZERO); +} + +/* + * struct usb_raw_ep_io - argument for USB_RAW_IOCTL_EP0/EP_WRITE/READ ioctls. + * @ep: Endpoint handle as returned by USB_RAW_IOCTL_EP_ENABLE for + * USB_RAW_IOCTL_EP_WRITE/READ. Ignored for USB_RAW_IOCTL_EP0_WRITE/READ. + * @flags: When USB_RAW_IO_FLAGS_ZERO is specified, the zero flag is set on + * the submitted USB request, see include/linux/usb/gadget.h for details. + * @length: Length of data. + * @data: Data to send for USB_RAW_IOCTL_EP0/EP_WRITE. Buffer to store received + * data for USB_RAW_IOCTL_EP0/EP_READ. + */ +struct usb_raw_ep_io { + __u16 ep; + __u16 flags; + __u32 length; + __u8 data[0]; +}; + +/* + * Initializes a Raw Gadget instance. + * Accepts a pointer to the usb_raw_init struct as an argument. + * Returns 0 on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init) + +/* + * Instructs Raw Gadget to bind to a UDC and start emulating a USB device. + * Returns 0 on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_RUN _IO('U', 1) + +/* + * A blocking ioctl that waits for an event and returns fetched event data to + * the user. + * Accepts a pointer to the usb_raw_event struct. + * Returns 0 on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event) + +/* + * Queues an IN (OUT for READ) urb as a response to the last control request + * received on endpoint 0, provided that was an IN (OUT for READ) request and + * waits until the urb is completed. Copies received data to user for READ. + * Accepts a pointer to the usb_raw_ep_io struct as an argument. + * Returns length of trasferred data on success or negative error code on + * failure. + */ +#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io) +#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io) + +/* + * Finds an endpoint that supports the transfer type specified in the + * descriptor and enables it. + * Accepts a pointer to the usb_endpoint_descriptor struct as an argument. + * Returns enabled endpoint handle on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor) + +/* Disables specified endpoint. + * Accepts endpoint handle as an argument. + * Returns 0 on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32) + +/* + * Queues an IN (OUT for READ) urb as a response to the last control request + * received on endpoint usb_raw_ep_io.ep, provided that was an IN (OUT for READ) + * request and waits until the urb is completed. Copies received data to user + * for READ. + * Accepts a pointer to the usb_raw_ep_io struct as an argument. + * Returns length of trasferred data on success or negative error code on + * failure. + */ +#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io) +#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io) + +/* + * Switches the gadget into the configured state. + * Returns 0 on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9) + +/* + * Constrains UDC VBUS power usage. + * Accepts current limit in 2 mA units as an argument. + * Returns 0 on success or negative error code on failure. + */ +#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32) + +#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */ -- cgit v1.2.3 From 68983a354a655c35d3fb204489d383a2a051fda7 Mon Sep 17 00:00:00 2001 From: Manoj Basapathi Date: Thu, 6 Feb 2020 16:37:29 +0530 Subject: netfilter: xtables: Add snapshot of hardidletimer target This is a snapshot of hardidletimer netfilter target. This patch implements a hardidletimer Xtables target that can be used to identify when interfaces have been idle for a certain period of time. Timers are identified by labels and are created when a rule is set with a new label. The rules also take a timeout value (in seconds) as an option. If more than one rule uses the same timer label, the timer will be restarted whenever any of the rules get a hit. One entry for each timer is created in sysfs. This attribute contains the timer remaining for the timer to expire. The attributes are located under the xt_idletimer class: /sys/class/xt_idletimer/timers/