summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNitin Kumbhar <nkumbhar@nvidia.com>2011-04-25 14:42:58 +0530
committerNitin Kumbhar <nkumbhar@nvidia.com>2011-04-25 14:42:58 +0530
commited22062df7ae836e674c01f69e8567ad51ba31b1 (patch)
tree32972feea81e770d650a3dc7d36907a7c49f9d4d
parent90651aad036485974b99101df66a50e57c19e732 (diff)
parent2e35e1d7b965893e68f2fb1af77129406be5ff05 (diff)
merging android-tegra-2.6.36 into git-master/linux-2.6/android-tegra-2.6.36
Conflicts: arch/arm/mm/proc-v7.S drivers/video/tegra/dc/dc.c Change-Id: I40be0d615f14f1c01305388a706d257f624ba968
-rw-r--r--arch/arm/Kconfig10
-rw-r--r--drivers/mmc/card/block.c44
-rw-r--r--drivers/mmc/core/Makefile3
-rw-r--r--drivers/mmc/core/mmc_ops.c11
-rw-r--r--drivers/mmc/core/mmc_ops.h1
-rw-r--r--drivers/mmc/core/quirks.c48
-rw-r--r--drivers/mmc/core/sdio.c1
-rw-r--r--drivers/net/pppolac.c95
-rw-r--r--drivers/net/pppopns.c87
-rw-r--r--drivers/net/wireless/bcm4329/dhd.h5
-rw-r--r--drivers/net/wireless/bcm4329/dhd_common.c78
-rw-r--r--drivers/net/wireless/bcm4329/dhd_linux.c55
-rw-r--r--drivers/video/tegra/dc/dc.c2
-rw-r--r--drivers/video/tegra/dc/nvhdcp.c3
-rw-r--r--include/linux/if_pppox.h22
-rw-r--r--include/linux/mmc/card.h93
-rw-r--r--include/linux/mmc/core.h1
-rw-r--r--include/net/tcp.h2
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/ipv4/devinet.c3
-rw-r--r--net/ipv4/tcp.c97
-rw-r--r--net/ipv4/tcp_ipv4.c43
-rw-r--r--net/ipv6/af_inet6.c17
23 files changed, 629 insertions, 94 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 0a293ee4a313..cd2c427f0e2b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1129,16 +1129,6 @@ config ARM_ERRATA_743622
visible impact on the overall performance or power consumption of the
processor.
-config ARM_ERRATA_720791
- bool "ARM errata: Dynamic high-level clock gating corrupts the Jazelle instruction stream"
- depends on CPU_V7
- help
- This option enables the workaround for the 720791 Cortex-A9
- (r1p0..r1p2) erratum. The Jazelle instruction stream may be
- corrupted when dynamic high-level clock gating is enabled.
- This workaround disables gating the Core clock when the Instruction
- side is waiting for a Page Table Walk answer or linefill completion.
-
endmenu
source "arch/arm/common/Kconfig"
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7054fd5863a0..e6f74718e06f 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -45,6 +45,13 @@
MODULE_ALIAS("mmc:block");
+#define INAND_CMD38_ARG_EXT_CSD 113
+#define INAND_CMD38_ARG_ERASE 0x00
+#define INAND_CMD38_ARG_TRIM 0x01
+#define INAND_CMD38_ARG_SECERASE 0x80
+#define INAND_CMD38_ARG_SECTRIM1 0x81
+#define INAND_CMD38_ARG_SECTRIM2 0x88
+
/*
* max 8 partitions per card
*/
@@ -265,6 +272,15 @@ static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
else
arg = MMC_ERASE_ARG;
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ arg == MMC_TRIM_ARG ?
+ INAND_CMD38_ARG_TRIM :
+ INAND_CMD38_ARG_ERASE);
+ if (err)
+ goto out;
+ }
err = mmc_erase(card, from, nr, arg);
out:
spin_lock_irq(&md->lock);
@@ -299,9 +315,26 @@ static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
else
arg = MMC_SECURE_ERASE_ARG;
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ arg == MMC_SECURE_TRIM1_ARG ?
+ INAND_CMD38_ARG_SECTRIM1 :
+ INAND_CMD38_ARG_SECERASE);
+ if (err)
+ goto out;
+ }
err = mmc_erase(card, from, nr, arg);
- if (!err && arg == MMC_SECURE_TRIM1_ARG)
+ if (!err && arg == MMC_SECURE_TRIM1_ARG) {
+ if (card->quirks & MMC_QUIRK_INAND_CMD38) {
+ err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ INAND_CMD38_ARG_EXT_CSD,
+ INAND_CMD38_ARG_SECTRIM2);
+ if (err)
+ goto out;
+ }
err = mmc_erase(card, from, nr, MMC_SECURE_TRIM2_ARG);
+ }
out:
spin_lock_irq(&md->lock);
__blk_end_request(req, err, blk_rq_bytes(req));
@@ -686,6 +719,13 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card)
return 0;
}
+static const struct mmc_fixup blk_fixups[] =
+{
+ MMC_FIXUP("SEM16G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
+ MMC_FIXUP("SEM32G", 0x2, 0x100, add_quirk, MMC_QUIRK_INAND_CMD38),
+ END_FIXUP
+};
+
static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md;
@@ -714,6 +754,8 @@ static int mmc_blk_probe(struct mmc_card *card)
cap_str, md->read_only ? "(ro)" : "");
mmc_set_drvdata(card, md);
+ mmc_fixup_device(card, blk_fixups);
+
#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME
mmc_set_bus_resume_policy(card->host, 1);
#endif
diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile
index 889e5f898f6f..f25f6a5d6ddd 100644
--- a/drivers/mmc/core/Makefile
+++ b/drivers/mmc/core/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_MMC) += mmc_core.o
mmc_core-y := core.o bus.o host.o \
mmc.o mmc_ops.o sd.o sd_ops.o \
sdio.o sdio_ops.o sdio_bus.o \
- sdio_cis.o sdio_io.o sdio_irq.o
+ sdio_cis.o sdio_io.o sdio_irq.o \
+ quirks.o
mmc_core-$(CONFIG_DEBUG_FS) += debugfs.o
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index 326447c9ede8..b6d75eec4b36 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -387,6 +387,15 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
return err;
}
+/**
+ * mmc_switch - modify EXT_CSD register
+ * @card: the MMC card associated with the data transfer
+ * @set: cmd set values
+ * @index: EXT_CSD register index
+ * @value: value to program into EXT_CSD register
+ *
+ * Modifies the EXT_CSD register for selected card.
+ */
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
{
int err;
@@ -434,6 +443,8 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value)
return 0;
}
+EXPORT_SYMBOL(mmc_switch);
+
int mmc_send_status(struct mmc_card *card, u32 *status)
{
int err;
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index 653eb8e84178..b1a806fbd396 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -20,7 +20,6 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid);
int mmc_set_relative_addr(struct mmc_card *card);
int mmc_send_csd(struct mmc_card *card, u32 *csd);
int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd);
-int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value);
int mmc_send_status(struct mmc_card *card, u32 *status);
int mmc_send_cid(struct mmc_host *host, u32 *cid);
int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
new file mode 100644
index 000000000000..981c11343f45
--- /dev/null
+++ b/drivers/mmc/core/quirks.c
@@ -0,0 +1,48 @@
+/*
+ * This file contains work-arounds for many known sdio hardware
+ * bugs.
+ *
+ * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com>
+ * Inspired from pci fixup code:
+ * Copyright (c) 1999 Martin Mares <mj@ucw.cz>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mmc/card.h>
+
+static const struct mmc_fixup mmc_fixup_methods[] = {
+ END_FIXUP
+};
+
+void mmc_fixup_device(struct mmc_card *card,
+ const struct mmc_fixup *table)
+{
+ const struct mmc_fixup *f;
+ u64 rev = cid_rev_card(card);
+
+ /* Non-core specific workarounds. */
+ if (!table)
+ table = mmc_fixup_methods;
+
+ for (f = table; f->vendor_fixup; f++) {
+ if ((f->manfid == CID_MANFID_ANY
+ || f->manfid == card->cid.manfid) &&
+ (f->oemid == CID_OEMID_ANY
+ || f->oemid == card->cid.oemid) &&
+ (f->name == CID_NAME_ANY
+ || !strcmp(f->name, card->cid.prod_name)) &&
+ (f->cis_vendor == card->cis.vendor
+ || f->cis_vendor == (u16) SDIO_ANY_ID) &&
+ (f->cis_device == card->cis.device
+ || f->cis_device == (u16) SDIO_ANY_ID) &&
+ rev >= f->rev_start &&
+ rev <= f->rev_end) {
+ dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup);
+ f->vendor_fixup(card, f->data);
+ }
+ }
+}
+EXPORT_SYMBOL(mmc_fixup_device);
+
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 155efb1ac797..d3ec4dc18d14 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -480,6 +480,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
card = oldcard;
return 0;
}
+ mmc_fixup_device(card, NULL);
if (card->type == MMC_TYPE_SD_COMBO) {
err = mmc_sd_setup_card(host, card, oldcard != NULL);
diff --git a/drivers/net/pppolac.c b/drivers/net/pppolac.c
index af3202a920a0..c94b8507d92b 100644
--- a/drivers/net/pppolac.c
+++ b/drivers/net/pppolac.c
@@ -15,12 +15,15 @@
*/
/* This driver handles L2TP data packets between a UDP socket and a PPP channel.
- * To keep things simple, only one session per socket is permitted. Packets are
- * sent via the socket, so it must keep connected to the same address. One must
- * not set sequencing in ICCN but let LNS controll it. Currently this driver
- * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */
+ * The socket must keep connected, and only one session per socket is permitted.
+ * Sequencing of outgoing packets is controlled by LNS. Incoming packets with
+ * sequences are reordered within a sliding window of one second. Currently
+ * reordering only happens when a packet is received. It is done for simplicity
+ * since no additional locks or threads are required. This driver only works on
+ * IPv4 due to the lack of UDP encapsulation support in IPv6. */
#include <linux/module.h>
+#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/file.h>
@@ -53,14 +56,28 @@ static inline union unaligned *unaligned(void *ptr)
return (union unaligned *)ptr;
}
+struct meta {
+ __u32 sequence;
+ __u32 timestamp;
+};
+
+static inline struct meta *skb_meta(struct sk_buff *skb)
+{
+ return (struct meta *)skb->cb;
+}
+
+/******************************************************************************/
+
static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
{
struct sock *sk = (struct sock *)sk_udp->sk_user_data;
struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac;
+ struct meta *meta = skb_meta(skb);
+ __u32 now = jiffies;
__u8 bits;
__u8 *ptr;
- /* Drop the packet if it is too short. */
+ /* Drop the packet if L2TP header is missing. */
if (skb->len < sizeof(struct udphdr) + 6)
goto drop;
@@ -99,9 +116,12 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
if (unaligned(ptr)->u32 != opt->local)
goto drop;
- /* Check the sequence if it is present. According to RFC 2661 section
- * 5.4, the only thing to do is to update opt->sequencing. */
- opt->sequencing = bits & L2TP_SEQUENCE_BIT;
+ /* Check the sequence if it is present. */
+ if (bits & L2TP_SEQUENCE_BIT) {
+ meta->sequence = ptr[4] << 8 | ptr[5];
+ if ((__s16)(meta->sequence - opt->recv_sequence) < 0)
+ goto drop;
+ }
/* Skip PPP address and control if they are present. */
if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
@@ -112,7 +132,54 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
if (skb->len >= 1 && skb->data[0] & 1)
skb_push(skb, 1)[0] = 0;
- /* Finally, deliver the packet to PPP channel. */
+ /* Drop the packet if PPP protocol is missing. */
+ if (skb->len < 2)
+ goto drop;
+
+ /* Perform reordering if sequencing is enabled. */
+ atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT);
+ if (bits & L2TP_SEQUENCE_BIT) {
+ struct sk_buff *skb1;
+
+ /* Insert the packet into receive queue in order. */
+ skb_set_owner_r(skb, sk);
+ skb_queue_walk(&sk->sk_receive_queue, skb1) {
+ struct meta *meta1 = skb_meta(skb1);
+ __s16 order = meta->sequence - meta1->sequence;
+ if (order == 0)
+ goto drop;
+ if (order < 0) {
+ meta->timestamp = meta1->timestamp;
+ skb_insert(skb1, skb, &sk->sk_receive_queue);
+ skb = NULL;
+ break;
+ }
+ }
+ if (skb) {
+ meta->timestamp = now;
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+ }
+
+ /* Remove packets from receive queue as long as
+ * 1. the receive buffer is full,
+ * 2. they are queued longer than one second, or
+ * 3. there are no missing packets before them. */
+ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
+ meta = skb_meta(skb);
+ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+ now - meta->timestamp < HZ &&
+ meta->sequence != opt->recv_sequence)
+ break;
+ skb_unlink(skb, &sk->sk_receive_queue);
+ opt->recv_sequence = (__u16)(meta->sequence + 1);
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ }
+ return NET_RX_SUCCESS;
+ }
+
+ /* Flush receive queue if sequencing is disabled. */
+ skb_queue_purge(&sk->sk_receive_queue);
skb_orphan(skb);
ppp_input(&pppox_sk(sk)->chan, skb);
return NET_RX_SUCCESS;
@@ -163,14 +230,14 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->data[1] = PPP_CTRL;
/* Install L2TP header. */
- if (opt->sequencing) {
+ if (atomic_read(&opt->sequencing)) {
skb_push(skb, 10);
skb->data[0] = L2TP_SEQUENCE_BIT;
- skb->data[6] = opt->sequence >> 8;
- skb->data[7] = opt->sequence;
+ skb->data[6] = opt->xmit_sequence >> 8;
+ skb->data[7] = opt->xmit_sequence;
skb->data[8] = 0;
skb->data[9] = 0;
- opt->sequence++;
+ opt->xmit_sequence++;
} else {
skb_push(skb, 6);
skb->data[0] = 0;
@@ -246,6 +313,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
po->chan.mtu = PPP_MTU - 80;
po->proto.lac.local = unaligned(&addr->local)->u32;
po->proto.lac.remote = unaligned(&addr->remote)->u32;
+ atomic_set(&po->proto.lac.sequencing, 1);
po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv;
error = ppp_register_channel(&po->chan);
@@ -283,6 +351,7 @@ static int pppolac_release(struct socket *sock)
if (sk->sk_state != PPPOX_NONE) {
struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private;
lock_sock(sk_udp);
+ skb_queue_purge(&sk->sk_receive_queue);
pppox_unbind_sock(sk);
udp_sk(sk_udp)->encap_type = 0;
udp_sk(sk_udp)->encap_rcv = NULL;
diff --git a/drivers/net/pppopns.c b/drivers/net/pppopns.c
index 298097127c90..fb8198447938 100644
--- a/drivers/net/pppopns.c
+++ b/drivers/net/pppopns.c
@@ -16,11 +16,14 @@
/* This driver handles PPTP data packets between a RAW socket and a PPP channel.
* The socket is created in the kernel space and connected to the same address
- * of the control socket. To keep things simple, packets are always sent with
- * sequence but without acknowledgement. This driver should work on both IPv4
- * and IPv6. */
+ * of the control socket. Outgoing packets are always sent with sequences but
+ * without acknowledgements. Incoming packets with sequences are reordered
+ * within a sliding window of one second. Currently reordering only happens when
+ * a packet is received. It is done for simplicity since no additional locks or
+ * threads are required. This driver should work on both IPv4 and IPv6. */
#include <linux/module.h>
+#include <linux/jiffies.h>
#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/file.h>
@@ -52,21 +55,35 @@ struct header {
__u32 sequence;
} __attribute__((packed));
+struct meta {
+ __u32 sequence;
+ __u32 timestamp;
+};
+
+static inline struct meta *skb_meta(struct sk_buff *skb)
+{
+ return (struct meta *)skb->cb;
+}
+
+/******************************************************************************/
+
static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb)
{
struct sock *sk = (struct sock *)sk_raw->sk_user_data;
struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns;
+ struct meta *meta = skb_meta(skb);
+ __u32 now = jiffies;
struct header *hdr;
/* Skip transport header */
skb_pull(skb, skb_transport_header(skb) - skb->data);
- /* Drop the packet if it is too short. */
+ /* Drop the packet if GRE header is missing. */
if (skb->len < GRE_HEADER_SIZE)
goto drop;
+ hdr = (struct header *)skb->data;
/* Check the header. */
- hdr = (struct header *)skb->data;
if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local ||
(hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS)
goto drop;
@@ -81,6 +98,13 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb)
if (skb->len != ntohs(hdr->length))
goto drop;
+ /* Check the sequence if it is present. */
+ if (hdr->bits & PPTP_GRE_SEQ_BIT) {
+ meta->sequence = ntohl(hdr->sequence);
+ if ((__s32)(meta->sequence - opt->recv_sequence) < 0)
+ goto drop;
+ }
+
/* Skip PPP address and control if they are present. */
if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
skb->data[1] == PPP_CTRL)
@@ -90,7 +114,53 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb)
if (skb->len >= 1 && skb->data[0] & 1)
skb_push(skb, 1)[0] = 0;
- /* Finally, deliver the packet to PPP channel. */
+ /* Drop the packet if PPP protocol is missing. */
+ if (skb->len < 2)
+ goto drop;
+
+ /* Perform reordering if sequencing is enabled. */
+ if (hdr->bits & PPTP_GRE_SEQ_BIT) {
+ struct sk_buff *skb1;
+
+ /* Insert the packet into receive queue in order. */
+ skb_set_owner_r(skb, sk);
+ skb_queue_walk(&sk->sk_receive_queue, skb1) {
+ struct meta *meta1 = skb_meta(skb1);
+ __s32 order = meta->sequence - meta1->sequence;
+ if (order == 0)
+ goto drop;
+ if (order < 0) {
+ meta->timestamp = meta1->timestamp;
+ skb_insert(skb1, skb, &sk->sk_receive_queue);
+ skb = NULL;
+ break;
+ }
+ }
+ if (skb) {
+ meta->timestamp = now;
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+ }
+
+ /* Remove packets from receive queue as long as
+ * 1. the receive buffer is full,
+ * 2. they are queued longer than one second, or
+ * 3. there are no missing packets before them. */
+ skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
+ meta = skb_meta(skb);
+ if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
+ now - meta->timestamp < HZ &&
+ meta->sequence != opt->recv_sequence)
+ break;
+ skb_unlink(skb, &sk->sk_receive_queue);
+ opt->recv_sequence = meta->sequence + 1;
+ skb_orphan(skb);
+ ppp_input(&pppox_sk(sk)->chan, skb);
+ }
+ return NET_RX_SUCCESS;
+ }
+
+ /* Flush receive queue if sequencing is disabled. */
+ skb_queue_purge(&sk->sk_receive_queue);
skb_orphan(skb);
ppp_input(&pppox_sk(sk)->chan, skb);
return NET_RX_SUCCESS;
@@ -151,8 +221,8 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb)
hdr->type = PPTP_GRE_TYPE;
hdr->length = htons(length);
hdr->call = opt->remote;
- hdr->sequence = htonl(opt->sequence);
- opt->sequence++;
+ hdr->sequence = htonl(opt->xmit_sequence);
+ opt->xmit_sequence++;
/* Now send the packet via the delivery queue. */
skb_set_owner_w(skb, sk_raw);
@@ -261,6 +331,7 @@ static int pppopns_release(struct socket *sock)
if (sk->sk_state != PPPOX_NONE) {
struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private;
lock_sock(sk_raw);
+ skb_queue_purge(&sk->sk_receive_queue);
pppox_unbind_sock(sk);
sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready;
sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv;
diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h
index 263de04a8f96..c2bfc376f5b6 100644
--- a/drivers/net/wireless/bcm4329/dhd.h
+++ b/drivers/net/wireless/bcm4329/dhd.h
@@ -455,4 +455,9 @@ extern char nv_path[MOD_PARAM_PATHLEN];
extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar);
extern void dhd_wait_event_wakeup(dhd_pub_t*dhd);
+/* dhd_commn arp offload wrapers */
+extern void dhd_arp_cleanup(dhd_pub_t *dhd);
+int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen);
+void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr);
+
#endif /* _dhd_h_ */
diff --git a/drivers/net/wireless/bcm4329/dhd_common.c b/drivers/net/wireless/bcm4329/dhd_common.c
index 8fcb95fde827..e50da1414c9e 100644
--- a/drivers/net/wireless/bcm4329/dhd_common.c
+++ b/drivers/net/wireless/bcm4329/dhd_common.c
@@ -21,7 +21,7 @@
* software in any way with any other Broadcom software provided under a license
* other than the GPL, without Broadcom's express prior written consent.
*
- * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011/02/11 21:16:02 Exp $
+ * $Id: dhd_common.c,v 1.5.6.8.2.6.6.69.4.25 2011-02-11 21:16:02 Exp $
*/
#include <typedefs.h>
#include <osl.h>
@@ -1220,6 +1220,82 @@ dhd_arp_offload_enable(dhd_pub_t * dhd, int arp_enable)
}
#endif
+
+void dhd_arp_cleanup(dhd_pub_t *dhd)
+{
+#ifdef ARP_OFFLOAD_SUPPORT
+ int ret = 0;
+ int iov_len = 0;
+ char iovbuf[128];
+
+ if (dhd == NULL) return;
+
+ dhd_os_proto_block(dhd);
+
+ iov_len = bcm_mkiovar("arp_hostip_clear", 0, 0, iovbuf, sizeof(iovbuf));
+ if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0)
+ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+
+ iov_len = bcm_mkiovar("arp_table_clear", 0, 0, iovbuf, sizeof(iovbuf));
+ if ((ret = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len)) < 0)
+ DHD_ERROR(("%s failed code %d\n", __FUNCTION__, ret));
+
+ dhd_os_proto_unblock(dhd);
+
+#endif /* ARP_OFFLOAD_SUPPORT */
+}
+
+void dhd_arp_offload_add_ip(dhd_pub_t *dhd, u32 ipaddr)
+{
+#ifdef ARP_OFFLOAD_SUPPORT
+ int iov_len = 0;
+ char iovbuf[32];
+ int retcode;
+
+ dhd_os_proto_block(dhd);
+
+ iov_len = bcm_mkiovar("arp_hostip", (char *)&ipaddr, 4, iovbuf, sizeof(iovbuf));
+ retcode = dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, iov_len);
+
+ dhd_os_proto_unblock(dhd);
+
+ if (retcode)
+ DHD_TRACE(("%s: ARP ip addr add failed, retcode = %d\n",
+ __FUNCTION__, retcode));
+ else
+ DHD_TRACE(("%s: ARP ipaddr entry added\n",
+ __FUNCTION__));
+#endif /* ARP_OFFLOAD_SUPPORT */
+}
+
+
+int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen)
+{
+#ifdef ARP_OFFLOAD_SUPPORT
+ int retcode;
+ int iov_len = 0;
+
+ if (!buf)
+ return -1;
+
+ dhd_os_proto_block(dhd);
+
+ iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen);
+ retcode = dhdcdc_query_ioctl(dhd, 0, WLC_GET_VAR, buf, buflen);
+
+ dhd_os_proto_unblock(dhd);
+
+ if (retcode) {
+ DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n",
+ __FUNCTION__, retcode));
+
+ return -1;
+ }
+#endif /* ARP_OFFLOAD_SUPPORT */
+ return 0;
+}
+
+
int
dhd_preinit_ioctls(dhd_pub_t *dhd)
{
diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c
index 1c4f1a383afa..b77c1a59d597 100644
--- a/drivers/net/wireless/bcm4329/dhd_linux.c
+++ b/drivers/net/wireless/bcm4329/dhd_linux.c
@@ -43,6 +43,7 @@
#include <linux/ethtool.h>
#include <linux/fcntl.h>
#include <linux/fs.h>
+#include <linux/inetdevice.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
@@ -201,6 +202,12 @@ void wifi_del_dev(void)
}
#endif /* defined(CUSTOMER_HW2) && defined(CONFIG_WIFI_CONTROL_FUNC) */
+static int dhd_device_event(struct notifier_block *this, unsigned long event,
+ void *ptr);
+
+static struct notifier_block dhd_notifier = {
+ .notifier_call = dhd_device_event
+};
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP)
#include <linux/suspend.h>
@@ -2015,6 +2022,7 @@ dhd_del_if(dhd_info_t *dhd, int ifidx)
up(&dhd->sysioc_sem);
}
+
dhd_pub_t *
dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
{
@@ -2175,6 +2183,8 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen)
register_early_suspend(&dhd->early_suspend);
#endif
+ register_inetaddr_notifier(&dhd_notifier);
+
return &dhd->pub;
fail:
@@ -2335,6 +2345,48 @@ static struct net_device_ops dhd_ops_virt = {
};
#endif
+static int dhd_device_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
+ dhd_info_t *dhd;
+ dhd_pub_t *dhd_pub;
+
+ if (!ifa)
+ return NOTIFY_DONE;
+
+ dhd = *(dhd_info_t **)netdev_priv(ifa->ifa_dev->dev);
+ dhd_pub = &dhd->pub;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 31))
+ if (ifa->ifa_dev->dev->netdev_ops == &dhd_ops_pri) {
+#else
+ if (ifa->ifa_dev->dev->open == &dhd_open) {
+#endif
+ switch (event) {
+ case NETDEV_UP:
+ DHD_TRACE(("%s: [%s] Up IP: 0x%x\n",
+ __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
+
+ dhd_arp_cleanup(dhd_pub);
+ break;
+
+ case NETDEV_DOWN:
+ DHD_TRACE(("%s: [%s] Down IP: 0x%x\n",
+ __FUNCTION__, ifa->ifa_label, ifa->ifa_address));
+
+ dhd_arp_cleanup(dhd_pub);
+ break;
+
+ default:
+ DHD_TRACE(("%s: [%s] Event: %lu\n",
+ __FUNCTION__, ifa->ifa_label, event));
+ break;
+ }
+ }
+ return NOTIFY_DONE;
+}
+
int
dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
{
@@ -2408,6 +2460,7 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx)
dhd->pub.mac.octet[0], dhd->pub.mac.octet[1], dhd->pub.mac.octet[2],
dhd->pub.mac.octet[3], dhd->pub.mac.octet[4], dhd->pub.mac.octet[5]);
+
#if defined(CONFIG_WIRELESS_EXT)
#if defined(CONFIG_FIRST_SCAN)
#ifdef SOFTAP
@@ -2473,6 +2526,8 @@ dhd_detach(dhd_pub_t *dhdp)
dhd_if_t *ifp;
int i;
+ unregister_inetaddr_notifier(&dhd_notifier);
+
#if defined(CONFIG_HAS_EARLYSUSPEND)
if (dhd->early_suspend.suspend)
unregister_early_suspend(&dhd->early_suspend);
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index fc87efb681a3..15bda8329d28 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -1192,7 +1192,7 @@ static bool _tegra_dc_controller_enable(struct tegra_dc *dc)
dc->out->enable();
tegra_dc_setup_clk(dc, dc->clk);
-
+ tegra_periph_reset_assert(dc->clk);
clk_enable(dc->clk);
clk_enable(dc->emc_clk);
tegra_periph_reset_deassert(dc->clk);
diff --git a/drivers/video/tegra/dc/nvhdcp.c b/drivers/video/tegra/dc/nvhdcp.c
index f3a689559dda..69b8161119de 100644
--- a/drivers/video/tegra/dc/nvhdcp.c
+++ b/drivers/video/tegra/dc/nvhdcp.c
@@ -108,8 +108,9 @@ static inline bool nvhdcp_is_plugged(struct tegra_nvhdcp *nvhdcp)
static inline bool nvhdcp_set_plugged(struct tegra_nvhdcp *nvhdcp, bool plugged)
{
- return nvhdcp->plugged = plugged;
+ nvhdcp->plugged = plugged;
wmb();
+ return plugged;
}
static int nvhdcp_i2c_read(struct tegra_nvhdcp *nvhdcp, u8 reg,
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index 03749f41131f..a807266c05b9 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -155,19 +155,21 @@ struct pppoe_opt {
};
struct pppolac_opt {
- __u32 local;
- __u32 remote;
- __u16 sequence;
- __u8 sequencing;
- int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb);
+ __u32 local;
+ __u32 remote;
+ __u32 recv_sequence;
+ __u32 xmit_sequence;
+ atomic_t sequencing;
+ int (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb);
};
struct pppopns_opt {
- __u16 local;
- __u16 remote;
- __u32 sequence;
- void (*data_ready)(struct sock *sk_raw, int length);
- int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb);
+ __u16 local;
+ __u16 remote;
+ __u32 recv_sequence;
+ __u32 xmit_sequence;
+ void (*data_ready)(struct sock *sk_raw, int length);
+ int (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb);
};
#include <net/sock.h>
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 6b7525099e56..13fe335fe653 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -11,6 +11,7 @@
#define LINUX_MMC_CARD_H
#include <linux/mmc/core.h>
+#include <linux/mod_devicetable.h>
struct mmc_cid {
unsigned int manfid;
@@ -119,6 +120,7 @@ struct mmc_card {
/* for byte mode */
#define MMC_QUIRK_NONSTD_SDIO (1<<2) /* non-standard SDIO card attached */
/* (missing CIA registers) */
+#define MMC_QUIRK_INAND_CMD38 (1<<3) /* iNAND devices have broken CMD38 */
unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
@@ -146,6 +148,94 @@ struct mmc_card {
struct dentry *debugfs_root;
};
+/*
+ * The world is not perfect and supplies us with broken mmc/sdio devices.
+ * For at least a part of these bugs we need a work-around
+ */
+
+struct mmc_fixup {
+
+ /* CID-specific fields. */
+ const char *name;
+
+ /* Valid revision range */
+ u64 rev_start, rev_end;
+
+ unsigned int manfid;
+ unsigned short oemid;
+
+ /* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */
+ u16 cis_vendor, cis_device;
+
+ void (*vendor_fixup)(struct mmc_card *card, int data);
+ int data;
+};
+
+#define CID_MANFID_ANY (-1ul)
+#define CID_OEMID_ANY ((unsigned short) -1)
+#define CID_NAME_ANY (NULL)
+
+#define END_FIXUP { 0 }
+
+#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
+ _cis_vendor, _cis_device, \
+ _fixup, _data) \
+ { \
+ .name = (_name), \
+ .manfid = (_manfid), \
+ .oemid = (_oemid), \
+ .rev_start = (_rev_start), \
+ .rev_end = (_rev_end), \
+ .cis_vendor = (_cis_vendor), \
+ .cis_device = (_cis_device), \
+ .vendor_fixup = (_fixup), \
+ .data = (_data), \
+ }
+
+#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
+ _fixup, _data) \
+ _FIXUP_EXT(_name, _manfid, \
+ _oemid, _rev_start, _rev_end, \
+ SDIO_ANY_ID, SDIO_ANY_ID, \
+ _fixup, _data) \
+
+#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
+ MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data)
+
+#define SDIO_FIXUP(_vendor, _device, _fixup, _data) \
+ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \
+ CID_OEMID_ANY, 0, -1ull, \
+ _vendor, _device, \
+ _fixup, _data) \
+
+#define cid_rev(hwrev, fwrev, year, month) \
+ (((u64) hwrev) << 40 | \
+ ((u64) fwrev) << 32 | \
+ ((u64) year) << 16 | \
+ ((u64) month))
+
+#define cid_rev_card(card) \
+ cid_rev(card->cid.hwrev, \
+ card->cid.fwrev, \
+ card->cid.year, \
+ card->cid.month)
+
+/*
+ * This hook just adds a quirk unconditionnally
+ */
+static inline void __maybe_unused add_quirk(struct mmc_card *card, int data)
+{
+ card->quirks |= data;
+}
+
+/*
+ * This hook just removes a quirk unconditionnally
+ */
+static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
+{
+ card->quirks &= ~data;
+}
+
#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC)
#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD)
#define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO)
@@ -191,4 +281,7 @@ struct mmc_driver {
extern int mmc_register_driver(struct mmc_driver *);
extern void mmc_unregister_driver(struct mmc_driver *);
+extern void mmc_fixup_device(struct mmc_card *card,
+ const struct mmc_fixup *table);
+
#endif
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 7429033acb66..a6cd7c35958a 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -135,6 +135,7 @@ extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int);
extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *,
struct mmc_command *, int);
+extern int mmc_switch(struct mmc_card *, u8, u8, u8);
#define MMC_ERASE_ARG 0x00000000
#define MMC_SECURE_ERASE_ARG 0x80000000
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 22c912c5dde6..0b612e7dba28 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1403,7 +1403,7 @@ extern struct sk_buff **tcp4_gro_receive(struct sk_buff **head,
extern int tcp_gro_complete(struct sk_buff *skb);
extern int tcp4_gro_complete(struct sk_buff *skb);
-extern void tcp_v4_nuke_addr(__u32 saddr);
+extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr);
#ifdef CONFIG_PROC_FS
extern int tcp4_proc_init(void);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index e48a4be92899..457f5892fb2d 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1612,7 +1612,7 @@ static inline void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb)
if (conn) {
register struct hci_proto *hp;
- hci_conn_enter_active_mode(conn, 1);
+ hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active);
/* Send to upper protocol */
if ((hp = hci_proto[HCI_PROTO_L2CAP]) && hp->recv_acldata) {
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 8b7dfaf3b6ea..ac582d17bf40 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -815,8 +815,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
}
break;
case SIOCKILLADDR: /* Nuke all connections on this address */
- ret = 0;
- tcp_v4_nuke_addr(sin->sin_addr.s_addr);
+ ret = tcp_nuke_addr(net, (struct sockaddr *) sin);
break;
}
done:
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 1b171b5dcf3c..886721da4888 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -272,6 +272,7 @@
#include <net/tcp.h>
#include <net/xfrm.h>
#include <net/ip.h>
+#include <net/ip6_route.h>
#include <net/netdma.h>
#include <net/sock.h>
@@ -3320,3 +3321,99 @@ void __init tcp_init(void)
tcp_secret_retiring = &tcp_secret_two;
tcp_secret_secondary = &tcp_secret_two;
}
+
+static int tcp_is_local(struct net *net, __be32 addr) {
+ struct rtable *rt;
+ struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
+ if (ip_route_output_key(net, &rt, &fl) || !rt)
+ return 0;
+ return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK);
+}
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static int tcp_is_local6(struct net *net, struct in6_addr *addr) {
+ struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0);
+ return rt6 && rt6->rt6i_dev && (rt6->rt6i_dev->flags & IFF_LOOPBACK);
+}
+#endif
+
+/*
+ * tcp_nuke_addr - destroy all sockets on the given local address
+ * if local address is the unspecified address (0.0.0.0 or ::), destroy all
+ * sockets with local addresses that are not configured.
+ */
+int tcp_nuke_addr(struct net *net, struct sockaddr *addr)
+{
+ int family = addr->sa_family;
+ unsigned int bucket;
+
+ struct in_addr *in;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ struct in6_addr *in6;
+#endif
+ if (family == AF_INET) {
+ in = &((struct sockaddr_in *)addr)->sin_addr;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ } else if (family == AF_INET6) {
+ in6 = &((struct sockaddr_in6 *)addr)->sin6_addr;
+#endif
+ } else {
+ return -EAFNOSUPPORT;
+ }
+
+ for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) {
+ struct hlist_nulls_node *node;
+ struct sock *sk;
+ spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket);
+
+restart:
+ spin_lock_bh(lock);
+ sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
+ struct inet_sock *inet = inet_sk(sk);
+
+ if (family == AF_INET) {
+ __be32 s4 = inet->inet_rcv_saddr;
+ if (in->s_addr != s4 &&
+ !(in->s_addr == INADDR_ANY &&
+ !tcp_is_local(net, s4)))
+ continue;
+ }
+
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+ if (family == AF_INET6) {
+ struct in6_addr *s6;
+ if (!inet->pinet6)
+ continue;
+ s6 = &inet->pinet6->rcv_saddr;
+ if (!ipv6_addr_equal(in6, s6) &&
+ !(ipv6_addr_equal(in6, &in6addr_any) &&
+ !tcp_is_local6(net, s6)))
+ continue;
+ }
+#endif
+
+ if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
+ continue;
+ if (sock_flag(sk, SOCK_DEAD))
+ continue;
+
+ sock_hold(sk);
+ spin_unlock_bh(lock);
+
+ local_bh_disable();
+ bh_lock_sock(sk);
+ sk->sk_err = ETIMEDOUT;
+ sk->sk_error_report(sk);
+
+ tcp_done(sk);
+ bh_unlock_sock(sk);
+ local_bh_enable();
+ sock_put(sk);
+
+ goto restart;
+ }
+ spin_unlock_bh(lock);
+ }
+
+ return 0;
+}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 459eb1d24629..cb8d305cb5b4 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1967,49 +1967,6 @@ void tcp_v4_destroy_sock(struct sock *sk)
}
EXPORT_SYMBOL(tcp_v4_destroy_sock);
-/*
- * tcp_v4_nuke_addr - destroy all sockets on the given local address
- */
-void tcp_v4_nuke_addr(__u32 saddr)
-{
- unsigned int bucket;
-
- for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) {
- struct hlist_nulls_node *node;
- struct sock *sk;
- spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket);
-
-restart:
- spin_lock_bh(lock);
- sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
- struct inet_sock *inet = inet_sk(sk);
-
- if (inet->inet_rcv_saddr != saddr)
- continue;
- if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
- continue;
- if (sock_flag(sk, SOCK_DEAD))
- continue;
-
- sock_hold(sk);
- spin_unlock_bh(lock);
-
- local_bh_disable();
- bh_lock_sock(sk);
- sk->sk_err = ETIMEDOUT;
- sk->sk_error_report(sk);
-
- tcp_done(sk);
- bh_unlock_sock(sk);
- local_bh_enable();
- sock_put(sk);
-
- goto restart;
- }
- spin_unlock_bh(lock);
- }
-}
-
#ifdef CONFIG_PROC_FS
/* Proc filesystem TCP sock list dumping. */
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 2f421a4beca7..fa743e0d3207 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -489,6 +489,21 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
EXPORT_SYMBOL(inet6_getname);
+int inet6_killaddr_ioctl(struct net *net, void __user *arg) {
+ struct in6_ifreq ireq;
+ struct sockaddr_in6 sin6;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EACCES;
+
+ if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq)))
+ return -EFAULT;
+
+ sin6.sin6_family = AF_INET6;
+ ipv6_addr_copy(&sin6.sin6_addr, &ireq.ifr6_addr);
+ return tcp_nuke_addr(net, (struct sockaddr *) &sin6);
+}
+
int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
@@ -513,6 +528,8 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return addrconf_del_ifaddr(net, (void __user *) arg);
case SIOCSIFDSTADDR:
return addrconf_set_dstaddr(net, (void __user *) arg);
+ case SIOCKILLADDR:
+ return inet6_killaddr_ioctl(net, (void __user *) arg);
default:
if (!sk->sk_prot->ioctl)
return -ENOIOCTLCMD;