From 053814ac3c51b7a60eb9b58c73ec289e55fd0d88 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 2 Nov 2011 13:29:56 -0700 Subject: [PATCH] 3.1 patches --- ...input-amp-handling-for-cx20549-codec.patch | 105 ++++++ ...wlagn-do-not-use-interruptible-waits.patch | 126 ++++++++ queue-3.1/jsm-remove-buggy-write-queue.patch | 300 ++++++++++++++++++ ...ss-in-vm_struct-via-proc-vmallocinfo.patch | 169 ++++++++++ ...g-to-free-nonexistent-resource-error.patch | 50 +++ ...he-empty-pathname-for-normal-lookups.patch | 133 ++++++++ queue-3.1/series | 8 + ...lloc-argument-order-in-um-vdso-vma.c.patch | 30 ++ queue-3.1/um-fix-ubd-cow-size.patch | 66 ++++ 9 files changed, 987 insertions(+) create mode 100644 queue-3.1/alsa-hda-fix-adc-input-amp-handling-for-cx20549-codec.patch create mode 100644 queue-3.1/iwlagn-do-not-use-interruptible-waits.patch create mode 100644 queue-3.1/jsm-remove-buggy-write-queue.patch create mode 100644 queue-3.1/mm-avoid-null-pointer-access-in-vm_struct-via-proc-vmallocinfo.patch create mode 100644 queue-3.1/omap-spi-fix-the-trying-to-free-nonexistent-resource-error.patch create mode 100644 queue-3.1/readlinkat-ensure-we-return-enoent-for-the-empty-pathname-for-normal-lookups.patch create mode 100644 queue-3.1/um-fix-kmalloc-argument-order-in-um-vdso-vma.c.patch create mode 100644 queue-3.1/um-fix-ubd-cow-size.patch diff --git a/queue-3.1/alsa-hda-fix-adc-input-amp-handling-for-cx20549-codec.patch b/queue-3.1/alsa-hda-fix-adc-input-amp-handling-for-cx20549-codec.patch new file mode 100644 index 00000000000..7cb1a7fe295 --- /dev/null +++ b/queue-3.1/alsa-hda-fix-adc-input-amp-handling-for-cx20549-codec.patch @@ -0,0 +1,105 @@ +From 6b45214277bec2193ad3ccb8d7aa6100b5a0f1a9 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 14 Oct 2011 15:26:20 +0200 +Subject: ALSA: hda - Fix ADC input-amp handling for Cx20549 codec + +From: Takashi Iwai + +commit 6b45214277bec2193ad3ccb8d7aa6100b5a0f1a9 upstream. + +It seems that Conexant CX20549 chip handle only a single input-amp even +though the audio-input widget has multiple sources. This has been never +clear, and I implemented in the current way based on the debug information +I got at the early time -- the device reacts individual input-amp values +for different sources. This is true for another Conexant codec, but it's +not applied to CX20549 actually. + +This patch changes the auto-parser code to handle a single input-amp +per audio-in widget for CX20549. After applying this, you'll see only a +single "Capture" volume control instead of separate "Mic" or "Line" +captures when the device is set up to use a single ADC. + +We haven't tested 20551 and 20561 codecs yet. If these show the similar +behavior like 20549, they need to set spec->single_adc_amp=1, too. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + + +--- + sound/pci/hda/patch_conexant.c | 30 ++++++++++++++++++++++++++++-- + 1 file changed, 28 insertions(+), 2 deletions(-) + +--- a/sound/pci/hda/patch_conexant.c ++++ b/sound/pci/hda/patch_conexant.c +@@ -137,6 +137,7 @@ struct conexant_spec { + unsigned int hp_laptop:1; + unsigned int asus:1; + unsigned int pin_eapd_ctrls:1; ++ unsigned int single_adc_amp:1; + + unsigned int adc_switching:1; + +@@ -4256,6 +4257,8 @@ static int cx_auto_add_capture_volume(st + int idx = get_input_connection(codec, adc_nid, nid); + if (idx < 0) + continue; ++ if (spec->single_adc_amp) ++ idx = 0; + return cx_auto_add_volume_idx(codec, label, pfx, + cidx, adc_nid, HDA_INPUT, idx); + } +@@ -4296,14 +4299,21 @@ static int cx_auto_build_input_controls( + struct hda_input_mux *imux = &spec->private_imux; + const char *prev_label; + int input_conn[HDA_MAX_NUM_INPUTS]; +- int i, err, cidx; ++ int i, j, err, cidx; + int multi_connection; + ++ if (!imux->num_items) ++ return 0; ++ + multi_connection = 0; + for (i = 0; i < imux->num_items; i++) { + cidx = get_input_connection(codec, spec->imux_info[i].adc, + spec->imux_info[i].pin); +- input_conn[i] = (spec->imux_info[i].adc << 8) | cidx; ++ if (cidx < 0) ++ continue; ++ input_conn[i] = spec->imux_info[i].adc; ++ if (!spec->single_adc_amp) ++ input_conn[i] |= cidx << 8; + if (i > 0 && input_conn[i] != input_conn[0]) + multi_connection = 1; + } +@@ -4332,6 +4342,15 @@ static int cx_auto_build_input_controls( + err = cx_auto_add_capture_volume(codec, nid, + "Capture", "", cidx); + } else { ++ bool dup_found = false; ++ for (j = 0; j < i; j++) { ++ if (input_conn[j] == input_conn[i]) { ++ dup_found = true; ++ break; ++ } ++ } ++ if (dup_found) ++ continue; + err = cx_auto_add_capture_volume(codec, nid, + label, " Capture", cidx); + } +@@ -4408,6 +4427,13 @@ static int patch_conexant_auto(struct hd + return -ENOMEM; + codec->spec = spec; + codec->pin_amp_workaround = 1; ++ ++ switch (codec->vendor_id) { ++ case 0x14f15045: ++ spec->single_adc_amp = 1; ++ break; ++ } ++ + err = cx_auto_search_adcs(codec); + if (err < 0) + return err; diff --git a/queue-3.1/iwlagn-do-not-use-interruptible-waits.patch b/queue-3.1/iwlagn-do-not-use-interruptible-waits.patch new file mode 100644 index 00000000000..ce1fd368265 --- /dev/null +++ b/queue-3.1/iwlagn-do-not-use-interruptible-waits.patch @@ -0,0 +1,126 @@ +From effd4d9aece9184f526e6556786a94d335e38b71 Mon Sep 17 00:00:00 2001 +From: Johannes Berg +Date: Thu, 15 Sep 2011 11:46:52 -0700 +Subject: iwlagn: do not use interruptible waits + +From: Johannes Berg + +commit effd4d9aece9184f526e6556786a94d335e38b71 upstream. + +Since the dawn of its time, iwlwifi has used +interruptible waits to wait for synchronous +commands and firmware loading. + +This leads to "interesting" bugs, because it +can't actually handle the interruptions; for +example when a command sending is interrupted +it will assume the command completed fully, +and then leave it pending, which leads to all +kinds of trouble when the command finishes +later. + +Since there's no easy way to gracefully deal +with interruptions, fix the driver to not use +interruptible waits. + +This at least fixes the error +iwlagn 0000:02:00.0: Error: Response NULL in 'REPLY_SCAN_ABORT_CMD' + +I have seen in P2P testing, but it is likely +that there are other errors caused by this. + +Cc: Stanislaw Gruszka +Cc: stable@kernel.org [2.6.24+] +Signed-off-by: Johannes Berg +Signed-off-by: Wey-Yi Guy +Signed-off-by: John W. Linville + + +--- + drivers/net/wireless/iwlwifi/iwl-agn-ucode.c | 9 ++------- + drivers/net/wireless/iwlwifi/iwl-core.c | 4 ++-- + drivers/net/wireless/iwlwifi/iwl-rx.c | 2 +- + drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c | 2 +- + drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c | 4 ++-- + 5 files changed, 8 insertions(+), 13 deletions(-) + +--- a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c ++++ b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c +@@ -113,13 +113,8 @@ static int iwlagn_load_section(struct iw + FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); + + IWL_DEBUG_FW(priv, "%s uCode section being loaded...\n", name); +- ret = wait_event_interruptible_timeout(priv->wait_command_queue, +- priv->ucode_write_complete, 5 * HZ); +- if (ret == -ERESTARTSYS) { +- IWL_ERR(priv, "Could not load the %s uCode section due " +- "to interrupt\n", name); +- return ret; +- } ++ ret = wait_event_timeout(priv->wait_command_queue, ++ priv->ucode_write_complete, 5 * HZ); + if (!ret) { + IWL_ERR(priv, "Could not load the %s uCode section\n", + name); +--- a/drivers/net/wireless/iwlwifi/iwl-core.c ++++ b/drivers/net/wireless/iwlwifi/iwl-core.c +@@ -867,7 +867,7 @@ void iwlagn_fw_error(struct iwl_priv *pr + * commands by clearing the ready bit */ + clear_bit(STATUS_READY, &priv->status); + +- wake_up_interruptible(&priv->wait_command_queue); ++ wake_up(&priv->wait_command_queue); + + if (!ondemand) { + /* +@@ -918,7 +918,7 @@ void iwl_irq_handle_error(struct iwl_pri + */ + clear_bit(STATUS_READY, &priv->status); + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); +- wake_up_interruptible(&priv->wait_command_queue); ++ wake_up(&priv->wait_command_queue); + IWL_ERR(priv, "RF is used by WiMAX\n"); + return; + } +--- a/drivers/net/wireless/iwlwifi/iwl-rx.c ++++ b/drivers/net/wireless/iwlwifi/iwl-rx.c +@@ -561,7 +561,7 @@ static void iwl_rx_card_state_notif(stru + wiphy_rfkill_set_hw_state(priv->hw->wiphy, + test_bit(STATUS_RF_KILL_HW, &priv->status)); + else +- wake_up_interruptible(&priv->wait_command_queue); ++ wake_up(&priv->wait_command_queue); + } + + static void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, +--- a/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c ++++ b/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c +@@ -671,7 +671,7 @@ void iwl_irq_tasklet(struct iwl_priv *pr + handled |= CSR_INT_BIT_FH_TX; + /* Wake up uCode load routine, now that load is complete */ + priv->ucode_write_complete = 1; +- wake_up_interruptible(&priv->wait_command_queue); ++ wake_up(&priv->wait_command_queue); + } + + if (inta & ~handled) { +--- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c ++++ b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c +@@ -790,7 +790,7 @@ void iwl_tx_cmd_complete(struct iwl_priv + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n", + get_cmd_string(cmd->hdr.cmd)); +- wake_up_interruptible(&priv->wait_command_queue); ++ wake_up(&priv->wait_command_queue); + } + + meta->flags = 0; +@@ -957,7 +957,7 @@ static int iwl_send_cmd_sync(struct iwl_ + return ret; + } + +- ret = wait_event_interruptible_timeout(priv->wait_command_queue, ++ ret = wait_event_timeout(priv->wait_command_queue, + !test_bit(STATUS_HCMD_ACTIVE, &priv->status), + HOST_COMPLETE_TIMEOUT); + if (!ret) { diff --git a/queue-3.1/jsm-remove-buggy-write-queue.patch b/queue-3.1/jsm-remove-buggy-write-queue.patch new file mode 100644 index 00000000000..580f7f2f69e --- /dev/null +++ b/queue-3.1/jsm-remove-buggy-write-queue.patch @@ -0,0 +1,300 @@ +From 9d898966c4a07e4a5092215b5a2829d0ef02baa2 Mon Sep 17 00:00:00 2001 +From: Thadeu Lima de Souza Cascardo +Date: Wed, 24 Aug 2011 13:14:22 -0300 +Subject: jsm: remove buggy write queue + +From: Thadeu Lima de Souza Cascardo + +commit 9d898966c4a07e4a5092215b5a2829d0ef02baa2 upstream. + +jsm uses a write queue that copies from uart_core circular buffer. This +copying however has some bugs, like not wrapping the head counter. Since +this write queue is also a circular buffer, the consumer function is +ready to use the uart_core circular buffer directly. + +This buggy copying function was making some bytes be dropped when +transmitting to a raw tty, doing something like this. + +[root@hostname ~]$ cat /dev/ttyn1 > cascardo/dump & +[1] 2658 +[root@hostname ~]$ cat /proc/tty/drivers > /dev/ttyn0 +[root@hostname ~]$ cat /proc/tty/drivers +/dev/tty /dev/tty 5 0 system:/dev/tty +/dev/console /dev/console 5 1 system:console +/dev/ptmx /dev/ptmx 5 2 system +/dev/vc/0 /dev/vc/0 4 0 system:vtmaster +jsm /dev/ttyn 250 0-31 serial +serial /dev/ttyS 4 64-95 serial +hvc /dev/hvc 229 0-7 system +pty_slave /dev/pts 136 0-1048575 pty:slave +pty_master /dev/ptm 128 0-1048575 pty:master +unknown /dev/tty 4 1-63 console +[root@hostname ~]$ cat cascardo/dump +/dev/tty /dev/tty 5 0 system:/dev/tty +/dev/console /dev/console 5 1 system:console +/dev/ptmx /dev/ptmx 5 2 system +/dev/vc/0 /dev/vc/0 4 0 system:vtmaste[root@hostname ~]$ + +This patch drops the driver write queue entirely, using the circular +buffer from uart_core only. + +Signed-off-by: Thadeu Lima de Souza Cascardo +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/tty/serial/jsm/jsm.h | 7 -- + drivers/tty/serial/jsm/jsm_driver.c | 1 + drivers/tty/serial/jsm/jsm_neo.c | 29 +++++------ + drivers/tty/serial/jsm/jsm_tty.c | 94 ++++-------------------------------- + 4 files changed, 28 insertions(+), 103 deletions(-) + +--- a/drivers/tty/serial/jsm/jsm.h ++++ b/drivers/tty/serial/jsm/jsm.h +@@ -183,10 +183,8 @@ struct jsm_board + /* Our Read/Error/Write queue sizes */ + #define RQUEUEMASK 0x1FFF /* 8 K - 1 */ + #define EQUEUEMASK 0x1FFF /* 8 K - 1 */ +-#define WQUEUEMASK 0x0FFF /* 4 K - 1 */ + #define RQUEUESIZE (RQUEUEMASK + 1) + #define EQUEUESIZE RQUEUESIZE +-#define WQUEUESIZE (WQUEUEMASK + 1) + + + /************************************************************************ +@@ -226,10 +224,6 @@ struct jsm_channel { + u16 ch_e_head; /* Head location of the error queue */ + u16 ch_e_tail; /* Tail location of the error queue */ + +- u8 *ch_wqueue; /* Our write queue buffer - malloc'ed */ +- u16 ch_w_head; /* Head location of the write queue */ +- u16 ch_w_tail; /* Tail location of the write queue */ +- + u64 ch_rxcount; /* total of data received so far */ + u64 ch_txcount; /* total of data transmitted so far */ + +@@ -378,7 +372,6 @@ extern int jsm_debug; + * Prototypes for non-static functions used in more than one module + * + *************************************************************************/ +-int jsm_tty_write(struct uart_port *port); + int jsm_tty_init(struct jsm_board *); + int jsm_uart_port_init(struct jsm_board *); + int jsm_remove_uart_port(struct jsm_board *); +--- a/drivers/tty/serial/jsm/jsm_driver.c ++++ b/drivers/tty/serial/jsm/jsm_driver.c +@@ -211,7 +211,6 @@ static void __devexit jsm_remove_one(str + if (brd->channels[i]) { + kfree(brd->channels[i]->ch_rqueue); + kfree(brd->channels[i]->ch_equeue); +- kfree(brd->channels[i]->ch_wqueue); + kfree(brd->channels[i]); + } + } +--- a/drivers/tty/serial/jsm/jsm_neo.c ++++ b/drivers/tty/serial/jsm/jsm_neo.c +@@ -496,12 +496,15 @@ static void neo_copy_data_from_queue_to_ + int s; + int qlen; + u32 len_written = 0; ++ struct circ_buf *circ; + + if (!ch) + return; + ++ circ = &ch->uart_port.state->xmit; ++ + /* No data to write to the UART */ +- if (ch->ch_w_tail == ch->ch_w_head) ++ if (uart_circ_empty(circ)) + return; + + /* If port is "stopped", don't send any data to the UART */ +@@ -517,11 +520,10 @@ static void neo_copy_data_from_queue_to_ + if (ch->ch_cached_lsr & UART_LSR_THRE) { + ch->ch_cached_lsr &= ~(UART_LSR_THRE); + +- writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_neo_uart->txrx); ++ writeb(circ->buf[circ->tail], &ch->ch_neo_uart->txrx); + jsm_printk(WRITE, INFO, &ch->ch_bd->pci_dev, +- "Tx data: %x\n", ch->ch_wqueue[ch->ch_w_head]); +- ch->ch_w_tail++; +- ch->ch_w_tail &= WQUEUEMASK; ++ "Tx data: %x\n", circ->buf[circ->head]); ++ circ->tail = (circ->tail + 1) & (UART_XMIT_SIZE - 1); + ch->ch_txcount++; + } + return; +@@ -536,36 +538,36 @@ static void neo_copy_data_from_queue_to_ + n = UART_17158_TX_FIFOSIZE - ch->ch_t_tlevel; + + /* cache head and tail of queue */ +- head = ch->ch_w_head & WQUEUEMASK; +- tail = ch->ch_w_tail & WQUEUEMASK; +- qlen = (head - tail) & WQUEUEMASK; ++ head = circ->head & (UART_XMIT_SIZE - 1); ++ tail = circ->tail & (UART_XMIT_SIZE - 1); ++ qlen = uart_circ_chars_pending(circ); + + /* Find minimum of the FIFO space, versus queue length */ + n = min(n, qlen); + + while (n > 0) { + +- s = ((head >= tail) ? head : WQUEUESIZE) - tail; ++ s = ((head >= tail) ? head : UART_XMIT_SIZE) - tail; + s = min(s, n); + + if (s <= 0) + break; + +- memcpy_toio(&ch->ch_neo_uart->txrxburst, ch->ch_wqueue + tail, s); ++ memcpy_toio(&ch->ch_neo_uart->txrxburst, circ->buf + tail, s); + /* Add and flip queue if needed */ +- tail = (tail + s) & WQUEUEMASK; ++ tail = (tail + s) & (UART_XMIT_SIZE - 1); + n -= s; + ch->ch_txcount += s; + len_written += s; + } + + /* Update the final tail */ +- ch->ch_w_tail = tail & WQUEUEMASK; ++ circ->tail = tail & (UART_XMIT_SIZE - 1); + + if (len_written >= ch->ch_t_tlevel) + ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM); + +- if (!jsm_tty_write(&ch->uart_port)) ++ if (uart_circ_empty(circ)) + uart_write_wakeup(&ch->uart_port); + } + +@@ -946,7 +948,6 @@ static void neo_param(struct jsm_channel + if ((ch->ch_c_cflag & (CBAUD)) == 0) { + ch->ch_r_head = ch->ch_r_tail = 0; + ch->ch_e_head = ch->ch_e_tail = 0; +- ch->ch_w_head = ch->ch_w_tail = 0; + + neo_flush_uart_write(ch); + neo_flush_uart_read(ch); +--- a/drivers/tty/serial/jsm/jsm_tty.c ++++ b/drivers/tty/serial/jsm/jsm_tty.c +@@ -118,6 +118,19 @@ static void jsm_tty_set_mctrl(struct uar + udelay(10); + } + ++/* ++ * jsm_tty_write() ++ * ++ * Take data from the user or kernel and send it out to the FEP. ++ * In here exists all the Transparent Print magic as well. ++ */ ++static void jsm_tty_write(struct uart_port *port) ++{ ++ struct jsm_channel *channel; ++ channel = container_of(port, struct jsm_channel, uart_port); ++ channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); ++} ++ + static void jsm_tty_start_tx(struct uart_port *port) + { + struct jsm_channel *channel = (struct jsm_channel *)port; +@@ -216,14 +229,6 @@ static int jsm_tty_open(struct uart_port + return -ENOMEM; + } + } +- if (!channel->ch_wqueue) { +- channel->ch_wqueue = kzalloc(WQUEUESIZE, GFP_KERNEL); +- if (!channel->ch_wqueue) { +- jsm_printk(INIT, ERR, &channel->ch_bd->pci_dev, +- "unable to allocate write queue buf"); +- return -ENOMEM; +- } +- } + + channel->ch_flags &= ~(CH_OPENING); + /* +@@ -237,7 +242,6 @@ static int jsm_tty_open(struct uart_port + */ + channel->ch_r_head = channel->ch_r_tail = 0; + channel->ch_e_head = channel->ch_e_tail = 0; +- channel->ch_w_head = channel->ch_w_tail = 0; + + brd->bd_ops->flush_uart_write(channel); + brd->bd_ops->flush_uart_read(channel); +@@ -836,75 +840,3 @@ void jsm_check_queue_flow_control(struct + } + } + } +- +-/* +- * jsm_tty_write() +- * +- * Take data from the user or kernel and send it out to the FEP. +- * In here exists all the Transparent Print magic as well. +- */ +-int jsm_tty_write(struct uart_port *port) +-{ +- int bufcount; +- int data_count = 0,data_count1 =0; +- u16 head; +- u16 tail; +- u16 tmask; +- u32 remain; +- int temp_tail = port->state->xmit.tail; +- struct jsm_channel *channel = (struct jsm_channel *)port; +- +- tmask = WQUEUEMASK; +- head = (channel->ch_w_head) & tmask; +- tail = (channel->ch_w_tail) & tmask; +- +- if ((bufcount = tail - head - 1) < 0) +- bufcount += WQUEUESIZE; +- +- bufcount = min(bufcount, 56); +- remain = WQUEUESIZE - head; +- +- data_count = 0; +- if (bufcount >= remain) { +- bufcount -= remain; +- while ((port->state->xmit.head != temp_tail) && +- (data_count < remain)) { +- channel->ch_wqueue[head++] = +- port->state->xmit.buf[temp_tail]; +- +- temp_tail++; +- temp_tail &= (UART_XMIT_SIZE - 1); +- data_count++; +- } +- if (data_count == remain) head = 0; +- } +- +- data_count1 = 0; +- if (bufcount > 0) { +- remain = bufcount; +- while ((port->state->xmit.head != temp_tail) && +- (data_count1 < remain)) { +- channel->ch_wqueue[head++] = +- port->state->xmit.buf[temp_tail]; +- +- temp_tail++; +- temp_tail &= (UART_XMIT_SIZE - 1); +- data_count1++; +- +- } +- } +- +- port->state->xmit.tail = temp_tail; +- +- data_count += data_count1; +- if (data_count) { +- head &= tmask; +- channel->ch_w_head = head; +- } +- +- if (data_count) { +- channel->ch_bd->bd_ops->copy_data_from_queue_to_uart(channel); +- } +- +- return data_count; +-} diff --git a/queue-3.1/mm-avoid-null-pointer-access-in-vm_struct-via-proc-vmallocinfo.patch b/queue-3.1/mm-avoid-null-pointer-access-in-vm_struct-via-proc-vmallocinfo.patch new file mode 100644 index 00000000000..ec093d80620 --- /dev/null +++ b/queue-3.1/mm-avoid-null-pointer-access-in-vm_struct-via-proc-vmallocinfo.patch @@ -0,0 +1,169 @@ +From f5252e009d5b87071a919221e4f6624184005368 Mon Sep 17 00:00:00 2001 +From: Mitsuo Hayasaka +Date: Mon, 31 Oct 2011 17:08:13 -0700 +Subject: mm: avoid null pointer access in vm_struct via /proc/vmallocinfo + +From: Mitsuo Hayasaka + +commit f5252e009d5b87071a919221e4f6624184005368 upstream. + +The /proc/vmallocinfo shows information about vmalloc allocations in +vmlist that is a linklist of vm_struct. It, however, may access pages +field of vm_struct where a page was not allocated. This results in a null +pointer access and leads to a kernel panic. + +Why this happens: In __vmalloc_node_range() called from vmalloc(), newly +allocated vm_struct is added to vmlist at __get_vm_area_node() and then, +some fields of vm_struct such as nr_pages and pages are set at +__vmalloc_area_node(). In other words, it is added to vmlist before it is +fully initialized. At the same time, when the /proc/vmallocinfo is read, +it accesses the pages field of vm_struct according to the nr_pages field +at show_numa_info(). Thus, a null pointer access happens. + +The patch adds the newly allocated vm_struct to the vmlist *after* it is +fully initialized. So, it can avoid accessing the pages field with +unallocated page when show_numa_info() is called. + +Signed-off-by: Mitsuo Hayasaka +Cc: Andrew Morton +Cc: David Rientjes +Cc: Namhyung Kim +Cc: "Paul E. McKenney" +Cc: Jeremy Fitzhardinge +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + include/linux/vmalloc.h | 1 + mm/vmalloc.c | 65 +++++++++++++++++++++++++++++++++++------------- + 2 files changed, 49 insertions(+), 17 deletions(-) + +--- a/include/linux/vmalloc.h ++++ b/include/linux/vmalloc.h +@@ -13,6 +13,7 @@ struct vm_area_struct; /* vma defining + #define VM_MAP 0x00000004 /* vmap()ed pages */ + #define VM_USERMAP 0x00000008 /* suitable for remap_vmalloc_range */ + #define VM_VPAGES 0x00000010 /* buffer for pages was vmalloc'ed */ ++#define VM_UNLIST 0x00000020 /* vm_struct is not listed in vmlist */ + /* bits [20..32] reserved for arch specific ioremap internals */ + + /* +--- a/mm/vmalloc.c ++++ b/mm/vmalloc.c +@@ -1253,18 +1253,22 @@ EXPORT_SYMBOL_GPL(map_vm_area); + DEFINE_RWLOCK(vmlist_lock); + struct vm_struct *vmlist; + +-static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va, ++static void setup_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va, + unsigned long flags, void *caller) + { +- struct vm_struct *tmp, **p; +- + vm->flags = flags; + vm->addr = (void *)va->va_start; + vm->size = va->va_end - va->va_start; + vm->caller = caller; + va->private = vm; + va->flags |= VM_VM_AREA; ++} ++ ++static void insert_vmalloc_vmlist(struct vm_struct *vm) ++{ ++ struct vm_struct *tmp, **p; + ++ vm->flags &= ~VM_UNLIST; + write_lock(&vmlist_lock); + for (p = &vmlist; (tmp = *p) != NULL; p = &tmp->next) { + if (tmp->addr >= vm->addr) +@@ -1275,6 +1279,13 @@ static void insert_vmalloc_vm(struct vm_ + write_unlock(&vmlist_lock); + } + ++static void insert_vmalloc_vm(struct vm_struct *vm, struct vmap_area *va, ++ unsigned long flags, void *caller) ++{ ++ setup_vmalloc_vm(vm, va, flags, caller); ++ insert_vmalloc_vmlist(vm); ++} ++ + static struct vm_struct *__get_vm_area_node(unsigned long size, + unsigned long align, unsigned long flags, unsigned long start, + unsigned long end, int node, gfp_t gfp_mask, void *caller) +@@ -1313,7 +1324,18 @@ static struct vm_struct *__get_vm_area_n + return NULL; + } + +- insert_vmalloc_vm(area, va, flags, caller); ++ /* ++ * When this function is called from __vmalloc_node_range, ++ * we do not add vm_struct to vmlist here to avoid ++ * accessing uninitialized members of vm_struct such as ++ * pages and nr_pages fields. They will be set later. ++ * To distinguish it from others, we use a VM_UNLIST flag. ++ */ ++ if (flags & VM_UNLIST) ++ setup_vmalloc_vm(area, va, flags, caller); ++ else ++ insert_vmalloc_vm(area, va, flags, caller); ++ + return area; + } + +@@ -1381,17 +1403,20 @@ struct vm_struct *remove_vm_area(const v + va = find_vmap_area((unsigned long)addr); + if (va && va->flags & VM_VM_AREA) { + struct vm_struct *vm = va->private; +- struct vm_struct *tmp, **p; +- /* +- * remove from list and disallow access to this vm_struct +- * before unmap. (address range confliction is maintained by +- * vmap.) +- */ +- write_lock(&vmlist_lock); +- for (p = &vmlist; (tmp = *p) != vm; p = &tmp->next) +- ; +- *p = tmp->next; +- write_unlock(&vmlist_lock); ++ ++ if (!(vm->flags & VM_UNLIST)) { ++ struct vm_struct *tmp, **p; ++ /* ++ * remove from list and disallow access to ++ * this vm_struct before unmap. (address range ++ * confliction is maintained by vmap.) ++ */ ++ write_lock(&vmlist_lock); ++ for (p = &vmlist; (tmp = *p) != vm; p = &tmp->next) ++ ; ++ *p = tmp->next; ++ write_unlock(&vmlist_lock); ++ } + + vmap_debug_free_range(va->va_start, va->va_end); + free_unmap_vmap_area(va); +@@ -1602,8 +1627,8 @@ void *__vmalloc_node_range(unsigned long + if (!size || (size >> PAGE_SHIFT) > totalram_pages) + return NULL; + +- area = __get_vm_area_node(size, align, VM_ALLOC, start, end, node, +- gfp_mask, caller); ++ area = __get_vm_area_node(size, align, VM_ALLOC | VM_UNLIST, ++ start, end, node, gfp_mask, caller); + + if (!area) + return NULL; +@@ -1611,6 +1636,12 @@ void *__vmalloc_node_range(unsigned long + addr = __vmalloc_area_node(area, gfp_mask, prot, node, caller); + + /* ++ * In this function, newly allocated vm_struct is not added ++ * to vmlist at __get_vm_area_node(). so, it is added here. ++ */ ++ insert_vmalloc_vmlist(area); ++ ++ /* + * A ref_count = 3 is needed because the vm_struct and vmap_area + * structures allocated in the __get_vm_area_node() function contain + * references to the virtual address of the vmalloc'ed block. diff --git a/queue-3.1/omap-spi-fix-the-trying-to-free-nonexistent-resource-error.patch b/queue-3.1/omap-spi-fix-the-trying-to-free-nonexistent-resource-error.patch new file mode 100644 index 00000000000..5b51774dccd --- /dev/null +++ b/queue-3.1/omap-spi-fix-the-trying-to-free-nonexistent-resource-error.patch @@ -0,0 +1,50 @@ +From 1458d160de3f1862aeaac57447ba96e7857ac52b Mon Sep 17 00:00:00 2001 +From: Shubhrajyoti D +Date: Mon, 24 Oct 2011 15:54:24 +0530 +Subject: OMAP: SPI: Fix the trying to free nonexistent resource error + +From: Shubhrajyoti D + +commit 1458d160de3f1862aeaac57447ba96e7857ac52b upstream. + +Currently there is a request_mem_region(r->start, .. +followed by r->start += pdata->regs_offset; + +And then in remove + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, resource_size(r)); + +Here the offset addition is not taken care. Fix the code for the +same. + +Signed-off-by: Shubhrajyoti D +Signed-off-by: Grant Likely +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/spi/spi-omap2-mcspi.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +--- a/drivers/spi/spi-omap2-mcspi.c ++++ b/drivers/spi/spi-omap2-mcspi.c +@@ -1116,15 +1116,16 @@ static int __init omap2_mcspi_probe(stru + status = -ENODEV; + goto err1; + } ++ ++ r->start += pdata->regs_offset; ++ r->end += pdata->regs_offset; ++ mcspi->phys = r->start; + if (!request_mem_region(r->start, resource_size(r), + dev_name(&pdev->dev))) { + status = -EBUSY; + goto err1; + } + +- r->start += pdata->regs_offset; +- r->end += pdata->regs_offset; +- mcspi->phys = r->start; + mcspi->base = ioremap(r->start, resource_size(r)); + if (!mcspi->base) { + dev_dbg(&pdev->dev, "can't ioremap MCSPI\n"); diff --git a/queue-3.1/readlinkat-ensure-we-return-enoent-for-the-empty-pathname-for-normal-lookups.patch b/queue-3.1/readlinkat-ensure-we-return-enoent-for-the-empty-pathname-for-normal-lookups.patch new file mode 100644 index 00000000000..3e25198cf9c --- /dev/null +++ b/queue-3.1/readlinkat-ensure-we-return-enoent-for-the-empty-pathname-for-normal-lookups.patch @@ -0,0 +1,133 @@ +From 1fa1e7f615f4d3ae436fa319af6e4eebdd4026a8 Mon Sep 17 00:00:00 2001 +From: Andy Whitcroft +Date: Wed, 2 Nov 2011 09:44:39 +0100 +Subject: readlinkat: ensure we return ENOENT for the empty pathname for normal lookups + +From: Andy Whitcroft + +commit 1fa1e7f615f4d3ae436fa319af6e4eebdd4026a8 upstream. + +Since the commit below which added O_PATH support to the *at() calls, the +error return for readlink/readlinkat for the empty pathname has switched +from ENOENT to EINVAL: + + commit 65cfc6722361570bfe255698d9cd4dccaf47570d + Author: Al Viro + Date: Sun Mar 13 15:56:26 2011 -0400 + + readlinkat(), fchownat() and fstatat() with empty relative pathnames + +This is both unexpected for userspace and makes readlink/readlinkat +inconsistant with all other interfaces; and inconsistant with our stated +return for these pathnames. + +As the readlinkat call does not have a flags parameter we cannot use the +AT_EMPTY_PATH approach used in the other calls. Therefore expose whether +the original path is infact entry via a new user_path_at_empty() path +lookup function. Use this to determine whether to default to EINVAL or +ENOENT for failures. + +Addresses http://bugs.launchpad.net/bugs/817187 + +[akpm@linux-foundation.org: remove unused getname_flags()] +Signed-off-by: Andy Whitcroft +Cc: Christoph Hellwig +Cc: Al Viro +Signed-off-by: Andrew Morton +Signed-off-by: Christoph Hellwig +Signed-off-by: Greg Kroah-Hartman + +--- + fs/namei.c | 18 +++++++++++++----- + fs/stat.c | 5 +++-- + include/linux/namei.h | 1 + + 3 files changed, 17 insertions(+), 7 deletions(-) + +--- a/fs/namei.c ++++ b/fs/namei.c +@@ -137,7 +137,7 @@ static int do_getname(const char __user + return retval; + } + +-static char *getname_flags(const char __user * filename, int flags) ++static char *getname_flags(const char __user *filename, int flags, int *empty) + { + char *tmp, *result; + +@@ -148,6 +148,8 @@ static char *getname_flags(const char __ + + result = tmp; + if (retval < 0) { ++ if (retval == -ENOENT && empty) ++ *empty = 1; + if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) { + __putname(tmp); + result = ERR_PTR(retval); +@@ -160,7 +162,7 @@ static char *getname_flags(const char __ + + char *getname(const char __user * filename) + { +- return getname_flags(filename, 0); ++ return getname_flags(filename, 0, 0); + } + + #ifdef CONFIG_AUDITSYSCALL +@@ -1798,11 +1800,11 @@ struct dentry *lookup_one_len(const char + return __lookup_hash(&this, base, NULL); + } + +-int user_path_at(int dfd, const char __user *name, unsigned flags, +- struct path *path) ++int user_path_at_empty(int dfd, const char __user *name, unsigned flags, ++ struct path *path, int *empty) + { + struct nameidata nd; +- char *tmp = getname_flags(name, flags); ++ char *tmp = getname_flags(name, flags, empty); + int err = PTR_ERR(tmp); + if (!IS_ERR(tmp)) { + +@@ -1816,6 +1818,12 @@ int user_path_at(int dfd, const char __u + return err; + } + ++int user_path_at(int dfd, const char __user *name, unsigned flags, ++ struct path *path) ++{ ++ return user_path_at_empty(dfd, name, flags, path, 0); ++} ++ + static int user_path_parent(int dfd, const char __user *path, + struct nameidata *nd, char **name) + { +--- a/fs/stat.c ++++ b/fs/stat.c +@@ -294,15 +294,16 @@ SYSCALL_DEFINE4(readlinkat, int, dfd, co + { + struct path path; + int error; ++ int empty = 0; + + if (bufsiz <= 0) + return -EINVAL; + +- error = user_path_at(dfd, pathname, LOOKUP_EMPTY, &path); ++ error = user_path_at_empty(dfd, pathname, LOOKUP_EMPTY, &path, &empty); + if (!error) { + struct inode *inode = path.dentry->d_inode; + +- error = -EINVAL; ++ error = empty ? -ENOENT : -EINVAL; + if (inode->i_op->readlink) { + error = security_inode_readlink(path.dentry); + if (!error) { +--- a/include/linux/namei.h ++++ b/include/linux/namei.h +@@ -67,6 +67,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LA + #define LOOKUP_EMPTY 0x4000 + + extern int user_path_at(int, const char __user *, unsigned, struct path *); ++extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty); + + #define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path) + #define user_lpath(name, path) user_path_at(AT_FDCWD, name, 0, path) diff --git a/queue-3.1/series b/queue-3.1/series index ff7d642d556..8b73f61433b 100644 --- a/queue-3.1/series +++ b/queue-3.1/series @@ -147,3 +147,11 @@ md-raid10-fix-bug-when-activating-a-hot-spare.patch plat-mxc-iomux-v3.h-implicitly-enable-pull-up-down-when-that-s-desired.patch arm-pxa-cm-x300-properly-set-bt_reset-pin.patch arm-mach-ux500-unlock-i-d-l2x0-caches-before-init.patch +mm-avoid-null-pointer-access-in-vm_struct-via-proc-vmallocinfo.patch +alsa-hda-fix-adc-input-amp-handling-for-cx20549-codec.patch +iwlagn-do-not-use-interruptible-waits.patch +um-fix-ubd-cow-size.patch +readlinkat-ensure-we-return-enoent-for-the-empty-pathname-for-normal-lookups.patch +um-fix-kmalloc-argument-order-in-um-vdso-vma.c.patch +omap-spi-fix-the-trying-to-free-nonexistent-resource-error.patch +jsm-remove-buggy-write-queue.patch diff --git a/queue-3.1/um-fix-kmalloc-argument-order-in-um-vdso-vma.c.patch b/queue-3.1/um-fix-kmalloc-argument-order-in-um-vdso-vma.c.patch new file mode 100644 index 00000000000..25f04ad290f --- /dev/null +++ b/queue-3.1/um-fix-kmalloc-argument-order-in-um-vdso-vma.c.patch @@ -0,0 +1,30 @@ +From 0d65ede0a605d6252acc5c8a9c536c4cd0211f3c Mon Sep 17 00:00:00 2001 +From: Dave Jones +Date: Mon, 24 Oct 2011 18:15:32 -0400 +Subject: um: Fix kmalloc argument order in um/vdso/vma.c + +From: Dave Jones + +commit 0d65ede0a605d6252acc5c8a9c536c4cd0211f3c upstream. + +kmalloc size is 1st arg, not second. + +Signed-off-by: Dave Jones +Signed-off-by: Richard Weinberger +Signed-off-by: Greg Kroah-Hartman + +--- + arch/um/sys-x86_64/vdso/vma.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/arch/um/sys-x86_64/vdso/vma.c ++++ b/arch/um/sys-x86_64/vdso/vma.c +@@ -28,7 +28,7 @@ static int __init init_vdso(void) + + um_vdso_addr = task_size - PAGE_SIZE; + +- vdsop = kmalloc(GFP_KERNEL, sizeof(struct page *)); ++ vdsop = kmalloc(sizeof(struct page *), GFP_KERNEL); + if (!vdsop) + goto oom; + diff --git a/queue-3.1/um-fix-ubd-cow-size.patch b/queue-3.1/um-fix-ubd-cow-size.patch new file mode 100644 index 00000000000..2d03c6bc5e8 --- /dev/null +++ b/queue-3.1/um-fix-ubd-cow-size.patch @@ -0,0 +1,66 @@ +From 8535639810e578960233ad39def3ac2157b0c3ec Mon Sep 17 00:00:00 2001 +From: Richard Weinberger +Date: Wed, 2 Nov 2011 13:17:27 +0100 +Subject: um: fix ubd cow size + +From: Richard Weinberger + +commit 8535639810e578960233ad39def3ac2157b0c3ec upstream. + +ubd_file_size() cannot use ubd_dev->cow.file because at this time +ubd_dev->cow.file is not initialized. +Therefore, ubd_file_size() will always report a wrong disk size when +COW files are used. +Reading from /dev/ubd* would crash the kernel. + +We have to read the correct disk size from the COW file's backing +file. + +Signed-off-by: Richard Weinberger +Signed-off-by: Greg Kroah-Hartman + +--- + arch/um/drivers/ubd_kern.c | 31 ++++++++++++++++++++++++++++++- + 1 file changed, 30 insertions(+), 1 deletion(-) + +--- a/arch/um/drivers/ubd_kern.c ++++ b/arch/um/drivers/ubd_kern.c +@@ -513,8 +513,37 @@ __uml_exitcall(kill_io_thread); + static inline int ubd_file_size(struct ubd *ubd_dev, __u64 *size_out) + { + char *file; ++ int fd; ++ int err; + +- file = ubd_dev->cow.file ? ubd_dev->cow.file : ubd_dev->file; ++ __u32 version; ++ __u32 align; ++ char *backing_file; ++ time_t mtime; ++ unsigned long long size; ++ int sector_size; ++ int bitmap_offset; ++ ++ if (ubd_dev->file && ubd_dev->cow.file) { ++ file = ubd_dev->cow.file; ++ ++ goto out; ++ } ++ ++ fd = os_open_file(ubd_dev->file, global_openflags, 0); ++ if (fd < 0) ++ return fd; ++ ++ err = read_cow_header(file_reader, &fd, &version, &backing_file, \ ++ &mtime, &size, §or_size, &align, &bitmap_offset); ++ os_close_file(fd); ++ ++ if(err == -EINVAL) ++ file = ubd_dev->file; ++ else ++ file = backing_file; ++ ++out: + return os_file_size(file, size_out); + } + -- 2.47.3