From 0c266898b42fe4e4e2f9edfc9d3474c10f93aa6a Mon Sep 17 00:00:00 2001 From: Satoru SATOH Date: Mon, 4 May 2009 11:11:01 -0700 Subject: tcp: Fix tcp_prequeue() to get correct rto_min value tcp_prequeue() refers to the constant value (TCP_RTO_MIN) regardless of the actual value might be tuned. The following patches fix this and make tcp_prequeue get the actual value returns from tcp_rto_min(). Signed-off-by: Satoru SATOH Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c96a6bb2543..eec3e6f9956 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -597,16 +597,6 @@ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) tcp_grow_window(sk, skb); } -static u32 tcp_rto_min(struct sock *sk) -{ - struct dst_entry *dst = __sk_dst_get(sk); - u32 rto_min = TCP_RTO_MIN; - - if (dst && dst_metric_locked(dst, RTAX_RTO_MIN)) - rto_min = dst_metric_rtt(dst, RTAX_RTO_MIN); - return rto_min; -} - /* Called to compute a smoothed rtt estimate. The data fed to this * routine either comes from timestamps, or from segments that were * known _not_ to have been retransmitted [see Karn/Partridge -- cgit v1.2.3 From 8ccd8f21122dcc30a665516d43aa8b4aa8ae51f6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 29 Apr 2009 23:35:56 +0200 Subject: mac80211: correct fragmentation threshold check The fragmentation threshold is defined to be including the FCS, and the code that sets the TX_FRAGMENTED flag correctly accounts for those four bytes. The code that verifies this doesn't though, which could lead to spurious warnings and frames being dropped although everything is ok. Correct the code by accounting for the FCS. (JWL -- The problem is described here: http://article.gmane.org/gmane.linux.kernel.wireless.general/32205 ) Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 3fb04a86444..63656266d56 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -772,7 +772,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx) hdrlen = ieee80211_hdrlen(hdr->frame_control); /* internal error, why is TX_FRAGMENTED set? */ - if (WARN_ON(skb->len <= frag_threshold)) + if (WARN_ON(skb->len + FCS_LEN <= frag_threshold)) return TX_DROP; /* -- cgit v1.2.3 From c0f0aac05fa84b37ed46db8cf6c8bee9a67bbcca Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 30 Apr 2009 20:09:56 +0200 Subject: cfg80211: fix truncated IEs Another bug in the "cfg80211: do not replace BSS structs" patch, a forgotten length update leads to bogus data being stored and passed to userspace, often truncated. Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/wireless/scan.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 2ae65b39b52..1f260c40b6c 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -395,6 +395,7 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, memcpy(ies, res->pub.information_elements, ielen); found->ies_allocated = true; found->pub.information_elements = ies; + found->pub.len_information_elements = ielen; } } } -- cgit v1.2.3 From ac46d48e00349c63650b3cc6f9460fcc183da6a6 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Fri, 1 May 2009 18:44:50 -0400 Subject: cfg80211: fix race condition with wiphy_apply_custom_regulatory() We forgot to lock using the cfg80211_mutex in wiphy_apply_custom_regulatory(). Without the lock there is possible race between processing a reply from CRDA and a driver calling wiphy_apply_custom_regulatory(). During the processing of the reply from CRDA we free last_request and wiphy_apply_custom_regulatory() eventually accesses an element from last_request in the through freq_reg_info_regd(). This is very difficult to reproduce (I haven't), it takes us 3 hours and you need to be banging hard, but the race is obvious by looking at the code. This should only affect those who use this caller, which currently is ath5k, ath9k, and ar9170. EIP: 0060:[] EFLAGS: 00210282 CPU: 1 EIP is at freq_reg_info_regd+0x24/0x121 [cfg80211] EAX: 00000000 EBX: f7ca0060 ECX: f5183d94 EDX: 0024cde0 ESI: f8f56edc EDI: 00000000 EBP: 00000000 ESP: f5183d44 DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process modprobe (pid: 14617, ti=f5182000 task=f3934d10 task.ti=f5182000) Stack: c0505300 f7ca0ab4 f5183d94 0024cde0 f8f403a6 f8f63160 f7ca0060 00000000 00000000 f8ebedf8 f5183d90 f8f56edc 00000000 00000004 00000f40 f8f56edc f7ca0060 f7ca1234 00000000 00000000 00000000 f7ca14f0 f7ca0ab4 f7ca1289 Call Trace: [] wiphy_apply_custom_regulatory+0x8f/0x122 [cfg80211] [] ath_attach+0x707/0x9e6 [ath9k] [] ath_pci_probe+0x18d/0x29a [ath9k] [] pci_device_probe+0xa3/0xe4 [] really_probe+0xd7/0x1de [] __driver_attach+0x37/0x55 [] bus_for_each_dev+0x31/0x57 [] driver_attach+0x16/0x18 [] bus_add_driver+0xec/0x21b [] driver_register+0x85/0xe2 [] __pci_register_driver+0x3c/0x69 [] ath9k_init+0x43/0x68 [ath9k] [] _stext+0x3b/0x116 [] sys_init_module+0x8a/0x19e [] sysenter_do_call+0x12/0x21 [] 0xffffe430 ======================= Code: 0f 94 c0 c3 31 c0 c3 55 57 56 53 89 c3 83 ec 14 8b 74 24 2c 89 54 24 0c 89 4c 24 08 85 f6 75 06 8b 35 c8 bb ec f8 a1 cc bb ec f8 <8b> 40 04 83 f8 03 74 3a 48 74 37 8b 43 28 85 c0 74 30 89 c6 8b EIP: [] freq_reg_info_regd+0x24/0x121 [cfg80211] SS:ESP 0068:f5183d44 Cc: stable@kernel.org Reported-by: Nataraj Sadasivam Reported-by: Vivek Natarajan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 6c1993d9990..3a734949769 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -907,6 +907,7 @@ EXPORT_SYMBOL(freq_reg_info); int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, const struct ieee80211_reg_rule **reg_rule) { + assert_cfg80211_lock(); return freq_reg_info_regd(wiphy, center_freq, bandwidth, reg_rule, NULL); } @@ -1176,6 +1177,8 @@ static void handle_channel_custom(struct wiphy *wiphy, struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; + assert_cfg80211_lock(); + sband = wiphy->bands[band]; BUG_ON(chan_idx >= sband->n_channels); chan = &sband->channels[chan_idx]; @@ -1214,10 +1217,13 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_regdomain *regd) { enum ieee80211_band band; + + mutex_lock(&cfg80211_mutex); for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) handle_band_custom(wiphy, band, regd); } + mutex_unlock(&cfg80211_mutex); } EXPORT_SYMBOL(wiphy_apply_custom_regulatory); -- cgit v1.2.3 From b1ed8ddd21a2d7acf8efbb60a112ea5c9f914159 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 00:34:15 -0400 Subject: cfg80211: fix bug while trying to process beacon hints on init During initialization we would not have received any beacons so skip processing reg beacon hints, also adds a check to reg_is_world_roaming() for last_request before accessing its fields. This should fix this: BUG: unable to handle kernel NULL pointer dereference at IP: [] wiphy_update_regulatory+0x20f/0x295 *pdpt = 0000000008bf1001 *pde = 0000000000000000 Oops: 0000 [#1] last sysfs file: /sys/class/backlight/eeepc/brightness Modules linked in: ath5k(+) mac80211 led_class cfg80211 go_bit cfbcopyarea cfbimgblt cfbfillrect ipv6 ydev usual_tables(P) snd_hda_codec_realtek snd_hda_intel nd_hwdep uhci_hcd snd_pcm_oss snd_mixer_oss i2c_i801 e serio_raw i2c_core pcspkr atl2 snd_pcm intel_agp re agpgart eeepc_laptop snd_page_alloc ac video backlight rfkill button processor evdev thermal fan ata_generic Pid: 2909, comm: modprobe Tainted: Pc #112) 701 EIP: 0060:[] EFLAGS: 00010246 CPU: 0 EIP is at wiphy_update_regulatory+0x20f/0x295 [cfg80211] EAX: 00000000 EBX: c5da0000 ECX: 00000000 EDX: c5da0060 ESI: 0000001a EDI: c5da0060 EBP: df3bdd70 ESP: df3bdd40 DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068 Process modprobe (pid: 2909, ti=df3bc000 task=c5d030000) Stack: df3bdd90 c5da0060 c04277e0 00000001 00000044 c04277e402 00000002 c5da0000 0000001a c5da0060 df3bdda8 e01706a2 02 00000282 000080d0 00000068 c5d53500 00000080 0000028240 Call Trace: [] ? wiphy_register+0x122/0x1b7 [cfg80211] [] ? ieee80211_register_hw+0xd8/0x346 [] ? ath5k_hw_set_bssid_mask+0x71/0x78 [ath5k] [] ? ath5k_pci_probe+0xa5c/0xd0a [ath5k] [] ? sysfs_find_dirent+0x16/0x27 [] ? local_pci_probe+0xe/0x10 [] ? pci_device_probe+0x48/0x66 [] ? driver_probe_device+0x7f/0xf2 [] ? __driver_attach+0x43/0x5f [] ? bus_for_each_dev+0x39/0x5a [] ? driver_attach+0x14/0x16 [] ? __driver_attach+0x0/0x5f [] ? bus_add_driver+0xd7/0x1e7 [] ? driver_register+0x7b/0xd7 [] ? __pci_register_driver+0x32/0x85 [] ? init_ath5k_pci+0x18/0x30 [ath5k] [] ? _stext+0x49/0x10b [] ? init_ath5k_pci+0x0/0x30 [ath5k] [] ? __blocking_notifier_call_chain+0x40/0x4c [] ? sys_init_module+0x87/0x18b [] ? sysenter_do_call+0x12/0x22 Code: b8 da 17 e0 83 c0 04 e8 92 f9 ff ff 84 c0 75 2a 8b 85 c0 74 0c 83 c0 04 e8 7c f9 ff ff 84 c0 75 14 a1 bc da 4 03 74 66 8b 4d d4 80 79 08 00 74 5d a1 e0 d2 17 e0 48 EIP: [] wiphy_update_regulatory+0x20f/0x295 SP 0068:df3bdd40 CR2: 0000000000000004 ---[ end trace 830f2dd2a95fd1a8 ]--- This issue is hard to reproduce, but it was noticed and discussed on this thread: http://marc.info/?t=123938022700005&r=1&w=2 Cc: stable@kernel.org Reported-by: Alan Jenkins Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 3a734949769..974b127aea6 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1134,7 +1134,8 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) if (is_world_regdom(cfg80211_regdomain->alpha2) || (wiphy->regd && is_world_regdom(wiphy->regd->alpha2))) return true; - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + if (last_request && + last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && wiphy->custom_regulatory) return true; return false; @@ -1143,6 +1144,12 @@ static bool reg_is_world_roaming(struct wiphy *wiphy) /* Reap the advantages of previously found beacons */ static void reg_process_beacons(struct wiphy *wiphy) { + /* + * Means we are just firing up cfg80211, so no beacons would + * have been processed yet. + */ + if (!last_request) + return; if (!reg_is_world_roaming(wiphy)) return; wiphy_update_beacon_reg(wiphy); -- cgit v1.2.3 From 30a548c727514484b08ac06edf0a7eb0f7fd70bf Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Sat, 2 May 2009 01:17:27 -0400 Subject: cfg80211: fix comment on regulatory hint processing Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/wireless/reg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 974b127aea6..08265ca1578 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1436,7 +1436,7 @@ new_request: return call_crda(last_request->alpha2); } -/* This currently only processes user and driver regulatory hints */ +/* This processes *all* regulatory hints */ static void reg_process_hint(struct regulatory_request *reg_request) { int r = 0; -- cgit v1.2.3 From 8e532175277d9a5eae49768ed086555081f741a7 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 May 2009 18:04:55 +0200 Subject: mac80211: minstrel, fix memory corruption minstrel doesn't count max rate count in fact, since it doesn't use a loop variable `i' and hence allocs space only for bitrates found in the first band. Fix it by involving the `i' as an index so that it traverses all the bands now and finds the real max bitrate count. Signed-off-by: Jiri Slaby Cc: Felix Fietkau Signed-off-by: John W. Linville --- net/mac80211/rc80211_minstrel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 3824990d340..70df3dcc3cf 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -476,7 +476,7 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) return NULL; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { - sband = hw->wiphy->bands[hw->conf.channel->band]; + sband = hw->wiphy->bands[i]; if (sband->n_bitrates > max_rates) max_rates = sband->n_bitrates; } -- cgit v1.2.3 From 6909268dc93ae4b0b8e1ebb4b2fa70b1a47dd347 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 4 May 2009 18:10:28 +0200 Subject: mac80211: pid, fix memory corruption pid doesn't count with some band having more bitrates than the one associated the first time. Fix that by counting the maximal available bitrate count and allocate big enough space. Secondly, fix touching uninitialized memory which causes panics. Index sucked from this random memory points to the hell. The fix is to sort the rates on each band change. Signed-off-by: Jiri Slaby Signed-off-by: John W. Linville --- net/mac80211/rc80211_pid_algo.c | 73 ++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c index b16801cde06..01d59a8e334 100644 --- a/net/mac80211/rc80211_pid_algo.c +++ b/net/mac80211/rc80211_pid_algo.c @@ -317,13 +317,44 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { struct rc_pid_sta_info *spinfo = priv_sta; + struct rc_pid_info *pinfo = priv; + struct rc_pid_rateinfo *rinfo = pinfo->rinfo; struct sta_info *si; + int i, j, tmp; + bool s; /* TODO: This routine should consider using RSSI from previous packets * as we need to have IEEE 802.1X auth succeed immediately after assoc.. * Until that method is implemented, we will use the lowest supported * rate as a workaround. */ + /* Sort the rates. This is optimized for the most common case (i.e. + * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed + * mapping too. */ + for (i = 0; i < sband->n_bitrates; i++) { + rinfo[i].index = i; + rinfo[i].rev_index = i; + if (RC_PID_FAST_START) + rinfo[i].diff = 0; + else + rinfo[i].diff = i * pinfo->norm_offset; + } + for (i = 1; i < sband->n_bitrates; i++) { + s = 0; + for (j = 0; j < sband->n_bitrates - i; j++) + if (unlikely(sband->bitrates[rinfo[j].index].bitrate > + sband->bitrates[rinfo[j + 1].index].bitrate)) { + tmp = rinfo[j].index; + rinfo[j].index = rinfo[j + 1].index; + rinfo[j + 1].index = tmp; + rinfo[rinfo[j].index].rev_index = j; + rinfo[rinfo[j + 1].index].rev_index = j + 1; + s = 1; + } + if (!s) + break; + } + spinfo->txrate_idx = rate_lowest_index(sband, sta); /* HACK */ si = container_of(sta, struct sta_info, sta); @@ -336,21 +367,22 @@ static void *rate_control_pid_alloc(struct ieee80211_hw *hw, struct rc_pid_info *pinfo; struct rc_pid_rateinfo *rinfo; struct ieee80211_supported_band *sband; - int i, j, tmp; - bool s; + int i, max_rates = 0; #ifdef CONFIG_MAC80211_DEBUGFS struct rc_pid_debugfs_entries *de; #endif - sband = hw->wiphy->bands[hw->conf.channel->band]; - pinfo = kmalloc(sizeof(*pinfo), GFP_ATOMIC); if (!pinfo) return NULL; - /* We can safely assume that sband won't change unless we get - * reinitialized. */ - rinfo = kmalloc(sizeof(*rinfo) * sband->n_bitrates, GFP_ATOMIC); + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + sband = hw->wiphy->bands[i]; + if (sband->n_bitrates > max_rates) + max_rates = sband->n_bitrates; + } + + rinfo = kmalloc(sizeof(*rinfo) * max_rates, GFP_ATOMIC); if (!rinfo) { kfree(pinfo); return NULL; @@ -368,33 +400,6 @@ static void *rate_control_pid_alloc(struct ieee80211_hw *hw, pinfo->rinfo = rinfo; pinfo->oldrate = 0; - /* Sort the rates. This is optimized for the most common case (i.e. - * almost-sorted CCK+OFDM rates). Kind of bubble-sort with reversed - * mapping too. */ - for (i = 0; i < sband->n_bitrates; i++) { - rinfo[i].index = i; - rinfo[i].rev_index = i; - if (RC_PID_FAST_START) - rinfo[i].diff = 0; - else - rinfo[i].diff = i * pinfo->norm_offset; - } - for (i = 1; i < sband->n_bitrates; i++) { - s = 0; - for (j = 0; j < sband->n_bitrates - i; j++) - if (unlikely(sband->bitrates[rinfo[j].index].bitrate > - sband->bitrates[rinfo[j + 1].index].bitrate)) { - tmp = rinfo[j].index; - rinfo[j].index = rinfo[j + 1].index; - rinfo[j + 1].index = tmp; - rinfo[rinfo[j].index].rev_index = j; - rinfo[rinfo[j + 1].index].rev_index = j + 1; - s = 1; - } - if (!s) - break; - } - #ifdef CONFIG_MAC80211_DEBUGFS de = &pinfo->dentries; de->target = debugfs_create_u32("target_pf", S_IRUSR | S_IWUSR, -- cgit v1.2.3 From a67e899cf38ae542d1a028ccd021f9189f76fb74 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 2 May 2009 18:24:06 -0700 Subject: Bluetooth: Fix issue with sysfs handling for connections Due to a semantic changes in flush_workqueue() the current approach of synchronizing the sysfs handling for connections doesn't work anymore. The whole approach is actually fully broken and based on assumptions that are no longer valid. With the introduction of Simple Pairing support, the creation of low-level ACL links got changed. This change invalidates the reason why in the past two independent work queues have been used for adding/removing sysfs devices. The adding of the actual sysfs device is now postponed until the host controller successfully assigns an unique handle to that link. So the real synchronization happens inside the controller and not the host. The only left-over problem is that some internals of the sysfs device handling are not initialized ahead of time. This leaves potential access to invalid data and can cause various NULL pointer dereferences. To fix this a new function makes sure that all sysfs details are initialized when an connection attempt is made. The actual sysfs device is only registered when the connection has been successfully established. To avoid a race condition with the registration, the check if a device is registered has been moved into the removal work. As an extra protection two flush_work() calls are left in place to make sure a previous add/del work has been completed first. Based on a report by Marc Pignat Signed-off-by: Marcel Holtmann Tested-by: Justin P. Mattock Tested-by: Roger Quadros Tested-by: Marc Pignat --- net/bluetooth/hci_conn.c | 2 ++ net/bluetooth/hci_sysfs.c | 74 +++++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 375f4b4f7f7..61309b26f27 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -248,6 +248,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_ADD); + hci_conn_init_sysfs(conn); + tasklet_enable(&hdev->tx_task); return conn; diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index b7c51082dde..582d8877078 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -9,7 +9,7 @@ struct class *bt_class = NULL; EXPORT_SYMBOL_GPL(bt_class); -static struct workqueue_struct *bluetooth; +static struct workqueue_struct *bt_workq; static inline char *link_typetostr(int type) { @@ -89,8 +89,8 @@ static void add_conn(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, work_add); - /* ensure previous add/del is complete */ - flush_workqueue(bluetooth); + /* ensure previous del is complete */ + flush_work(&conn->work_del); if (device_add(&conn->dev) < 0) { BT_ERR("Failed to register connection device"); @@ -98,27 +98,6 @@ static void add_conn(struct work_struct *work) } } -void hci_conn_add_sysfs(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - - BT_DBG("conn %p", conn); - - conn->dev.type = &bt_link; - conn->dev.class = bt_class; - conn->dev.parent = &hdev->dev; - - dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); - - dev_set_drvdata(&conn->dev, conn); - - device_initialize(&conn->dev); - - INIT_WORK(&conn->work_add, add_conn); - - queue_work(bluetooth, &conn->work_add); -} - /* * The rfcomm tty device will possibly retain even when conn * is down, and sysfs doesn't support move zombie device, @@ -134,8 +113,11 @@ static void del_conn(struct work_struct *work) struct hci_conn *conn = container_of(work, struct hci_conn, work_del); struct hci_dev *hdev = conn->hdev; - /* ensure previous add/del is complete */ - flush_workqueue(bluetooth); + /* ensure previous add is complete */ + flush_work(&conn->work_add); + + if (!device_is_registered(&conn->dev)) + return; while (1) { struct device *dev; @@ -152,16 +134,40 @@ static void del_conn(struct work_struct *work) hci_dev_put(hdev); } -void hci_conn_del_sysfs(struct hci_conn *conn) +void hci_conn_init_sysfs(struct hci_conn *conn) { + struct hci_dev *hdev = conn->hdev; + BT_DBG("conn %p", conn); - if (!device_is_registered(&conn->dev)) - return; + conn->dev.type = &bt_link; + conn->dev.class = bt_class; + conn->dev.parent = &hdev->dev; + + dev_set_drvdata(&conn->dev, conn); + device_initialize(&conn->dev); + + INIT_WORK(&conn->work_add, add_conn); INIT_WORK(&conn->work_del, del_conn); +} + +void hci_conn_add_sysfs(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + + BT_DBG("conn %p", conn); + + dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); + + queue_work(bt_workq, &conn->work_add); +} + +void hci_conn_del_sysfs(struct hci_conn *conn) +{ + BT_DBG("conn %p", conn); - queue_work(bluetooth, &conn->work_del); + queue_work(bt_workq, &conn->work_del); } static inline char *host_typetostr(int type) @@ -438,13 +444,13 @@ void hci_unregister_sysfs(struct hci_dev *hdev) int __init bt_sysfs_init(void) { - bluetooth = create_singlethread_workqueue("bluetooth"); - if (!bluetooth) + bt_workq = create_singlethread_workqueue("bluetooth"); + if (!bt_workq) return -ENOMEM; bt_class = class_create(THIS_MODULE, "bluetooth"); if (IS_ERR(bt_class)) { - destroy_workqueue(bluetooth); + destroy_workqueue(bt_workq); return PTR_ERR(bt_class); } @@ -453,7 +459,7 @@ int __init bt_sysfs_init(void) void bt_sysfs_cleanup(void) { - destroy_workqueue(bluetooth); + destroy_workqueue(bt_workq); class_destroy(bt_class); } -- cgit v1.2.3 From b98b4947cb79d670fceca0e951c092eea93e9baa Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Tue, 5 May 2009 15:32:16 +0200 Subject: netfilter: ip6t_ipv6header: fix match on packets ending with NEXTHDR_NONE As packets ending with NEXTHDR_NONE don't have a last extension header, the check for the length needs to be after the check for NEXTHDR_NONE. Signed-off-by: Christoph Paasch Signed-off-by: Patrick McHardy --- net/ipv6/netfilter/ip6t_ipv6header.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/netfilter/ip6t_ipv6header.c b/net/ipv6/netfilter/ip6t_ipv6header.c index 14e6724d567..91490ad9302 100644 --- a/net/ipv6/netfilter/ip6t_ipv6header.c +++ b/net/ipv6/netfilter/ip6t_ipv6header.c @@ -50,14 +50,14 @@ ipv6header_mt6(const struct sk_buff *skb, const struct xt_match_param *par) struct ipv6_opt_hdr _hdr; int hdrlen; - /* Is there enough space for the next ext header? */ - if (len < (int)sizeof(struct ipv6_opt_hdr)) - return false; /* No more exthdr -> evaluate */ if (nexthdr == NEXTHDR_NONE) { temp |= MASK_NONE; break; } + /* Is there enough space for the next ext header? */ + if (len < (int)sizeof(struct ipv6_opt_hdr)) + return false; /* ESP -> evaluate */ if (nexthdr == NEXTHDR_ESP) { temp |= MASK_ESP; -- cgit v1.2.3 From 280f37afa2c270ff029cb420b34396aa002909c3 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 5 May 2009 17:46:07 +0200 Subject: netfilter: xt_cluster: fix use of cluster match with 32 nodes This patch fixes a problem when you use 32 nodes in the cluster match: % iptables -I PREROUTING -t mangle -i eth0 -m cluster \ --cluster-total-nodes 32 --cluster-local-node 32 \ --cluster-hash-seed 0xdeadbeef -j MARK --set-mark 0xffff iptables: Invalid argument. Run `dmesg' for more information. % dmesg | tail -1 xt_cluster: this node mask cannot be higher than the total number of nodes The problem is related to this checking: if (info->node_mask >= (1 << info->total_nodes)) { printk(KERN_ERR "xt_cluster: this node mask cannot be " "higher than the total number of nodes\n"); return false; } (1 << 32) is 1. Thus, the checking fails. BTW, I said this before but I insist: I have only tested the cluster match with 2 nodes getting ~45% extra performance in an active-active setup. The maximum limit of 32 nodes is still completely arbitrary. I'd really appreciate if people that have more nodes in their setups let me know. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- net/netfilter/xt_cluster.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/xt_cluster.c b/net/netfilter/xt_cluster.c index 6c4847662b8..69a639f3540 100644 --- a/net/netfilter/xt_cluster.c +++ b/net/netfilter/xt_cluster.c @@ -135,7 +135,13 @@ static bool xt_cluster_mt_checkentry(const struct xt_mtchk_param *par) { struct xt_cluster_match_info *info = par->matchinfo; - if (info->node_mask >= (1 << info->total_nodes)) { + if (info->total_nodes > XT_CLUSTER_NODES_MAX) { + printk(KERN_ERR "xt_cluster: you have exceeded the maximum " + "number of cluster nodes (%u > %u)\n", + info->total_nodes, XT_CLUSTER_NODES_MAX); + return false; + } + if (info->node_mask >= (1ULL << info->total_nodes)) { printk(KERN_ERR "xt_cluster: this node mask cannot be " "higher than the total number of nodes\n"); return false; -- cgit v1.2.3 From fecc1133b66af6e0cd49115a248f34bbb01f180a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 5 May 2009 17:48:26 +0200 Subject: netfilter: ctnetlink: fix wrong message type in user updates This patch fixes the wrong message type that are triggered by user updates, the following commands: (term1)# conntrack -I -p tcp -s 1.1.1.1 -d 2.2.2.2 -t 10 --sport 10 --dport 20 --state LISTEN (term1)# conntrack -U -p tcp -s 1.1.1.1 -d 2.2.2.2 -t 10 --sport 10 --dport 20 --state SYN_SENT (term1)# conntrack -U -p tcp -s 1.1.1.1 -d 2.2.2.2 -t 10 --sport 10 --dport 20 --state SYN_RECV only trigger event message of type NEW, when only the first is NEW while others should be UPDATE. (term2)# conntrack -E [NEW] tcp 6 10 LISTEN src=1.1.1.1 dst=2.2.2.2 sport=10 dport=20 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=20 dport=10 mark=0 [NEW] tcp 6 10 SYN_SENT src=1.1.1.1 dst=2.2.2.2 sport=10 dport=20 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=20 dport=10 mark=0 [NEW] tcp 6 10 SYN_RECV src=1.1.1.1 dst=2.2.2.2 sport=10 dport=20 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=20 dport=10 mark=0 This patch also removes IPCT_REFRESH from the bitmask since it is not of any use. Signed-off-by: Pablo Neira Ayuso Signed-off-by: Patrick McHardy --- net/netfilter/nf_conntrack_netlink.c | 48 +++++++++++++++--------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 0ea36e0c8a0..fd77619256a 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -1186,28 +1186,6 @@ ctnetlink_change_conntrack(struct nf_conn *ct, struct nlattr *cda[]) return 0; } -static inline void -ctnetlink_event_report(struct nf_conn *ct, u32 pid, int report) -{ - unsigned int events = 0; - - if (test_bit(IPS_EXPECTED_BIT, &ct->status)) - events |= IPCT_RELATED; - else - events |= IPCT_NEW; - - nf_conntrack_event_report(IPCT_STATUS | - IPCT_HELPER | - IPCT_REFRESH | - IPCT_PROTOINFO | - IPCT_NATSEQADJ | - IPCT_MARK | - events, - ct, - pid, - report); -} - static struct nf_conn * ctnetlink_create_conntrack(struct nlattr *cda[], struct nf_conntrack_tuple *otuple, @@ -1373,6 +1351,7 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, err = -ENOENT; if (nlh->nlmsg_flags & NLM_F_CREATE) { struct nf_conn *ct; + enum ip_conntrack_events events; ct = ctnetlink_create_conntrack(cda, &otuple, &rtuple, u3); @@ -1383,9 +1362,18 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, err = 0; nf_conntrack_get(&ct->ct_general); spin_unlock_bh(&nf_conntrack_lock); - ctnetlink_event_report(ct, - NETLINK_CB(skb).pid, - nlmsg_report(nlh)); + if (test_bit(IPS_EXPECTED_BIT, &ct->status)) + events = IPCT_RELATED; + else + events = IPCT_NEW; + + nf_conntrack_event_report(IPCT_STATUS | + IPCT_HELPER | + IPCT_PROTOINFO | + IPCT_NATSEQADJ | + IPCT_MARK | events, + ct, NETLINK_CB(skb).pid, + nlmsg_report(nlh)); nf_ct_put(ct); } else spin_unlock_bh(&nf_conntrack_lock); @@ -1404,9 +1392,13 @@ ctnetlink_new_conntrack(struct sock *ctnl, struct sk_buff *skb, if (err == 0) { nf_conntrack_get(&ct->ct_general); spin_unlock_bh(&nf_conntrack_lock); - ctnetlink_event_report(ct, - NETLINK_CB(skb).pid, - nlmsg_report(nlh)); + nf_conntrack_event_report(IPCT_STATUS | + IPCT_HELPER | + IPCT_PROTOINFO | + IPCT_NATSEQADJ | + IPCT_MARK, + ct, NETLINK_CB(skb).pid, + nlmsg_report(nlh)); nf_ct_put(ct); } else spin_unlock_bh(&nf_conntrack_lock); -- cgit v1.2.3 From 457ca7bb6bdf39d0832d3f88c65fa367a3b20de6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 5 May 2009 13:09:01 -0700 Subject: Bluetooth: Move dev_set_name() to a context that can sleep Setting the name of a sysfs device has to be done in a context that can actually sleep. It allocates its memory with GFP_KERNEL. Previously it was a static (size limited) string and that got changed to accommodate longer device names. So move the dev_set_name() just before calling device_add() which is executed in a work queue. This fixes the following error: [ 110.012125] BUG: sleeping function called from invalid context at mm/slub.c:1595 [ 110.012135] in_atomic(): 1, irqs_disabled(): 0, pid: 0, name: swapper [ 110.012141] 2 locks held by swapper/0: [ 110.012145] #0: (hci_task_lock){++.-.+}, at: [] hci_rx_task+0x2f/0x2d0 [bluetooth] [ 110.012173] #1: (&hdev->lock){+.-.+.}, at: [] hci_event_packet+0x72/0x25c0 [bluetooth] [ 110.012198] Pid: 0, comm: swapper Tainted: G W 2.6.30-rc4-g953cdaa #1 [ 110.012203] Call Trace: [ 110.012207] [] __might_sleep+0x14d/0x170 [ 110.012228] [] __kmalloc+0x111/0x170 [ 110.012239] [] kvasprintf+0x64/0xb0 [ 110.012248] [] kobject_set_name_vargs+0x3b/0xa0 [ 110.012257] [] dev_set_name+0x76/0xa0 [ 110.012273] [] ? hci_event_packet+0x72/0x25c0 [bluetooth] [ 110.012289] [] hci_conn_add_sysfs+0x3d/0x70 [bluetooth] [ 110.012303] [] hci_event_packet+0xbc/0x25c0 [bluetooth] [ 110.012312] [] ? sock_def_readable+0x80/0xa0 [ 110.012328] [] ? hci_send_to_sock+0xfc/0x1c0 [bluetooth] [ 110.012343] [] ? sock_def_readable+0x80/0xa0 [ 110.012347] [] ? _read_unlock+0x75/0x80 [ 110.012354] [] ? hci_send_to_sock+0xfc/0x1c0 [bluetooth] [ 110.012360] [] hci_rx_task+0x203/0x2d0 [bluetooth] [ 110.012365] [] tasklet_action+0xb5/0x160 [ 110.012369] [] __do_softirq+0x9c/0x150 [ 110.012372] [] ? _spin_unlock+0x3f/0x80 [ 110.012376] [] call_softirq+0x1c/0x30 [ 110.012380] [] do_softirq+0x8d/0xe0 [ 110.012383] [] irq_exit+0xc5/0xe0 [ 110.012386] [] do_IRQ+0x9d/0x120 [ 110.012389] [] ret_from_intr+0x0/0xf [ 110.012391] [] ? acpi_idle_enter_bm+0x264/0x2a6 [ 110.012399] [] ? acpi_idle_enter_bm+0x25a/0x2a6 [ 110.012403] [] ? cpuidle_idle_call+0xc5/0x130 [ 110.012407] [] ? cpu_idle+0xc4/0x130 [ 110.012411] [] ? rest_init+0x88/0xb0 [ 110.012416] [] ? start_kernel+0x3b5/0x412 [ 110.012420] [] ? x86_64_start_reservations+0x91/0xb5 [ 110.012424] [] ? x86_64_start_kernel+0xef/0x11b Based on a report by Davide Pesavento Signed-off-by: Marcel Holtmann Tested-by: Hugo Mildenberger Tested-by: Bing Zhao --- net/bluetooth/hci_sysfs.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 582d8877078..a05d45eb3ba 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -88,10 +88,13 @@ static struct device_type bt_link = { static void add_conn(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, work_add); + struct hci_dev *hdev = conn->hdev; /* ensure previous del is complete */ flush_work(&conn->work_del); + dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); + if (device_add(&conn->dev) < 0) { BT_ERR("Failed to register connection device"); return; @@ -154,12 +157,8 @@ void hci_conn_init_sysfs(struct hci_conn *conn) void hci_conn_add_sysfs(struct hci_conn *conn) { - struct hci_dev *hdev = conn->hdev; - BT_DBG("conn %p", conn); - dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); - queue_work(bt_workq, &conn->work_add); } -- cgit v1.2.3 From d1a2627a29667fe7c4a9d06e1579a2d65bd39bba Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Mon, 30 Mar 2009 17:50:17 -0700 Subject: wimax: fix oops if netlink fails to add attribute When sending a message to user space using wimax_msg(), if nla_put() fails, correctly interpret the return code from wimax_msg_alloc() as an err ptr and return the error code instead of crashing (as it is assuming than non-NULL means the pointer is ok). Signed-off-by: Inaky Perez-Gonzalez --- net/wimax/op-msg.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/wimax/op-msg.c b/net/wimax/op-msg.c index 5d149c1b5f0..9ad4d893a56 100644 --- a/net/wimax/op-msg.c +++ b/net/wimax/op-msg.c @@ -149,7 +149,8 @@ struct sk_buff *wimax_msg_alloc(struct wimax_dev *wimax_dev, } result = nla_put(skb, WIMAX_GNL_MSG_DATA, size, msg); if (result < 0) { - dev_err(dev, "no memory to add payload in attribute\n"); + dev_err(dev, "no memory to add payload (msg %p size %zu) in " + "attribute: %d\n", msg, size, result); goto error_nla_put; } genlmsg_end(skb, genl_msg); @@ -299,10 +300,10 @@ int wimax_msg(struct wimax_dev *wimax_dev, const char *pipe_name, struct sk_buff *skb; skb = wimax_msg_alloc(wimax_dev, pipe_name, buf, size, gfp_flags); - if (skb == NULL) - goto error_msg_new; - result = wimax_msg_send(wimax_dev, skb); -error_msg_new: + if (IS_ERR(skb)) + result = PTR_ERR(skb); + else + result = wimax_msg_send(wimax_dev, skb); return result; } EXPORT_SYMBOL_GPL(wimax_msg); -- cgit v1.2.3 From 94c7f2d49521b0bb3ab91cbeb3518ac34355d47f Mon Sep 17 00:00:00 2001 From: Inaky Perez-Gonzalez Date: Sat, 2 May 2009 02:30:28 -0700 Subject: wimax: oops: wimax_dev_add() is the only one that can initialize the state When a new wimax_dev is created, it's state has to be __WIMAX_ST_NULL until wimax_dev_add() is succesfully called. This allows calls into the stack that happen before said time to be rejected. Until now, the state was being set (by mistake) to UNINITIALIZED, which was allowing calls such as wimax_report_rfkill_hw() to go through even when a call to wimax_dev_add() had failed; that was causing an oops when touching uninitialized data. This situation is normal when the device starts reporting state before the whole initialization has been completed. It just has to be dealt with. Signed-off-by: Inaky Perez-Gonzalez --- net/wimax/stack.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/wimax/stack.c b/net/wimax/stack.c index a0ee76b5251..933e1422b09 100644 --- a/net/wimax/stack.c +++ b/net/wimax/stack.c @@ -338,8 +338,21 @@ out: */ void wimax_state_change(struct wimax_dev *wimax_dev, enum wimax_st new_state) { + /* + * A driver cannot take the wimax_dev out of the + * __WIMAX_ST_NULL state unless by calling wimax_dev_add(). If + * the wimax_dev's state is still NULL, we ignore any request + * to change its state because it means it hasn't been yet + * registered. + * + * There is no need to complain about it, as routines that + * call this might be shared from different code paths that + * are called before or after wimax_dev_add() has done its + * job. + */ mutex_lock(&wimax_dev->mutex); - __wimax_state_change(wimax_dev, new_state); + if (wimax_dev->state > __WIMAX_ST_NULL) + __wimax_state_change(wimax_dev, new_state); mutex_unlock(&wimax_dev->mutex); return; } @@ -376,7 +389,7 @@ EXPORT_SYMBOL_GPL(wimax_state_get); void wimax_dev_init(struct wimax_dev *wimax_dev) { INIT_LIST_HEAD(&wimax_dev->id_table_node); - __wimax_state_set(wimax_dev, WIMAX_ST_UNINITIALIZED); + __wimax_state_set(wimax_dev, __WIMAX_ST_NULL); mutex_init(&wimax_dev->mutex); mutex_init(&wimax_dev->mutex_reset); } -- cgit v1.2.3 From 6473990c7f0565fca2007f8662395d122e30f0d8 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Wed, 6 May 2009 16:45:07 -0700 Subject: net-sched: fix bfifo default limit When no limit is given, the bfifo uses a default of tx_queue_len * mtu. Packets handled by qdiscs include the link layer header, so this should be taken into account, similar to what other qdiscs do. Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/sched/sch_fifo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index 92cfc9d7e3b..69188e8358b 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -51,7 +51,7 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt) u32 limit = qdisc_dev(sch)->tx_queue_len ? : 1; if (sch->ops == &bfifo_qdisc_ops) - limit *= qdisc_dev(sch)->mtu; + limit *= psched_mtu(qdisc_dev(sch)); q->limit = limit; } else { -- cgit v1.2.3 From b805007545813d276c844f0f6d6c825b07c6aec6 Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Wed, 6 May 2009 16:49:18 -0700 Subject: net: update skb_recycle_check() for hardware timestamping changes Commit ac45f602ee3d1b6f326f68bc0c2591ceebf05ba4 ("net: infrastructure for hardware time stamping") added two skb initialization actions to __alloc_skb(), which need to be added to skb_recycle_check() as well. Signed-off-by: Lennert Buytenhek Signed-off-by: Patrick Ohly Signed-off-by: David S. Miller --- net/core/skbuff.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index f091a5a845c..d152394b261 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -502,7 +502,9 @@ int skb_recycle_check(struct sk_buff *skb, int skb_size) shinfo->gso_segs = 0; shinfo->gso_type = 0; shinfo->ip6_frag_id = 0; + shinfo->tx_flags.flags = 0; shinfo->frag_list = NULL; + memset(&shinfo->hwtstamps, 0, sizeof(shinfo->hwtstamps)); memset(skb, 0, offsetof(struct sk_buff, tail)); skb->data = skb->head + NET_SKB_PAD; -- cgit v1.2.3 From 9b05126baa146fc3f41360164141d4e1b3ea93c4 Mon Sep 17 00:00:00 2001 From: Ashish Karkare Date: Thu, 7 May 2009 16:31:01 -0700 Subject: net: remove stale reference to fastroute from Kconfig help text Signed-off-by: Ashish Karkare Signed-off-by: Lennert Buytenhek Signed-off-by: David S. Miller --- net/Kconfig | 6 ------ 1 file changed, 6 deletions(-) (limited to 'net') diff --git a/net/Kconfig b/net/Kconfig index ce77db4fcec..c19f549c8e7 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -119,12 +119,6 @@ menuconfig NETFILTER under "iptables" for the location of these packages. - Make sure to say N to "Fast switching" below if you intend to say Y - here, as Fast switching currently bypasses netfilter. - - Chances are that you should say Y here if you compile a kernel which - will run as a router and N for regular hosts. If unsure, say N. - if NETFILTER config NETFILTER_DEBUG -- cgit v1.2.3