/* * Copyright (C) ST-Ericsson SA 2012 * * Author: Ola Lilja , * Kristoffer Karlsson * for ST-Ericsson. * * License terms: * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include "ux500_pcm.h" #include "ux500_msp_dai.h" #include "mop500_ab8500.h" #include "../codecs/ab8500-codec.h" #define TX_SLOT_MONO 0x0008 #define TX_SLOT_STEREO 0x000a #define RX_SLOT_MONO 0x0001 #define RX_SLOT_STEREO 0x0003 #define TX_SLOT_8CH 0x00FF #define RX_SLOT_8CH 0x00FF #define DEF_TX_SLOTS TX_SLOT_STEREO #define DEF_RX_SLOTS RX_SLOT_MONO #define DRIVERMODE_NORMAL 0 #define DRIVERMODE_CODEC_ONLY 1 /* Slot configuration */ static unsigned int tx_slots = DEF_TX_SLOTS; static unsigned int rx_slots = DEF_RX_SLOTS; /* Configuration consistency parameters */ static DEFINE_MUTEX(mop500_ab8500_params_lock); static unsigned long mop500_ab8500_usage; static int mop500_ab8500_rate; static int mop500_ab8500_channels; /* Clocks */ static const char * const enum_mclk[] = { "SYSCLK", "ULPCLK" }; enum mclk { MCLK_SYSCLK, MCLK_ULPCLK, }; static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_mclk, enum_mclk); /* Private data for machine-part MOP500<->AB8500 */ struct mop500_ab8500_drvdata { /* Clocks */ enum mclk mclk_sel; struct clk *clk_ptr_intclk; struct clk *clk_ptr_sysclk; struct clk *clk_ptr_ulpclk; }; static inline const char *get_mclk_str(enum mclk mclk_sel) { switch (mclk_sel) { case MCLK_SYSCLK: return "SYSCLK"; case MCLK_ULPCLK: return "ULPCLK"; default: return "Unknown"; } } static int mop500_ab8500_set_mclk(struct device *dev, struct mop500_ab8500_drvdata *drvdata) { int status; struct clk *clk_ptr; if (IS_ERR(drvdata->clk_ptr_intclk)) { dev_err(dev, "%s: ERROR: intclk not initialized!\n", __func__); return -EIO; } switch (drvdata->mclk_sel) { case MCLK_SYSCLK: clk_ptr = drvdata->clk_ptr_sysclk; break; case MCLK_ULPCLK: clk_ptr = drvdata->clk_ptr_ulpclk; break; default: return -EINVAL; } if (IS_ERR(clk_ptr)) { dev_err(dev, "%s: ERROR: %s not initialized!\n", __func__, get_mclk_str(drvdata->mclk_sel)); return -EIO; } status = clk_set_parent(drvdata->clk_ptr_intclk, clk_ptr); if (status) dev_err(dev, "%s: ERROR: Setting intclk parent to %s failed (ret = %d)!", __func__, get_mclk_str(drvdata->mclk_sel), status); else dev_dbg(dev, "%s: intclk parent changed to %s.\n", __func__, get_mclk_str(drvdata->mclk_sel)); return status; } /* * Control-events */ static int mclk_input_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card); ucontrol->value.enumerated.item[0] = drvdata->mclk_sel; return 0; } static int mclk_input_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card); unsigned int val = ucontrol->value.enumerated.item[0]; if (val > (unsigned int)MCLK_ULPCLK) return -EINVAL; if (drvdata->mclk_sel == val) return 0; drvdata->mclk_sel = val; return 1; } /* * Controls */ static struct snd_kcontrol_new mop500_ab8500_ctrls[] = { SOC_ENUM_EXT("Master Clock Select", soc_enum_mclk, mclk_input_control_get, mclk_input_control_put), SOC_DAPM_PIN_SWITCH("Headset Left"), SOC_DAPM_PIN_SWITCH("Headset Right"), SOC_DAPM_PIN_SWITCH("Earpiece"), SOC_DAPM_PIN_SWITCH("Speaker Left"), SOC_DAPM_PIN_SWITCH("Speaker Right"), SOC_DAPM_PIN_SWITCH("LineOut Left"), SOC_DAPM_PIN_SWITCH("LineOut Right"), SOC_DAPM_PIN_SWITCH("Vibra 1"), SOC_DAPM_PIN_SWITCH("Vibra 2"), SOC_DAPM_PIN_SWITCH("Mic 1"), SOC_DAPM_PIN_SWITCH("Mic 2"), SOC_DAPM_PIN_SWITCH("LineIn Left"), SOC_DAPM_PIN_SWITCH("LineIn Right"), SOC_DAPM_PIN_SWITCH("DMic 1"), SOC_DAPM_PIN_SWITCH("DMic 2"), SOC_DAPM_PIN_SWITCH("DMic 3"), SOC_DAPM_PIN_SWITCH("DMic 4"), SOC_DAPM_PIN_SWITCH("DMic 5"), SOC_DAPM_PIN_SWITCH("DMic 6"), }; /* ASoC */ static int mop500_ab8500_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; /* Set audio-clock source */ return mop500_ab8500_set_mclk(rtd->card->dev, snd_soc_card_get_drvdata(rtd->card)); } static void mop500_ab8500_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct device *dev = rtd->card->dev; dev_dbg(dev, "%s: Enter\n", __func__); /* Reset slots configuration to default(s) */ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) tx_slots = DEF_TX_SLOTS; else rx_slots = DEF_RX_SLOTS; } static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct device *dev = rtd->card->dev; unsigned int fmt; int channels, ret = 0, driver_mode, slots; unsigned int sw_codec, sw_cpu; bool is_playback; dev_dbg(dev, "%s: Enter\n", __func__); dev_dbg(dev, "%s: substream->pcm->name = %s\n" "substream->pcm->id = %s.\n" "substream->name = %s.\n" "substream->number = %d.\n", __func__, substream->pcm->name, substream->pcm->id, substream->name, substream->number); /* Ensure configuration consistency between DAIs */ mutex_lock(&mop500_ab8500_params_lock); if (mop500_ab8500_usage) { if (mop500_ab8500_rate != params_rate(params) || mop500_ab8500_channels != params_channels(params)) { mutex_unlock(&mop500_ab8500_params_lock); return -EBUSY; } } else { mop500_ab8500_rate = params_rate(params); mop500_ab8500_channels = params_channels(params); } __set_bit(cpu_dai->id, &mop500_ab8500_usage); mutex_unlock(&mop500_ab8500_params_lock); channels = params_channels(params); switch (params_format(params)) { case SNDRV_PCM_FORMAT_S32_LE: sw_cpu = 32; break; case SNDRV_PCM_FORMAT_S16_LE: sw_cpu = 16; break; default: return -EINVAL; } /* Setup codec depending on driver-mode */ if (channels == 8) driver_mode = DRIVERMODE_CODEC_ONLY; else driver_mode = DRIVERMODE_NORMAL; dev_dbg(dev, "%s: Driver-mode: %s.\n", __func__, (driver_mode == DRIVERMODE_NORMAL) ? "NORMAL" : "CODEC_ONLY"); /* Setup format */ if (driver_mode == DRIVERMODE_NORMAL) { fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CONT; } else { fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_GATED; } ret = snd_soc_runtime_set_dai_fmt(rtd, fmt); if (ret) return ret; /* Setup TDM-slots */ is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); switch (channels) { case 1: slots = 16; tx_slots = (is_playback) ? TX_SLOT_MONO : 0; rx_slots = (is_playback) ? 0 : RX_SLOT_MONO; break; case 2: slots = 16; tx_slots = (is_playback) ? TX_SLOT_STEREO : 0; rx_slots = (is_playback) ? 0 : RX_SLOT_STEREO; break; case 8: slots = 16; tx_slots = (is_playback) ? TX_SLOT_8CH : 0; rx_slots = (is_playback) ? 0 : RX_SLOT_8CH; break; default: return -EINVAL; } if (driver_mode == DRIVERMODE_NORMAL) sw_codec = sw_cpu; else sw_codec = 20; dev_dbg(dev, "%s: CPU-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__, tx_slots, rx_slots); ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_slots, rx_slots, slots, sw_cpu); if (ret) return ret; dev_dbg(dev, "%s: CODEC-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__, tx_slots, rx_slots); ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_slots, rx_slots, slots, sw_codec); if (ret) return ret; return 0; } static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock(&mop500_ab8500_params_lock); __clear_bit(cpu_dai->id, &mop500_ab8500_usage); mutex_unlock(&mop500_ab8500_params_lock); return 0; } struct snd_soc_ops mop500_ab8500_ops[] = { { .hw_params = mop500_ab8500_hw_params, .hw_free = mop500_ab8500_hw_free, .startup = mop500_ab8500_startup, .shutdown = mop500_ab8500_shutdown, } }; int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dapm_context *dapm = &rtd->card->dapm; struct device *dev = rtd->card->dev; struct mop500_ab8500_drvdata *drvdata; int ret; dev_dbg(dev, "%s Enter.\n", __func__); /* Create driver private-data struct */ drvdata = devm_kzalloc(dev, sizeof(struct mop500_ab8500_drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; snd_soc_card_set_drvdata(rtd->card, drvdata); /* Setup clocks */ drvdata->clk_ptr_sysclk = clk_get(dev, "sysclk"); if (IS_ERR(drvdata->clk_ptr_sysclk)) dev_warn(dev, "%s: WARNING: clk_get failed for 'sysclk'!\n", __func__); drvdata->clk_ptr_ulpclk = clk_get(dev, "ulpclk"); if (IS_ERR(drvdata->clk_ptr_ulpclk)) dev_warn(dev, "%s: WARNING: clk_get failed for 'ulpclk'!\n", __func__); drvdata->clk_ptr_intclk = clk_get(dev, "intclk"); if (IS_ERR(drvdata->clk_ptr_intclk)) dev_warn(dev, "%s: WARNING: clk_get failed for 'intclk'!\n", __func__); /* Set intclk default parent to ulpclk */ drvdata->mclk_sel = MCLK_ULPCLK; ret = mop500_ab8500_set_mclk(dev, drvdata); if (ret < 0) dev_warn(dev, "%s: WARNING: mop500_ab8500_set_mclk!\n", __func__); drvdata->mclk_sel = MCLK_ULPCLK; /* Add controls */ ret = snd_soc_add_card_controls(rtd->card, mop500_ab8500_ctrls, ARRAY_SIZE(mop500_ab8500_ctrls)); if (ret < 0) { pr_err("%s: Failed to add machine-controls (%d)!\n", __func__, ret); return ret; } ret = snd_soc_dapm_disable_pin(dapm, "Earpiece"); ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Left"); ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Right"); ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Left"); ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Right"); ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 1"); ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 2"); ret |= snd_soc_dapm_disable_pin(dapm, "Mic 1"); ret |= snd_soc_dapm_disable_pin(dapm, "Mic 2"); ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Left"); ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Right"); ret |= snd_soc_dapm_disable_pin(dapm, "DMic 1"); ret |= snd_soc_dapm_disable_pin(dapm, "DMic 2"); ret |= snd_soc_dapm_disable_pin(dapm, "DMic 3"); ret |= snd_soc_dapm_disable_pin(dapm, "DMic 4"); ret |= snd_soc_dapm_disable_pin(dapm, "DMic 5"); ret |= snd_soc_dapm_disable_pin(dapm, "DMic 6"); return ret; } void mop500_ab8500_remove(struct snd_soc_card *card) { struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card); if (drvdata->clk_ptr_sysclk != NULL) clk_put(drvdata->clk_ptr_sysclk); if (drvdata->clk_ptr_ulpclk != NULL) clk_put(drvdata->clk_ptr_ulpclk); if (drvdata->clk_ptr_intclk != NULL) clk_put(drvdata->clk_ptr_intclk); snd_soc_card_set_drvdata(card, drvdata); } warnings. More lines of code were moved than required to keep kernel-doc comments uniform. Signed-off-by: Tobin C. Harding <me@tobin.cc> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10net/act_pedit: Introduce 'add' operationAmir Vadai1-4/+26 This command could be useful to inc/dec fields. For example, to forward any TCP packet and decrease its TTL: $ tc filter add dev enp0s9 protocol ip parent ffff: \ flower ip_proto tcp \ action pedit munge ip ttl add 0xff pipe \ action mirred egress redirect dev veth0 In the example above, adding 0xff to this u8 field is actually decreasing it by one, since the operation is masked. Signed-off-by: Amir Vadai <amir@vadai.me> Reviewed-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10net/act_pedit: Support using offset relative to the conventional network headersAmir Vadai1-16/+180 Extend pedit to enable the user setting offset relative to network headers. This change would enable to work with more complex header schemes (vs the simple IPv4 case) where setting a fixed offset relative to the network header is not enough. After this patch, the action has information about the exact header type and field inside this header. This information could be used later on for hardware offloading of pedit. Backward compatibility was being kept: 1. Old kernel <-> new userspace 2. New kernel <-> old userspace 3. add rule using new userspace <-> dump using old userspace 4. add rule using old userspace <-> dump using new userspace When using the extended api, new netlink attributes are being used. This way, operation will fail in (1) and (3) - and no malformed rule be added or dumped. Of course, new user space that doesn't need the new functionality can use the old netlink attributes and operation will succeed. Since action can support both api's, (2) should work, and it is easy to write the new user space to have (4) work. The action is having a strict check that only header types and commands it can handle are accepted. This way future additions will be much easier. Usage example: $ tc filter add dev enp0s9 protocol ip parent ffff: \ flower \ ip_proto tcp \ dst_port 80 \ action pedit munge tcp dport set 8080 pipe \ action mirred egress redirect dev veth0 Will forward tcp port whose original dest port is 80, while modifying the destination port to 8080. Signed-off-by: Amir Vadai <amir@vadai.me> Reviewed-by: Or Gerlitz <ogerlitz@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10switchdev: bridge: Offload mc router portsNogah Frankel1-0/+15 Offload the mc router ports list, whenever it is being changed. It is done because in some cases mc packets needs to be flooded to all the ports in this list. Signed-off-by: Nogah Frankel <nogahf@mellanox.com> Signed-off-by: Yotam Gigi <yotamg@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Ivan Vecera <ivecera@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10bridge: mcast: Merge the mc router ports deletions to one functionNogah Frankel1-15/+9 There are three places where a port gets deleted from the mc router port list. This patch join the actual deletion to one function. It will be helpful for later patch that will offload changes in the mc router ports list. Signed-off-by: Nogah Frankel <nogahf@mellanox.com> Signed-off-by: Yotam Gigi <yotamg@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Ivan Vecera <ivecera@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10switchdev: bridge: Offload multicast disabledNogah Frankel1-0/+16 Offload multicast disabled flag, for more accurate mc flood behavior: When it is on, the mdb should be ignored. When it is off, unregistered mc packets should be flooded to mc router ports. Signed-off-by: Nogah Frankel <nogahf@mellanox.com> Signed-off-by: Yotam Gigi <yotamg@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Ivan Vecera <ivecera@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10sched: check negative err value to safe one level of indentJiri Pirko1-13/+9 As it is more common, check err for !0. That allows to safe one level of indentation and makes the code easier to read. Also, make 'next' variable global in function as it is used twice. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10sched: add missing curly braces in else branch in tc_ctl_tfilterJiri Pirko1-1/+2 Curly braces need to be there, for stylistic reasons. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10sched: move err set right before goto errout in tc_ctl_tfilterJiri Pirko1-10/+19 This makes the reader to know right away what is the error value. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10sched: push TC filter protocol creation into a separate functionJiri Pirko1-51/+59 Make the long function tc_ctl_tfilter a little bit shorter and easier to read. Also make the creation of filter proto symmetric to destruction. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10sched: move tcf_proto_destroy and tcf_destroy_chain helpers into cls_apiJiri Pirko13-24/+32 Creation is done in this file, move destruction to be at the same place. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10sched: rename tcf_destroy to tcf_destroy_protoJiri Pirko2-6/+6 This function destroys TC filter protocol, not TC filter. So name it accordingly. Signed-off-by: Jiri Pirko <jiri@mellanox.com> Acked-by: Jamal Hadi Salim <jhs@mojatatu.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10ipv4: fib: Add events for FIB replace and appendIdo Schimmel1-13/+14 The FIB notification chain currently uses the NLM_F_{REPLACE,APPEND} flags to signal routes being replaced or appended. Instead of using netlink flags for in-kernel notifications we can simply introduce two new events in the FIB notification chain. This has the added advantage of making the API cleaner, thereby making it clear that these events should be supported by listeners of the notification chain. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> CC: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10ipv4: fib: Send notification before deleting FIB aliasIdo Schimmel1-7/+7 When a FIB alias is replaced following NLM_F_REPLACE, the ENTRY_ADD notification is sent after the reference on the previous FIB info was dropped. This is problematic as potential listeners might need to access it in their notification blocks. Solve this by sending the notification prior to the deletion of the replaced FIB alias. This is consistent with ENTRY_DEL notifications. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> CC: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10ipv4: fib: Send deletion notification with actual FIB alias typeIdo Schimmel1-2/+2 When a FIB alias is removed, a notification is sent using the type passed from user space - can be RTN_UNSPEC - instead of the actual type of the removed alias. This is problematic for listeners of the FIB notification chain, as several FIB aliases can exist with matching parameters, but the type. Solve this by passing the actual type of the removed FIB alias. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> CC: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-10ipv4: fib: Only flush FIB aliases belonging to currently flushed tableIdo Schimmel1-1/+2 In case the MAIN table is flushed and its trie is shared with the LOCAL table, then we might be flushing FIB aliases belonging to the latter. This can lead to FIB_ENTRY_DEL notifications sent with the wrong table ID. The above doesn't affect current listeners, as the table ID is ignored during entry deletion, but this will change later in the patchset. When flushing a particular table, skip any aliases belonging to a different one. Signed-off-by: Ido Schimmel <idosch@mellanox.com> Signed-off-by: Jiri Pirko <jiri@mellanox.com> CC: Alexander Duyck <alexander.h.duyck@intel.com> CC: Patrick McHardy <kaber@trash.net> Reviewed-by: Alexander Duyck <alexander.h.duyck@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-09openvswitch: Pack struct sw_flow_key.Jarno Rajahalme4-34/+39 struct sw_flow_key has two 16-bit holes. Move the most matched conntrack match fields there. In some typical cases this reduces the size of the key that needs to be hashed into half and into one cache line. Signed-off-by: Jarno Rajahalme <jarno@ovn.org> Acked-by: Joe Stringer <joe@ovn.org> Acked-by: Pravin B Shelar <pshelar@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-09openvswitch: Add force commit.Jarno Rajahalme1-2/+24 Stateful network admission policy may allow connections to one direction and reject connections initiated in the other direction. After policy change it is possible that for a new connection an overlapping conntrack entry already exists, where the original direction of the existing connection is opposed to the new connection's initial packet. Most importantly, conntrack state relating to the current packet gets the "reply" designation based on whether the original direction tuple or the reply direction tuple matched. If this "directionality" is wrong w.r.t. to the stateful network admission policy it may happen that packets in neither direction are correctly admitted. This patch adds a new "force commit" option to the OVS conntrack action that checks the original direction of an existing conntrack entry. If that direction is opposed to the current packet, the existing conntrack entry is deleted and a new one is subsequently created in the correct direction. Signed-off-by: Jarno Rajahalme <jarno@ovn.org> Acked-by: Pravin B Shelar <pshelar@ovn.org> Acked-by: Joe Stringer <joe@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-09openvswitch: Add original direction conntrack tuple to sw_flow_key.Jarno Rajahalme