* RE: [PATCH v1 net 1/1] net/sched: sch_dualpi2: fix limit/memlimit enforcement when dequeueing L-queue
From: Chia-Yu Chang (Nokia) @ 2026-04-16 16:36 UTC (permalink / raw)
To: Victor Nogueira, linux-hardening@vger.kernel.org, kees@kernel.org,
gustavoars@kernel.org, jhs@mojatatu.com, jiri@resnulli.us,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, horms@kernel.org, ij@kernel.org,
ncardwell@google.com, Koen De Schepper (Nokia),
g.white@cablelabs.com, ingemar.s.johansson@ericsson.com,
mirja.kuehlewind@ericsson.com, cheshire@apple.com, rs.ietf@gmx.at,
Jason_Livingood@comcast.com, vidhi_goel@apple.com
In-Reply-To: <8070149c-87c8-4d9c-ae12-6b9a956fb763@mojatatu.com>
> -----Original Message-----
> From: Victor Nogueira <victor@mojatatu.com>
> Sent: Thursday, April 16, 2026 4:27 PM
> To: Chia-Yu Chang (Nokia) <chia-yu.chang@nokia-bell-labs.com>; linux-hardening@vger.kernel.org; kees@kernel.org; gustavoars@kernel.org; jhs@mojatatu.com; jiri@resnulli.us; davem@davemloft.net; edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; linux-kernel@vger.kernel.org; netdev@vger.kernel.org; horms@kernel.org; ij@kernel.org; ncardwell@google.com; Koen De Schepper (Nokia) <koen.de_schepper@nokia-bell-labs.com>; g.white@cablelabs.com; ingemar.s.johansson@ericsson.com; mirja.kuehlewind@ericsson.com; cheshire@apple.com; rs.ietf@gmx.at; Jason_Livingood@comcast.com; vidhi_goel@apple.com
> Subject: Re: [PATCH v1 net 1/1] net/sched: sch_dualpi2: fix limit/memlimit enforcement when dequeueing L-queue
>
>
> CAUTION: This is an external email. Please be very careful when clicking links or opening attachments. See the URL nok.it/ext for additional information.
>
>
>
> On 13/04/2026 13:37, chia-yu.chang@nokia-bell-labs.com wrote:
> > From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
> >
> > Fix dualpi2_change() to correctly enforce updated limit and memlimit
> > values after a configuration change of the dualpi2 qdisc.
> >
> > Before this patch, dualpi2_change() always attempted to dequeue
> > packets via the root qdisc (C-queue) when reducing backlog or memory
> > usage, and unconditionally assumed that a valid skb will be returned.
> > When traffic classification results in packets being queued in the
> > L-queue while the C-queue is empty, this leads to a NULL skb
> > dereference during limit or memlimit enforcement.
> >
> > This is fixed by first dequeuing from the C-queue path if it is non-empty.
> > Once the C-queue is empty, packets are dequeued directly from the
> > L-queue.s Return values from qdisc_dequeue_internal() are checked for
> > both queues. When dequeuing from the L-queue, the parent qdisc qlen
> > and backlog counters are updated explicitly to keep overall qdisc statistics consistent.
> > [...]
> > ---
> > net/sched/sch_dualpi2.c | 24 +++++++++++++++++++-----
> > 1 file changed, 19 insertions(+), 5 deletions(-)
> >
> > diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c index
> > 6d7e6389758d..56d4422970b6 100644
> > --- a/net/sched/sch_dualpi2.c
> > +++ b/net/sched/sch_dualpi2.c
> > @@ -872,11 +872,25 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt,
> > old_backlog = sch->qstats.backlog;
> > while (qdisc_qlen(sch) > sch->limit ||
> > q->memory_used > q->memory_limit) {
> > - struct sk_buff *skb = qdisc_dequeue_internal(sch, true);
> > -
> > - q->memory_used -= skb->truesize;
> > - qdisc_qstats_backlog_dec(sch, skb);
> > - rtnl_qdisc_drop(skb, sch);
> > + int c_len = qdisc_qlen(sch) - qdisc_qlen(q->l_queue);
> > + struct sk_buff *skb = NULL;
> > +
> > + if (c_len) {
> > + skb = qdisc_dequeue_internal(sch, true);
> > + if (!skb)
> > + break;
> > + q->memory_used -= skb->truesize;
> > + rtnl_qdisc_drop(skb, sch);
> > + } else if (qdisc_qlen(q->l_queue)) {
> > + skb = qdisc_dequeue_internal(q->l_queue, true);
> > + if (!skb)
> > + break;
> > + q->memory_used -= skb->truesize;
> > + rtnl_qdisc_drop(skb, q->l_queue);
> > + /* Keep the overall qdisc stats consistent */
> > + --sch->q.qlen;
> > + qdisc_qstats_backlog_dec(sch, skb);
>
> Sashiko is hallucinating saying this will cause a UAF, it won't.
> However it is good to maintain a consistent order here.
> For example, see how sch_choke is doing [1].
>
> [1]
> https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/tree/net/sched/sch_choke.c?id=1f5ffc672165ff851063a5fd044b727ab2517ae3#n394
>
> cheers,
> Victor
Hi Victor,
Thanks for the pointer to sch_choke, it follows the order: (1) qdisc_qstats_backlog_dec(), (2) reduce qlen, and (3) rtnl_qdisc_drop().
But I've also checked sch_codel, its order is: (1) reduce qlen, (2) qdisc_qstats_backlog_dec(), and (3) rtnl_qdisc_drop().
So, the key is to place rtnl_qdisc_drop() after the reduction of qstats_backlog as well as qlen.
Then, I will follow the same order for dualpi2 in next version:
1. qdisc_dequeue_internal(q->l_queue), including (a) --q->l_queue->q.qlen, and (2) qdisc_qstats_backlog_dec(q->l_queue)
2. --sch->q.qlen
3. qdisc_qstats_backlog_dec(sch)
4. rtnl_qdisc_drop(skb, q->l_queue), which will do "qdisc_qstats_drop(q->l_queue)"
5. qdisc_qstats_drop(sch)
Thanks,
Chia-Yu
^ permalink raw reply
* Re: [PATCH net] netrom: do some basic forms of validation on incoming frames
From: Jakub Kicinski @ 2026-04-16 16:58 UTC (permalink / raw)
To: linux-hams
Cc: Chris Maness, hugh, Greg KH, Kuniyuki Iwashima, davem, edumazet,
horms, linux-kernel, netdev, pabeni, stable, workflows, yizhe,
f6bvp, Jean-Paul Roubelat, Joerg Reuter, Andreas Koensgen,
Thomas Sailer
In-Reply-To: <CANnsUMFVg9nZnJ_He38O9Ui1YUM_Je7MGO-y_J+oW=TG3jV1bA@mail.gmail.com>
On Sun, 12 Apr 2026 05:56:50 -0700 Chris Maness wrote:
> Thanks for your work, Hugh.
Hi good folks of hamradio.
There was a blip of activity after this thread started but now core
networking maintainers are back to reviewing all the fixes themselves
again.
We would like y'all to set up a git tree so that you can handle all the
incoming AI patches, and send them out as a pull request. This is how
wifi, bluetooth etc. operate. You already have a mailing list so that's
good.
We do not have the capacity to review all the AI generated fixes, and
ignoring security fixes does not feel like an option. I'm planning to
send a PR early next week, shedding some low usage / high CVE count code
I hope you can have the tree in place by then.
^ permalink raw reply
* [PATCH v2 net 1/1] net/sched: sch_dualpi2: fix limit/memlimit enforcement when dequeueing L-queue
From: chia-yu.chang @ 2026-04-16 17:09 UTC (permalink / raw)
To: victor, hxzene, linux-hardening, kees, gustavoars, jhs, jiri,
davem, edumazet, kuba, pabeni, linux-kernel, netdev, horms, ij,
ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel
Cc: Chia-Yu Chang
From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
Fix dualpi2_change() to correctly enforce updated limit and memlimit values
after a configuration change of the dualpi2 qdisc.
Before this patch, dualpi2_change() always attempted to dequeue packets via
the root qdisc (C-queue) when reducing backlog or memory usage, and
unconditionally assumed that a valid skb will be returned. When traffic
classification results in packets being queued in the L-queue while the
C-queue is empty, this leads to a NULL skb dereference during limit or
memlimit enforcement.
This is fixed by first dequeuing from the C-queue path if it is non-empty.
Once the C-queue is empty, packets are dequeued directly from the L-queue.
Return values from qdisc_dequeue_internal() are checked for both queues. When
dequeuing from the L-queue, the parent qdisc qlen and backlog counters are
updated explicitly to keep overall qdisc statistics consistent.
Fixes: 320d031ad6e4 ("sched: Struct definition and parsing of dualpi2 qdisc")
Reported-by: "Kito Xu (veritas501)" <hxzene@gmail.com>
Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
---
net/sched/sch_dualpi2.c | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/net/sched/sch_dualpi2.c b/net/sched/sch_dualpi2.c
index fe6f5e889625..5fcec5e6e97d 100644
--- a/net/sched/sch_dualpi2.c
+++ b/net/sched/sch_dualpi2.c
@@ -868,11 +868,31 @@ static int dualpi2_change(struct Qdisc *sch, struct nlattr *opt,
old_backlog = sch->qstats.backlog;
while (qdisc_qlen(sch) > sch->limit ||
q->memory_used > q->memory_limit) {
- struct sk_buff *skb = qdisc_dequeue_internal(sch, true);
-
- q->memory_used -= skb->truesize;
- qdisc_qstats_backlog_dec(sch, skb);
- rtnl_qdisc_drop(skb, sch);
+ int c_len = qdisc_qlen(sch) - qdisc_qlen(q->l_queue);
+ struct sk_buff *skb = NULL;
+
+ if (c_len) {
+ skb = qdisc_dequeue_internal(sch, true);
+ if (!skb)
+ break;
+ q->memory_used -= skb->truesize;
+ rtnl_qdisc_drop(skb, sch);
+ } else if (qdisc_qlen(q->l_queue)) {
+ skb = qdisc_dequeue_internal(q->l_queue, true);
+ if (!skb)
+ break;
+ /* Keep the overall qdisc stats consistent */
+ --sch->q.qlen;
+ qdisc_qstats_backlog_dec(sch, skb);
+
+ q->memory_used -= skb->truesize;
+ rtnl_qdisc_drop(skb, q->l_queue);
+
+ /* After incrementing the drop counter for the L-queue
+ via rtnl_qdisc_drop(), update the parent qdisc
+ drop counter via qdisc_qstats_drop(sch) */
+ qdisc_qstats_drop(sch);
+ }
}
qdisc_tree_reduce_backlog(sch, old_qlen - qdisc_qlen(sch),
old_backlog - sch->qstats.backlog);
--
2.34.1
^ permalink raw reply related
* Path forward for NFC in the kernel
From: Jakub Kicinski @ 2026-04-16 17:10 UTC (permalink / raw)
To: Michael Thalmeier, Raymond Hackley, Michael Walle, Bongsu Jeon,
Krzysztof Kozlowski, Mark Greer
Cc: netdev
Hi folks!
We are struggling to keep up with the number of security reports and AI
generated patches in the kernel. NFC is infamous for being a huge CVE
magnet. We need someone to step up as a maintainer, create an NFC tree
and handle all the incoming submissions. Send us (or Linus if you
prefer) periodic PRs, like WiFi, Bluetooth etc. do. If that does not
happen I'm afraid we'll have to move the NFC code out of the tree,
put it up on GH or some such, and let it accumulate CVEs there..
I'm planning to send a PR to Linus to shed the unmaintained code early
next week. We need to have a maintainer established by then.
^ permalink raw reply
* Re: [PATCH net] ixgbevf: fix use-after-free in VEPA multicast source pruning
From: Simon Horman @ 2026-04-16 17:13 UTC (permalink / raw)
To: Michael Bommarito
Cc: intel-wired-lan, Tony Nguyen, Przemek Kitszel, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
netdev, stable, linux-kernel
In-Reply-To: <CAJJ9bXwQyd-cZ0h_FCNj29GZYpXyCBu444VhLGLZkf1bWYqoKQ@mail.gmail.com>
On Wed, Apr 15, 2026 at 12:30:45PM -0400, Michael Bommarito wrote:
> On Wed, Apr 15, 2026 at 12:17 PM Simon Horman <horms@kernel.org> wrote:
> > Sashiko flags a number of issues in the same function that
> > do not seem related to your patch.
> >
> > I'd suggest looking over them if you are interested in
> > follow-up work in this area.
>
> Sure, I'd be happy to keep going here if you're open to more hardening
> patches.
Speaking for myself: I'm happy to review patches that correct bugs.
I'm also happy to review patches that otherwise improve the code.
But I think the Intel people might be able to provide better guidance here.
Please be aware of the Netdev guidance on cleanups:
>
> Two Qs for you:
>
> 1. Do you want smaller patches for each or bigger method-level patches?
The general rule of thumb is one patch per problem.
Personally, I prefer small patches.
>
> 2. Anything on my list below that you would *not* want me touching?
> I'll combine with anything I can find from your Sashiko items
...
> 3. line 2769
> rule: semgrep signed-int-as-size-param-kmalloc
> match: q_vector = kzalloc(size, GFP_KERNEL) (signed size)
> status: untriaged
>
> 4. line 3452
> rule: semgrep signed-int-as-size-param-kmalloc
> match: tx_ring->tx_buffer_info = vmalloc(size) (signed size)
> status: untriaged
>
> 5. line 3530
> rule: semgrep signed-int-as-size-param-kmalloc
> match: rx_ring->rx_buffer_info = vmalloc(size) (signed size)
> status: untriaged
I didn't look closely, but: I am a little skeptical that these signed size
problems are worth fixing; while the other items on your list look worth
fixing to me.
...
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-net 1/4] ice: fix timestamp interrupt configuration for E825C
From: Mekala, SunithaX D @ 2026-04-16 17:23 UTC (permalink / raw)
To: Keller, Jacob E, Nguyen, Anthony L, Intel Wired LAN,
netdev@vger.kernel.org
Cc: Loktionov, Aleksandr, Keller, Jacob E, Miskell, Timothy
In-Reply-To: <20260408-jk-even-more-e825c-fixes-v1-1-b959da91a81f@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Jacob Keller
> Sent: Wednesday, April 8, 2026 11:47 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; netdev@vger.kernel.org
> Cc: Loktionov, Aleksandr <aleksandr.loktionov@intel.com>; Keller, Jacob E <jacob.e.keller@intel.com>; Miskell, Timothy <timothy.miskell@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-net 1/4] ice: fix timestamp interrupt configuration for E825C
>
> From: Grzegorz Nitka <grzegorz.nitka@intel.com>
>
> The E825C ice_phy_cfg_intr_eth56g() function is responsible for programming
> the PHY interrupt for a given port. This function writes to the
> PHY_REG_TS_INT_CONFIG register of the port. The register is responsible for
> configuring whether the port interrupt logic is enabled, as well as
> programming the threshold of waiting timestamps that will trigger an
> interrupt from this port.
>
> This threshold value must not be programmed to zero while the interrupt is
> enabled. Doing so puts the port in a misconfigured state where the PHY
> timestamp interrupt for the quad of connected ports will become stuck.
>
> This occurs, because a threshold of zero results in the timestamp interrupt
> status for the port becoming stuck high. The four ports in the connected
> quad have their timestamp status indicators muxed together. A new interrupt
> cannot be generated until the timestamp status indicators return low for
> all four ports.
>
> Normally, the timestamp status for a port will clear once there are fewer
> timestamps in that ports timestamp memory bank than the threshold. A
> threshold of zero makes this impossible, so the timestamp status for the
> port does not clear.
>
> The ice driver never intentionally programs the threshold to zero, indeed
> the driver always programs it to a value of 1, intending to get an
> interrupt immediately as soon as even a single packet is waiting for a
> timestamp.
>
> However, there is a subtle flaw in the programming logic in the
> ice_phy_cfg_intr_eth56g() function. Due to the way that the hardware
> handles enabling the PHY interrupt. If the threshold value is modified at
> the same time as the interrupt is enabled, the HW PHY state machine might
> enable the interrupt before the new threshold value is actually updated.
> This leaves a potential race condition caused by the hardware logic where
> a PHY timestamp interrupt might be triggered before the non-zero threshold
> is written, resulting in the PHY timestamp logic becoming stuck.
>
> Once the PHY timestamp status is stuck high, it will remain stuck even
> after attempting to reprogram the PHY block by changing its threshold or
> disabling the interrupt. Even a typical PF or CORE reset will not reset the
> particular block of the PHY that becomes stuck. Even a warm power cycle is
> not guaranteed to cause the PHY block to reset, and a cold power cycle is
> required.
>
> Prevent this by always writing the PHY_REG_TS_INT_CONFIG in two stages.
> First write the threshold value with the interrupt disabled, and only write
> the enable bit after the threshold has been programmed. When disabling the
> interrupt, leave the threshold unchanged. Additionally, re-read the
> register after writing it to guarantee that the write to the PHY has been
> flushed upon exit of the function.
>
> While we're modifying this function implementation, explicitly reject
> programming a threshold of 0 when enabling the interrupt. No caller does
> this today, but the consequences of doing so are significant. An explicit
> rejection in the code makes this clear.
>
> Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 36 +++++++++++++++++++++++++----
> 1 file changed, 32 insertions(+), 4 deletions(-)
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-net 2/4] ice: perform PHY soft reset for E825C ports at initialization
From: Mekala, SunithaX D @ 2026-04-16 17:23 UTC (permalink / raw)
To: Keller, Jacob E, Nguyen, Anthony L, Intel Wired LAN,
netdev@vger.kernel.org
Cc: Loktionov, Aleksandr, Keller, Jacob E, Miskell, Timothy
In-Reply-To: <20260408-jk-even-more-e825c-fixes-v1-2-b959da91a81f@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Jacob Keller
> Sent: Wednesday, April 8, 2026 11:47 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; netdev@vger.kernel.org
> Cc: Loktionov, Aleksandr <aleksandr.loktionov@intel.com>; Keller, Jacob E <jacob.e.keller@intel.com>; Miskell, Timothy <timothy.miskell@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-net 2/4] ice: perform PHY soft reset for E825C ports at initialization
>
> From: Grzegorz Nitka <grzegorz.nitka@intel.com>
>
> In some cases the PHY timestamp block of the E825C can become stuck. This
> is known to occur if the software writes 0 to the Tx timestamp threshold,
> and with older versions of the ice driver the threshold configuration is
> buggy and can race in such that hardware briefly operates with a zero
> threshold enabled. There are no other known ways to trigger this behavior,
> but once it occurs, the hardware is not recovered by normal reset, a driver
> reload, or even a warm power cycle of the system. A cold power cycle is
> sufficient to recover hardware, but this is extremely invasive and can
> result in significant downtime on customer deployments.
>
> The PHY for each port has a timestamping block which has its own reset
> functionality accessible by programming the PHY_REG_GLOBAL register.
> Writing to the PHY_REG_GLOBAL_SOFT_RESET_BIT triggers the hardware to
> perform a complete reset of the timestamping block of the PHY. This
> includes clearing the timestamp status for the port, clearing all
> outstanding timestamps in the memory bank, and resetting the PHY timer.
>
> The new ice_ptp_phy_soft_reset_eth56g() function toggles the
> PHY_REG_GLOBAL soft reset bit with the required delays, ensuring the
> PHY is properly reinitialized without requiring a full device reset.
> The sequence clears the reset bit, asserts it, then clears it again,
> with short waits between transitions to allow hardware stabilization.
>
> Call this function in the new ice_ptp_init_phc_e825c(), implementing the
> E825C device specific variant of the ice_ptp_init_phc(). Note that if
> ice_ptp_init_phc() fails, PTP functionality may be disabled, but the driver
> will still load to allow basic functionality to continue.
>
> This causes the clock owning PF driver to perform a PHY soft reset for
> every port during initialization. This ensures the driver begins life in a
> known functional state regardless of how it was previously programmed.
>
> This ensures that we properly reconfigure the hardware after a device reset
> or when loading the driver, even if it was previously misconfigured with an
> out-of-date or modified driver.
>
> Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
> Signed-off-by: Timothy Miskell <timothy.miskell@intel.com>
> Signed-off-by: Grzegorz Nitka <grzegorz.nitka@intel.com>
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 4 ++
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 90 ++++++++++++++++++++++++++++-
> 2 files changed, 93 insertions(+), 1 deletion(-)
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-net 3/4] ice: fix ready bitmap check for non-E822 devices
From: Mekala, SunithaX D @ 2026-04-16 17:23 UTC (permalink / raw)
To: Keller, Jacob E, Nguyen, Anthony L, Intel Wired LAN,
netdev@vger.kernel.org
Cc: Loktionov, Aleksandr, Keller, Jacob E, Miskell, Timothy
In-Reply-To: <20260408-jk-even-more-e825c-fixes-v1-3-b959da91a81f@intel.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Jacob Keller
> Sent: Wednesday, April 8, 2026 11:47 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; netdev@vger.kernel.org
> Cc: Loktionov, Aleksandr <aleksandr.loktionov@intel.com>; Keller, Jacob E <jacob.e.keller@intel.com>; Miskell, Timothy <timothy.miskell@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-net 3/4] ice: fix ready bitmap check for non-E822 devices
>
> The E800 hardware (apart from E810) has a ready bitmap for the PHY
> indicating which timestamp slots currently have an outstanding timestamp
> waiting to be read by software.
>
> This bitmap is checked in multiple places using the
> ice_get_phy_tx_tstamp_ready():
>
> * ice_ptp_process_tx_tstamp() calls it to determine which timestamps to
> attempt reading from the PHY
> * ice_ptp_tx_tstamps_pending() calls it in a loop at the end of the
> miscellaneous IRQ to check if new timestamps came in while the interrupt
> handler was executing.
> * ice_ptp_maybe_trigger_tx_interrupt() calls it in the auxiliary work task
> to trigger a software interrupt in the event that the hardware logic
> gets stuck.
>
> For E82X devices, multiple PHYs share the same block, and the parameter
> passed to the ready bitmap is a block number associated with the given
> port. For E825-C devices, the PHYs have their own independent blocks and do
> not share, so the parameter passed needs to be the port number. For E810
> devices, the ice_get_phy_tx_tstamp_ready() always returns all 1s regardless
> of what port, since this hardware does not have a ready bitmap. Finally,
> for E830 devices, each PF has its own ready bitmap accessible via register,
> and the block parameter is unused.
>
> The first call correctly uses the Tx timestamp tracker block parameter to
> check the appropriate timestamp block. This works because the tracker is
> setup correctly for each timestamp device type.
>
> The second two callers behave incorrectly for all device types other than
> the older E822 devices. They both iterate in a loop using
> ICE_GET_QUAD_NUM() which is a macro only used by E822 devices. This logic
> is incorrect for devices other than the E822 devices.
>
> For E810 the calls would always return true, causing E810 devices to always
> attempt to trigger a software interrupt even when they have no reason to.
> For E830, this results in duplicate work as the ready bitmap is checked
> once per number of quads. Finally, for E825-C, this results in the pending
> checks failing to detect timestamps on ports other than the first two.
>
> Fix this by introducing a new hardware API function to ice_ptp_hw.c,
> ice_check_phy_tx_tstamp_ready(). This function will check if any timestamps
> are available and returns a positive value if any timestamps are pending.
> For E810, the function always returns false, so that the re-trigger checks
> never happen. For E830, check the ready bitmap just once. For E82x
> hardware, check each quad. Finally, for E825-C, check every port.
>
> The interface function returns an integer to enable reporting of error code
> if the driver is unable read the ready bitmap. This enables callers to
> handle this case properly. The previous implementation assumed that
> timestamps are available if they failed to read the bitmap. This is
> problematic as it could lead to continuous software IRQ triggering if the
> PHY timestamp registers somehow become inaccessible.
>
> This change is especially important for E825-C devices, as the missing
> checks could leave a window open where a new timestamp could arrive while
> the existing timestamps aren't completed. As a result, the hardware
> threshold logic would not trigger a new interrupt. Without the check, the
> timestamp is left unhandled, and new timestamps will not cause an interrupt
> again until the timestamp is handled. Since both the interrupt check and
> the backup check in the auxiliary task do not function properly, the device
> may have Tx timestamps permanently stuck failing on a given port.
>
> The faulty checks originate from commit d938a8cca88a ("ice: Auxbus devices
> & driver for E822 TS") and commit 712e876371f8 ("ice: periodically kick Tx
> timestamp interrupt"), however at the time of the original coding, both
> functions only operated on E822 hardware. This is no longer the case, and
> hasn't been since the introduction of the ETH56G PHY model in commit
> 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
>
> Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_ptp_hw.h | 1 +
> drivers/net/ethernet/intel/ice/ice_ptp.c | 40 ++++------
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 117 ++++++++++++++++++++++++++++
> 3 files changed, 132 insertions(+), 26 deletions(-)
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH iwl-net 4/4] ice: fix ice_ptp_read_tx_hwtstamp_status_eth56g
From: Mekala, SunithaX D @ 2026-04-16 17:24 UTC (permalink / raw)
To: Keller, Jacob E, Nguyen, Anthony L, Intel Wired LAN,
netdev@vger.kernel.org
Cc: Loktionov, Aleksandr, Keller, Jacob E, Miskell, Timothy
In-Reply-To: <20260408-jk-even-more-e825c-fixes-v1-4-b959da91a81f@intel.com>
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of Jacob Keller
> Sent: Wednesday, April 8, 2026 11:47 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; netdev@vger.kernel.org
> Cc: Loktionov, Aleksandr <aleksandr.loktionov@intel.com>; Keller, Jacob E <jacob.e.keller@intel.com>; Miskell, Timothy <timothy.miskell@intel.com>
> Subject: [Intel-wired-lan] [PATCH iwl-net 4/4] ice: fix ice_ptp_read_tx_hwtstamp_status_eth56g
>
> The ice_ptp_read_tx_hwtstamp_status_eth56g function calls
> ice_read_phy_eth56g with a PHY index. However the function actually expects
> a port index. This causes the function to read the wrong PHY_PTP_INT_STATUS
> registers, and effectively makes the status wrong for the second set of
> ports from 4 to 7.
>
> The ice_read_phy_eth56g function uses the provided port index to determine
> which PHY device to read. We could refactor the entire chain to take a PHY
> index, but this would impact many code sites. Instead, multiply the PHY
> index by the number of ports, so that we read from the first port of each
> PHY.
>
> Fixes: 7cab44f1c35f ("ice: Introduce ETH56G PHY model for E825C products")
> Signed-off-by: Jacob Keller <jacob.e.keller@intel.com>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_ptp_hw.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
Tested-by: Sunitha Mekala <sunithax.d.mekala@intel.com> (A Contingent worker at Intel)
^ permalink raw reply
* Re: [PATCH net v3] net/sched: taprio: fix NULL pointer dereference in class dump
From: Weiming Shi @ 2026-04-16 17:24 UTC (permalink / raw)
To: Jamal Hadi Salim
Cc: Vinicius Costa Gomes, Jiri Pirko, David S . Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, netdev, Xiang Mei
In-Reply-To: <CAM0EoMnhH+7NA6A-mg3rvQJLwN0Wvhd6tYeOYa_YMMdYkMPZHQ@mail.gmail.com>
On 26-04-16 11:50, Jamal Hadi Salim wrote:
> On Tue, Apr 14, 2026 at 6:43 AM Weiming Shi <bestswngs@gmail.com> wrote:
> >
> > When a TAPRIO child qdisc is deleted via RTM_DELQDISC, taprio_graft()
> > is called with new == NULL and stores NULL into q->qdiscs[cl - 1].
> > Subsequent RTM_GETTCLASS dump operations walk all classes via
> > taprio_walk() and call taprio_dump_class(), which calls taprio_leaf()
> > returning the NULL pointer, then dereferences it to read child->handle,
> > causing a kernel NULL pointer dereference.
> >
> > The bug is reachable with namespace-scoped CAP_NET_ADMIN on any kernel
> > with CONFIG_NET_SCH_TAPRIO enabled. On systems with unprivileged user
> > namespaces enabled, an unprivileged local user can trigger a kernel
> > panic by creating a taprio qdisc inside a new network namespace,
> > grafting an explicit child qdisc, deleting it, and requesting a class
> > dump. The RTM_GETTCLASS dump itself requires no capability.
> >
>
> While i would say this looks good to me, I hate to sound like a broken
> record but:
>
> Do you have a reproducer?
> And it would be nice to get a tdc test from you.
>
> Also, please use assisted-by: or even suggested-by if this patch was
> suggested by AI.
>
> cheers,
> jamal
Hi Jamal,
Thanks for the review. Reproducer below -- tested on a KASAN kernel
under vng:
```
unshare -Urn
ip link add veth0 numtxqueues 8 numrxqueues 8 type veth \
peer name veth1 numtxqueues 8 numrxqueues 8
ip link set veth0 up
tc qdisc replace dev veth0 root handle 100: taprio \
num_tc 8 map 0 1 2 3 4 5 6 7 \
queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \
base-time 0 sched-entry S ff 20000000 clockid CLOCK_TAI
tc qdisc add dev veth0 parent 100:1 handle 200: pfifo
tc qdisc del dev veth0 parent 100:1 handle 200:
tc class show dev veth0
```
A C PoC is also available if needed.
I'll test and send a v4 with the tdc test case and an Assisted-by tag.
Thanks,
Weiming Shi
>
>
> > Oops: general protection fault, probably for non-canonical address 0xdffffc0000000007: 0000 [#1] SMP KASAN NOPTI
> > KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f]
> > RIP: 0010:taprio_dump_class (net/sched/sch_taprio.c:2475)
> > Call Trace:
> > <TASK>
> > tc_fill_tclass (net/sched/sch_api.c:1966)
> > qdisc_class_dump (net/sched/sch_api.c:2329)
> > taprio_walk (net/sched/sch_taprio.c:2510)
> > tc_dump_tclass_qdisc (net/sched/sch_api.c:2353)
> > tc_dump_tclass_root (net/sched/sch_api.c:2370)
> > tc_dump_tclass (net/sched/sch_api.c:2431)
> > rtnl_dumpit (net/core/rtnetlink.c:6827)
> > netlink_dump (net/netlink/af_netlink.c:2325)
> > rtnetlink_rcv_msg (net/core/rtnetlink.c:6927)
> > netlink_rcv_skb (net/netlink/af_netlink.c:2550)
> > </TASK>
> >
> > Fix this by substituting &noop_qdisc when new is NULL in
> > taprio_graft(), following the same pattern used by multiq_graft() and
> > prio_graft(). This ensures q->qdiscs[] slots are never NULL, making
> > control-plane dump paths safe without requiring individual NULL checks.
> >
> > Since the data-plane paths (taprio_enqueue and taprio_dequeue_from_txq)
> > previously had explicit NULL guards that would drop/skip the packet
> > cleanly, update those checks to test for &noop_qdisc instead. Without
> > this, packets would reach taprio_enqueue_one() which increments the root
> > qdisc's qlen and backlog before calling the child's enqueue; noop_qdisc
> > drops the packet but those counters are never rolled back, permanently
> > inflating the root qdisc's statistics.
> >
> > Fixes: 665338b2a7a0 ("net/sched: taprio: dump class stats for the actual q->qdiscs[]")
> > Reported-by: Xiang Mei <xmei5@asu.edu>
> > Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> > ---
> > v3: fix broken patch
> > v2: Also update NULL guards in taprio_enqueue() and
> > taprio_dequeue_from_txq() to avoid qlen/backlog inflation (Paolo).
> > ---
> > net/sched/sch_taprio.c | 11 +++++++----
> > 1 file changed, 7 insertions(+), 4 deletions(-)
> >
> > diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
> > index f721c03514f60..07723b156c5b3 100644
> > --- a/net/sched/sch_taprio.c
> > +++ b/net/sched/sch_taprio.c
> > @@ -634,7 +634,7 @@ static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
> > queue = skb_get_queue_mapping(skb);
> >
> > child = q->qdiscs[queue];
> > - if (unlikely(!child))
> > + if (unlikely(child == &noop_qdisc))
> > return qdisc_drop(skb, sch, to_free);
> >
> > if (taprio_skb_exceeds_queue_max_sdu(sch, skb)) {
> > @@ -717,7 +717,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,
> > int len;
> > u8 tc;
> >
> > - if (unlikely(!child))
> > + if (unlikely(child == &noop_qdisc))
> > return NULL;
> >
> > if (TXTIME_ASSIST_IS_ENABLED(q->flags))
> > @@ -2183,6 +2183,9 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
> > if (!dev_queue)
> > return -EINVAL;
> >
> > + if (!new)
> > + new = &noop_qdisc;
> > +
> > if (dev->flags & IFF_UP)
> > dev_deactivate(dev);
> >
> > @@ -2196,14 +2199,14 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
> > *old = q->qdiscs[cl - 1];
> > if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
> > WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old);
> > - if (new)
> > + if (new != &noop_qdisc)
> > qdisc_refcount_inc(new);
> > if (*old)
> > qdisc_put(*old);
> > }
> >
> > q->qdiscs[cl - 1] = new;
> > - if (new)
> > + if (new != &noop_qdisc)
> > new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
> >
> > if (dev->flags & IFF_UP)
> > --
> > 2.43.0
> >
^ permalink raw reply
* Re: [PATCH bpf v2 1/2] bpf: Reject TCP_NODELAY in TCP header option callbacks
From: Martin KaFai Lau @ 2026-04-16 17:35 UTC (permalink / raw)
To: KaFai Wan
Cc: daniel, john.fastabend, sdf, ast, andrii, eddyz87, memxor, song,
yonghong.song, jolsa, davem, edumazet, kuba, pabeni, horms, shuah,
jiayuan.chen, bpf, netdev, linux-kernel, linux-kselftest,
Quan Sun, Yinhao Hu, Kaiyan Mei
In-Reply-To: <20260416112308.1820332-2-kafai.wan@linux.dev>
On Thu, Apr 16, 2026 at 07:23:07PM +0800, KaFai Wan wrote:
> diff --git a/net/core/filter.c b/net/core/filter.c
> index fcfcb72663ca..911ff04bca5a 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -5833,6 +5833,11 @@ BPF_CALL_5(bpf_sock_ops_setsockopt, struct bpf_sock_ops_kern *, bpf_sock,
> if (!is_locked_tcp_sock_ops(bpf_sock))
> return -EOPNOTSUPP;
>
> + if ((bpf_sock->op == BPF_SOCK_OPS_HDR_OPT_LEN_CB ||
> + bpf_sock->op == BPF_SOCK_OPS_WRITE_HDR_OPT_CB) &&
> + IS_ENABLED(CONFIG_INET) && level == SOL_TCP && optname == TCP_NODELAY)
IS_ENABLED(CONFIG_INET) is unnecessary.
pw-bot: cr
> + return -EOPNOTSUPP;
> +
> return _bpf_setsockopt(bpf_sock->sk, level, optname, optval, optlen);
> }
>
> --
> 2.43.0
>
^ permalink raw reply
* Re: [PATCH net 1/1] mptcp: hold subflow request owners when cloning reqsk
From: Matthieu Baerts @ 2026-04-16 17:45 UTC (permalink / raw)
To: Ren Wei, netdev, mptcp
Cc: davem, edumazet, kuba, pabeni, horms, ncardwell, kuniyu, dsahern,
martineau, geliang, daniel, kafai, yuantan098, yifanwucs,
tomapufckgml, bird, caoruide123, enjou1224z
In-Reply-To: <86e2514b533bf4d55d4aa2fdbf1404022e8c9430.1776149210.git.caoruide123@gmail.com>
Hi Ren,
On 15/04/2026 11:31, Ren Wei wrote:
> From: Ruide Cao <caoruide123@gmail.com>
>
> TCP request migration clones pending request sockets with
> inet_reqsk_clone(). For MPTCP MP_JOIN requests this raw-copies
> subflow_req->msk, but the cloned request does not take a new reference.
>
> Both the original and the cloned request can later drop the same msk in
> subflow_req_destructor(), and a migrated request may keep a dangling msk
> pointer after the original owner has already been released.
>
> Add a request_sock clone callback and let MPTCP grab a reference for cloned
> subflow requests that carry an msk. This keeps ownership balanced across
> both successful migrations and failed clone/insert paths without changing
> other protocols.
Thank you for this patch!
Indeed, it looks like this path has not been covered by the MPTCP test
suite so far.
By chance, do you have any simpler reproducer using packetdrill? (the
MPTCP fork)
https://github.com/multipath-tcp/packetdrill
Also, I see Sashiko is pointing to a potential issue with MP_CAPABLE and
the token, also only visible with net.ipv4.tcp_migrate_req=1:
https://sashiko.dev/#/patchset/86e2514b533bf4d55d4aa2fdbf1404022e8c9430.1776149210.git.caoruide123%40gmail.com
Are you also looking at that?
> Fixes: c905dee62232 ("tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.")
> Cc: stable@kernel.org
> Reported-by: Yuan Tan <yuantan098@gmail.com>
> Reported-by: Yifan Wu <yifanwucs@gmail.com>
> Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> Reported-by: Xin Liu <bird@lzu.edu.cn>
> Signed-off-by: Ruide Cao <caoruide123@gmail.com>
> Tested-by: Ren Wei <enjou1224z@gmail.com>
> Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> ---
> include/net/request_sock.h | 2 ++
> net/ipv4/inet_connection_sock.c | 3 +++
> net/mptcp/subflow.c | 13 +++++++++++++
> 3 files changed, 18 insertions(+)
>
> diff --git a/include/net/request_sock.h b/include/net/request_sock.h
> index 5a9c826a7092..560e464c400f 100644
> --- a/include/net/request_sock.h
> +++ b/include/net/request_sock.h
> @@ -36,6 +36,8 @@ struct request_sock_ops {
> struct sk_buff *skb,
> enum sk_rst_reason reason);
> void (*destructor)(struct request_sock *req);
> + void (*init_clone)(const struct request_sock *req,
> + struct request_sock *new_req);
@TCP/INET maintainers: are you OK with this new function pointer?
Currently, MPTCP is the only user, and "req" is not used, see below.
> };
>
> struct saved_syn {
> diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
> index e961936b6be7..140a9e96ad58 100644
> --- a/net/ipv4/inet_connection_sock.c
> +++ b/net/ipv4/inet_connection_sock.c
> @@ -954,6 +954,9 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req,
> if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener)
> rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq);
(Maybe TCP with fastopen could be this other user to call
rcu_assign_pointer()? (net-next material))
> + if (req->rsk_ops->init_clone)
> + req->rsk_ops->init_clone(req, nreq);
> +
> return nreq;
> }
>
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 4ff5863aa9fd..5f4069647822 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -47,6 +47,17 @@ static void subflow_req_destructor(struct request_sock *req)
> mptcp_token_destroy_request(req);
> }
>
> +static void subflow_req_clone(const struct request_sock *req,
> + struct request_sock *new_req)
> +{
> + struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(new_req);
> +
> + (void)req;
Note: if 'req' is not used, and MPTCP is currently the only user of this
new init_clone() callback, perhaps enough to pass only 'new_req' for the
moment? 'req' can be added later if other users need it, no?
With only init_close(nreq) in a v2, or if TCP maintainers prefer to keep
it, feel free to add:
Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
^ permalink raw reply
* Re: [PATCH net v6 1/2] dpll: export __dpll_pin_change_ntf() for use under dpll_lock
From: Vadim Fedorenko @ 2026-04-16 17:50 UTC (permalink / raw)
To: Petr Oros, netdev
Cc: Ivan Vecera, Arkadiusz Kubalewski, Jiri Pirko, Tony Nguyen,
Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel,
intel-wired-lan, Aleksandr Loktionov, Rinitha S, Michal Schmidt,
Jacob Keller
In-Reply-To: <20260416113952.389405-2-poros@redhat.com>
On 16/04/2026 12:39, Petr Oros wrote:
> From: Ivan Vecera <ivecera@redhat.com>
>
> Export __dpll_pin_change_ntf() so that drivers can send pin change
> notifications from within pin callbacks, which are already called
> under dpll_lock. Using dpll_pin_change_ntf() in that context would
> deadlock.
>
> Add lockdep_assert_held() to catch misuse without the lock held.
>
> Signed-off-by: Ivan Vecera <ivecera@redhat.com>
> Signed-off-by: Petr Oros <poros@redhat.com>
Acked-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
^ permalink raw reply
* Re: [PATCH v2 net 1/1] net/sched: sch_dualpi2: fix limit/memlimit enforcement when dequeueing L-queue
From: Stephen Hemminger @ 2026-04-16 17:55 UTC (permalink / raw)
To: chia-yu.chang
Cc: victor, hxzene, linux-hardening, kees, gustavoars, jhs, jiri,
davem, edumazet, kuba, pabeni, linux-kernel, netdev, horms, ij,
ncardwell, koen.de_schepper, g.white, ingemar.s.johansson,
mirja.kuehlewind, cheshire, rs.ietf, Jason_Livingood, vidhi_goel
In-Reply-To: <20260416170906.66432-1-chia-yu.chang@nokia-bell-labs.com>
On Thu, 16 Apr 2026 19:09:06 +0200
chia-yu.chang@nokia-bell-labs.com wrote:
> From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
>
> Fix dualpi2_change() to correctly enforce updated limit and memlimit values
> after a configuration change of the dualpi2 qdisc.
>
> Before this patch, dualpi2_change() always attempted to dequeue packets via
> the root qdisc (C-queue) when reducing backlog or memory usage, and
> unconditionally assumed that a valid skb will be returned. When traffic
> classification results in packets being queued in the L-queue while the
> C-queue is empty, this leads to a NULL skb dereference during limit or
> memlimit enforcement.
>
> This is fixed by first dequeuing from the C-queue path if it is non-empty.
> Once the C-queue is empty, packets are dequeued directly from the L-queue.
> Return values from qdisc_dequeue_internal() are checked for both queues. When
> dequeuing from the L-queue, the parent qdisc qlen and backlog counters are
> updated explicitly to keep overall qdisc statistics consistent.
>
> Fixes: 320d031ad6e4 ("sched: Struct definition and parsing of dualpi2 qdisc")
> Reported-by: "Kito Xu (veritas501)" <hxzene@gmail.com>
> Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
> ---
I was a little concerned about the complexity of managing qlen here.
But could not find anything obvious.
Turned to AI review and it found some things:
Right fix direction and the reported crash is real. A few issues before this is ready:
1. The `c_len` construction is fragile. Declared `int`, initialized
from a `u32 - u32`. If the invariant `qdisc_qlen(sch) >=
qdisc_qlen(q->l_queue)` is ever violated, you get a large positive
value, the C-queue branch is taken on an empty C-queue,
`qdisc_dequeue_internal()` returns NULL, and the loop breaks out
without draining the L-queue -- leaving the qdisc over limit. Simpler
and more robust to just compare the two qlens directly and drop the
delta variable entirely.
2. Missing else/termination. If both branches' conditions are false
(neither `c_len` nor `qdisc_qlen(q->l_queue)`) but the outer `while`
still holds because `memory_used > memory_limit`, the loop spins
forever. An explicit `else break;` guards against an accounting desync
becoming a hang.
3. Whitespace: two lines in the L-queue branch use spaces instead of tabs --
+ q->memory_used -= skb->truesize;
+ rtnl_qdisc_drop(skb, q->l_queue);
checkpatch will flag this.
4. Comment style. The three-line comment at the end of the L-queue
branch doesn't follow the net subsystem multi-line comment style
(leading ' * ' on continuation lines, closing ' */' on its own line).
Once the code is cleaner, the comment could also just be dropped or
shortened to one line.
5. The accounting in the L-queue branch is correct, but only if you
trace the enqueue invariants carefully: L-queue packets are counted in
*both* `sch` and `q->l_queue` on enqueue (see dualpi2_enqueue_skb lines
413-423), `qdisc_dequeue_internal(q->l_queue, true)` adjusts l_queue's
side, and the explicit `--sch->q.qlen` + `qdisc_qstats_backlog_dec(sch,
skb)` adjusts sch's side. Separately, the C-queue branch now quietly
relies on the post-CVE-2025-39677 semantics of
`qdisc_dequeue_internal()` handling parent backlog -- which is why the
pre-patch `qdisc_qstats_backlog_dec(sch, skb)` could be removed.
Neither of these load-bearing invariants is documented in the code or
the commit message. Please add an inline comment in the L-queue branch
explaining the double-count-on-enqueue, and mention the
qdisc_dequeue_internal() dependency in the commit log.
6. Commit message / subject. Subject reads as if only the L-queue path
changed, but the whole drain loop was restructured. Something like
"sch_dualpi2: drain both C-queue and L-queue in dualpi2_change()" would
describe it better. Also, on NULL return from qdisc_dequeue_internal()
the loop silently breaks -- if that ever triggers it means qdisc_qlen()
> 0 but dequeue returned NULL, which is a real invariant violation.
> Worth a WARN_ON_ONCE().
Suggested shape:
while (qdisc_qlen(sch) > sch->limit ||
q->memory_used > q->memory_limit) {
struct sk_buff *skb;
if (qdisc_qlen(sch) > qdisc_qlen(q->l_queue)) {
skb = qdisc_dequeue_internal(sch, true);
if (!skb)
break;
q->memory_used -= skb->truesize;
rtnl_qdisc_drop(skb, sch);
} else if (qdisc_qlen(q->l_queue)) {
skb = qdisc_dequeue_internal(q->l_queue, true);
if (!skb)
break;
/* L-queue packets are counted in both sch and
* l_queue on enqueue; qdisc_dequeue_internal()
* handled l_queue, account sch here.
*/
sch->q.qlen--;
qdisc_qstats_backlog_dec(sch, skb);
q->memory_used -= skb->truesize;
rtnl_qdisc_drop(skb, q->l_queue);
qdisc_qstats_drop(sch);
} else {
break;
}
}
As with any AI feedback, expect it to generate hints but also be wrong.
^ permalink raw reply
* Re: [PATCH net] ipv6: fix possible UAF in icmpv6_rcv()
From: Joe Damato @ 2026-04-16 18:10 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
David Ahern, Ido Schimmel, netdev, eric.dumazet
In-Reply-To: <20260416103505.2380753-1-edumazet@google.com>
On Thu, Apr 16, 2026 at 10:35:05AM +0000, Eric Dumazet wrote:
> Caching saddr and daddr before pskb_pull() is problematic
> since skb->head can change.
>
> Remove these temporary variables:
>
> - We only access &ipv6_hdr(skb)->saddr and &ipv6_hdr(skb)->daddr
> when net_dbg_ratelimited() is called in the slow path.
>
> - Avoid potential future misuse after pskb_pull() call.
>
> Fixes: 4b3418fba0fe ("ipv6: icmp: include addresses in debug messages")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> ---
> net/ipv6/icmp.c | 10 ++++------
> 1 file changed, 4 insertions(+), 6 deletions(-)
Reviewed-by: Joe Damato <joe@dama.to>
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH v5 net-next 0/8] dpll/ice: Add TXC DPLL type and full TX reference clock control for E825
From: Kubalewski, Arkadiusz @ 2026-04-16 18:26 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Vecera, Ivan, vadim.fedorenko@linux.dev, edumazet@google.com,
netdev@vger.kernel.org, richardcochran@gmail.com,
donald.hunter@gmail.com, linux-kernel@vger.kernel.org,
davem@davemloft.net, Prathosh.Satish@microchip.com,
andrew+netdev@lunn.ch, intel-wired-lan@lists.osuosl.org,
horms@kernel.org, Kitszel, Przemyslaw, Nguyen, Anthony L,
pabeni@redhat.com, jiri@resnulli.us
In-Reply-To: <20260416082751.04782987@kernel.org>
>From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf Of
>Jakub Kicinski
>Sent: Thursday, April 16, 2026 5:28 PM
>
>On Wed, 15 Apr 2026 13:23:22 +0000 Kubalewski, Arkadiusz wrote:
>> >> Well, the true is that we did not anticipated per-port control of the
>> >> TX clock source, as a single DPLL device could drive multiple of
>> >> such.
>> >>
>> >> This is not true, that we pretend there is a second PLL - there is a
>> >> PLL on each TX clock, maybe not a full DPLL, but still the loop with
>> >> a control over it's sources is there and it has the same 2 external
>> >> sources + default XO.
>> >
>> >Don't we put that MAC PLL into bypass mode if we feed a clock from
>> >the EEC DPLL?
>>
>> This HW doesn't use EEC DPLL signal to feed MAC clock, as DPLL is
>> external from NIC point of view. Only 2 signals from such external DPLL
>> device are used by NIC:
>> - synce (a single source for all those TXC per-port DPLL device)
>> - time_ref (a source for the TS_PLL - which drives PTP timer)
>
>No bypass? The PLL is actually in the loop? oof, this is beyond
>my understanding of clocks and signals :S
>
TBH, I am not entirely sure what do you mean with MAC PLL into bypass
mode, but the HW description I have provided is still true, the MAC is
not fed with any DPLL provided signal here. Only port tx clocks PLLs and
a timer PLL can use those.
>> >> A mentioned try of adding per port MUX-type pin, just to give some
>> >> control
>> >> to the user, is where we wanted to simplify things, but in the end
>> >> the
>> >> API
>> >> would have to be modified in significant way, various paths related
>> >> to
>> >> pin
>> >> registration and keeping correct references, just to make working
>> >> case
>> >> for the pin_on_pin_register and it's internals. We decided that the
>> >> burden
>> >> and impact for existing design was to high.
>> >>
>> >> And that is why the TXC approach emerged, the change of DPLL is
>> >> minimal,
>> >> The model is still correct from user perspective, SyncE SW controller
>> >> shall
>> >> anticipate possibility that per-port TXC dpll is there
>> >
>> >We are starting to push into what was previously the domain of
>> >drivers/clk, tho. IIUC the "ASIC PLL"s are usually integrated with
>> >clock dividers. And cannot be "configured" after chip init / async
>> >reset (which is why I presume you whack a reset in patch 7?).
>>
>> Well, we need CGU-dividers change for a frequency-compliance with lower
>> link speeds, the link reset which is required as part of tx-clk switch
>> and link establishment on a new clock.
>>
>> >
>> >> This particular device and driver doesn't implement any EEC-type DPLL
>> >> device, the one could think that we can just change the type here and
>> >> use
>> >> EEC type instead of new one TXC - since we share pins from external
>> >> dpll
>> >> driver, which is EEC type, and our DPLL device would have different
>> >> clock_id
>> >> and module. But, further designs, where a single NIC is having
>> >> control
>> >> over
>> >> both a EEC DPLL and ability to control each source per-port this
>> >> would
>> >> be
>> >> problematic. At least one NIC Port driver would have to have 2 EEC-
>> >> type
>> >> DPLLs
>> >> leaving user with extra confusion.
>> >
>> >The distinction between TXC and EEC dpll is confusing.
>> >I thought EEC one _was_supposed_to_ drive the Tx clock?
>> >What PPS means is obvious, what EEC means if not driving Tx clock is
>> >unclear to me..
>> >
>>
>> Yes, correct, EEC DPLL main task would be to drive TX clocks of NIC
>> ports, but if there is a per-port control something extra is required.
>>
>> >Let me summarize my concerns - we need to navigate the split between
>> >drivers/clk and dpll. We need a distinction on what goes where, because
>> >every ASIC has a bunch of PLLs which until now have been controlled by
>> >device tree (if at all). If the main question we want to answer is
>> >"which clock ref is used to drive internal clock" all we need is a MUX.
>> >If we want to make dpll cover also ASIC PLLs for platforms without
>> >device tree we need a more generic name than TXC, IMHO.
>>
>> Well, 'floating' MUX type pin not connected to any dpll would require a
>> lot of additional implementations, just to allow source selection, as we
>> have tried it already.
>>
>> Wouldn't more generic name cause a DPLL purpose problem?
>
>The old proposal in netdev family was to to have source selection
>without creating a real mux. Not saying I'm dead set on that direction.
>
Yes, correct, it kept the list of dpll pins valid for source selection of
tx clock within the netdev and control over it through RT netlink.
That solution was rather simple but you requested to hack into dpll so we
did here.
IMHO this is cleanest and simplest solution we could find to keep it
within DPLL subsystem.
>> We still want to make sure that given DPLL device would serve the role
>> of source selection for particular port where a source pin should be an
>> output either on EEC dpll or some external signal generator but somehow
>> related to SyncE or similar solutions.
>
>Right, but adding a new "type" per location of the PLL (especially if
>we lean into covering any ASIC PLL) may not scale, and opens us up to
>"vendor X calls it Y" and "in design A clock is fed by pll type X and
>in design B by type Y".
>
I was thinking that this is more like a purpose specific DPLL device, if
someone would want something similar we would have to review it, right?
>IIUC you do provide "linking" of the pins? netdev will have the MAC pin
>assigned. Is the pin that connects the PLLs also annotated so that user
>knows what's on the "other side"? Maybe the topology would be clear
>enough from just that, and we don't have to add a TXC type.
>Call the PLL "integrated" or something generic. User should be able to
>trace the path of the signals?
It depends, TX clock has one of external pins connected to external DPLL,
but second is a board-level pin with ability to provide some external
clock signal, the user would have to determine that purpose just based
on the topology of one of the pins, which seems a bit problematic?
I.e. if at some point there would be HW with only external non-DPLL
connected pins?
I mean 'generic' type is something we could do, but as already mentioned,
thought that we want a DPLL types specified/designed for some particular
functions/tasks.
Thank you!
Arkadiusz
^ permalink raw reply
* [PATCH net-deletions] caif: remove CAIF NETWORK LAYER
From: Jakub Kicinski @ 2026-04-16 18:28 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, Jakub Kicinski,
corbet, skhan, alexs, si.yanteng, dzm91, linux, mst, jasowang,
xuanzhuo, eperezma, xu.xin16, wang.yaxin, jiang.kun2, linusw,
jihed.chaibi.dev, arnd, tytso, jiayuan.chen, gregkh
Remove CAIF (Communication CPU to Application CPU Interface), the
ST-Ericsson modem protocol. The subsystem has been orphaned since 2013.
The last meaningful changes from the maintainers were in March 2013:
a8c7687bf216 ("caif_virtio: Check that vringh_config is not null")
b2273be8d2df ("caif_virtio: Use vringh_notify_enable correctly")
0d2e1a2926b1 ("caif_virtio: Introduce caif over virtio")
Not-so-coincidentally, according to "the Internet" ST-Ericsson officially
shut down its modem joint venture in Aug 2013.
If anyone is using this code please yell!
In the 13 years since, the code has accumulated 200 non-merge commits,
of which 71 were cross-tree API changes, 21 carried Fixes: tags, and
the remaining ~110 were cleanups, doc conversions, treewide refactors,
and one partial removal (caif_hsi, ca75bcf0a83b).
We are still getting fixes to this code, in the last 10 days there were
3 reports on security@ about CAIF that I have been CCed on.
UAPI constants (AF_CAIF, ARPHRD_CAIF, N_CAIF, VIRTIO_ID_CAIF) and the
SELinux classmap entry are intentionally kept for ABI stability.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
I think we should accumulate such patches over the coming days on a separate
branch. CAIF is a no-brainer IMO but other removals may be more controversial.
CC: corbet@lwn.net
CC: skhan@linuxfoundation.org
CC: alexs@kernel.org
CC: si.yanteng@linux.dev
CC: dzm91@hust.edu.cn
CC: linux@armlinux.org.uk
CC: mst@redhat.com
CC: jasowang@redhat.com
CC: xuanzhuo@linux.alibaba.com
CC: eperezma@redhat.com
CC: xu.xin16@zte.com.cn
CC: wang.yaxin@zte.com.cn
CC: jiang.kun2@zte.com.cn
CC: linusw@kernel.org
CC: jihed.chaibi.dev@gmail.com
CC: arnd@arndb.de
CC: tytso@mit.edu
CC: jiayuan.chen@shopee.com
CC: gregkh@linuxfoundation.org
---
MAINTAINERS | 9 -
Documentation/networking/caif/caif.rst | 138 --
Documentation/networking/caif/index.rst | 12 -
Documentation/networking/caif/linux_caif.rst | 195 ---
Documentation/networking/index.rst | 1 -
.../translations/zh_CN/networking/index.rst | 1 -
drivers/net/Kconfig | 2 -
drivers/net/caif/Kconfig | 33 -
net/Kconfig | 1 -
net/caif/Kconfig | 54 -
drivers/net/Makefile | 1 -
drivers/net/caif/Makefile | 8 -
net/Makefile | 1 -
net/caif/Makefile | 16 -
include/linux/virtio_caif.h | 24 -
include/net/caif/caif_dev.h | 128 --
include/net/caif/caif_device.h | 55 -
include/net/caif/caif_layer.h | 277 ----
include/net/caif/cfcnfg.h | 90 --
include/net/caif/cfctrl.h | 130 --
include/net/caif/cffrml.h | 21 -
include/net/caif/cfmuxl.h | 20 -
include/net/caif/cfpkt.h | 232 ----
include/net/caif/cfserl.h | 13 -
include/net/caif/cfsrvl.h | 61 -
include/uapi/linux/caif/caif_socket.h | 195 ---
include/uapi/linux/caif/if_caif.h | 35 -
drivers/net/caif/caif_serial.c | 443 -------
drivers/net/caif/caif_virtio.c | 791 ------------
net/caif/caif_dev.c | 586 ---------
net/caif/caif_socket.c | 1114 -----------------
net/caif/caif_usb.c | 216 ----
net/caif/cfcnfg.c | 612 ---------
net/caif/cfctrl.c | 631 ----------
net/caif/cfdbgl.c | 55 -
net/caif/cfdgml.c | 113 --
net/caif/cffrml.c | 204 ---
net/caif/cfmuxl.c | 267 ----
net/caif/cfpkt_skbuff.c | 373 ------
net/caif/cfrfml.c | 299 -----
net/caif/cfserl.c | 192 ---
net/caif/cfsrvl.c | 224 ----
net/caif/cfutill.c | 104 --
net/caif/cfveil.c | 101 --
net/caif/cfvidl.c | 65 -
net/caif/chnl_net.c | 531 --------
arch/arm/configs/u8500_defconfig | 1 -
47 files changed, 8675 deletions(-)
delete mode 100644 Documentation/networking/caif/caif.rst
delete mode 100644 Documentation/networking/caif/index.rst
delete mode 100644 Documentation/networking/caif/linux_caif.rst
delete mode 100644 drivers/net/caif/Kconfig
delete mode 100644 net/caif/Kconfig
delete mode 100644 drivers/net/caif/Makefile
delete mode 100644 net/caif/Makefile
delete mode 100644 include/linux/virtio_caif.h
delete mode 100644 include/net/caif/caif_dev.h
delete mode 100644 include/net/caif/caif_device.h
delete mode 100644 include/net/caif/caif_layer.h
delete mode 100644 include/net/caif/cfcnfg.h
delete mode 100644 include/net/caif/cfctrl.h
delete mode 100644 include/net/caif/cffrml.h
delete mode 100644 include/net/caif/cfmuxl.h
delete mode 100644 include/net/caif/cfpkt.h
delete mode 100644 include/net/caif/cfserl.h
delete mode 100644 include/net/caif/cfsrvl.h
delete mode 100644 include/uapi/linux/caif/caif_socket.h
delete mode 100644 include/uapi/linux/caif/if_caif.h
delete mode 100644 drivers/net/caif/caif_serial.c
delete mode 100644 drivers/net/caif/caif_virtio.c
delete mode 100644 net/caif/caif_dev.c
delete mode 100644 net/caif/caif_socket.c
delete mode 100644 net/caif/caif_usb.c
delete mode 100644 net/caif/cfcnfg.c
delete mode 100644 net/caif/cfctrl.c
delete mode 100644 net/caif/cfdbgl.c
delete mode 100644 net/caif/cfdgml.c
delete mode 100644 net/caif/cffrml.c
delete mode 100644 net/caif/cfmuxl.c
delete mode 100644 net/caif/cfpkt_skbuff.c
delete mode 100644 net/caif/cfrfml.c
delete mode 100644 net/caif/cfserl.c
delete mode 100644 net/caif/cfsrvl.c
delete mode 100644 net/caif/cfutill.c
delete mode 100644 net/caif/cfveil.c
delete mode 100644 net/caif/cfvidl.c
delete mode 100644 net/caif/chnl_net.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e7dc9e6fad2e..2b1b5e93c272 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5674,15 +5674,6 @@ T: git git://linuxtv.org/media.git
F: Documentation/admin-guide/media/cafe_ccic*
F: drivers/media/platform/marvell/
-CAIF NETWORK LAYER
-L: netdev@vger.kernel.org
-S: Orphan
-F: Documentation/networking/caif/
-F: drivers/net/caif/
-F: include/net/caif/
-F: include/uapi/linux/caif/
-F: net/caif/
-
CAKE QDISC
M: Toke Høiland-Jørgensen <toke@toke.dk>
L: cake@lists.bufferbloat.net (moderated for non-subscribers)
diff --git a/Documentation/networking/caif/caif.rst b/Documentation/networking/caif/caif.rst
deleted file mode 100644
index d922d419c513..000000000000
--- a/Documentation/networking/caif/caif.rst
+++ /dev/null
@@ -1,138 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-.. include:: <isonum.txt>
-
-
-================
-Using Linux CAIF
-================
-
-
-:Copyright: |copy| ST-Ericsson AB 2010
-
-:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
-
-Start
-=====
-
-If you have compiled CAIF for modules do::
-
- $modprobe crc_ccitt
- $modprobe caif
- $modprobe caif_socket
- $modprobe chnl_net
-
-
-Preparing the setup with a STE modem
-====================================
-
-If you are working on integration of CAIF you should make sure
-that the kernel is built with module support.
-
-There are some things that need to be tweaked to get the host TTY correctly
-set up to talk to the modem.
-Since the CAIF stack is running in the kernel and we want to use the existing
-TTY, we are installing our physical serial driver as a line discipline above
-the TTY device.
-
-To achieve this we need to install the N_CAIF ldisc from user space.
-The benefit is that we can hook up to any TTY.
-
-The use of Start-of-frame-extension (STX) must also be set as
-module parameter "ser_use_stx".
-
-Normally Frame Checksum is always used on UART, but this is also provided as a
-module parameter "ser_use_fcs".
-
-::
-
- $ modprobe caif_serial ser_ttyname=/dev/ttyS0 ser_use_stx=yes
- $ ifconfig caif_ttyS0 up
-
-PLEASE NOTE:
- There is a limitation in Android shell.
- It only accepts one argument to insmod/modprobe!
-
-Trouble shooting
-================
-
-There are debugfs parameters provided for serial communication.
-/sys/kernel/debug/caif_serial/<tty-name>/
-
-* ser_state: Prints the bit-mask status where
-
- - 0x02 means SENDING, this is a transient state.
- - 0x10 means FLOW_OFF_SENT, i.e. the previous frame has not been sent
- and is blocking further send operation. Flow OFF has been propagated
- to all CAIF Channels using this TTY.
-
-* tty_status: Prints the bit-mask tty status information
-
- - 0x01 - tty->warned is on.
- - 0x04 - tty->packed is on.
- - 0x08 - tty->flow.tco_stopped is on.
- - 0x10 - tty->hw_stopped is on.
- - 0x20 - tty->flow.stopped is on.
-
-* last_tx_msg: Binary blob Prints the last transmitted frame.
-
- This can be printed with::
-
- $od --format=x1 /sys/kernel/debug/caif_serial/<tty>/last_rx_msg.
-
- The first two tx messages sent look like this. Note: The initial
- byte 02 is start of frame extension (STX) used for re-syncing
- upon errors.
-
- - Enumeration::
-
- 0000000 02 05 00 00 03 01 d2 02
- | | | | | |
- STX(1) | | | |
- Length(2)| | |
- Control Channel(1)
- Command:Enumeration(1)
- Link-ID(1)
- Checksum(2)
-
- - Channel Setup::
-
- 0000000 02 07 00 00 00 21 a1 00 48 df
- | | | | | | | |
- STX(1) | | | | | |
- Length(2)| | | | |
- Control Channel(1)
- Command:Channel Setup(1)
- Channel Type(1)
- Priority and Link-ID(1)
- Endpoint(1)
- Checksum(2)
-
-* last_rx_msg: Prints the last transmitted frame.
-
- The RX messages for LinkSetup look almost identical but they have the
- bit 0x20 set in the command bit, and Channel Setup has added one byte
- before Checksum containing Channel ID.
-
- NOTE:
- Several CAIF Messages might be concatenated. The maximum debug
- buffer size is 128 bytes.
-
-Error Scenarios
-===============
-
-- last_tx_msg contains channel setup message and last_rx_msg is empty ->
- The host seems to be able to send over the UART, at least the CAIF ldisc get
- notified that sending is completed.
-
-- last_tx_msg contains enumeration message and last_rx_msg is empty ->
- The host is not able to send the message from UART, the tty has not been
- able to complete the transmit operation.
-
-- if /sys/kernel/debug/caif_serial/<tty>/tty_status is non-zero there
- might be problems transmitting over UART.
-
- E.g. host and modem wiring is not correct you will typically see
- tty_status = 0x10 (hw_stopped) and ser_state = 0x10 (FLOW_OFF_SENT).
-
- You will probably see the enumeration message in last_tx_message
- and empty last_rx_message.
diff --git a/Documentation/networking/caif/index.rst b/Documentation/networking/caif/index.rst
deleted file mode 100644
index ec29b6f4bdb4..000000000000
--- a/Documentation/networking/caif/index.rst
+++ /dev/null
@@ -1,12 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-CAIF
-====
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
- linux_caif
- caif
diff --git a/Documentation/networking/caif/linux_caif.rst b/Documentation/networking/caif/linux_caif.rst
deleted file mode 100644
index a0480862ab8c..000000000000
--- a/Documentation/networking/caif/linux_caif.rst
+++ /dev/null
@@ -1,195 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-.. include:: <isonum.txt>
-
-==========
-Linux CAIF
-==========
-
-Copyright |copy| ST-Ericsson AB 2010
-
-:Author: Sjur Brendeland/ sjur.brandeland@stericsson.com
-:License terms: GNU General Public License (GPL) version 2
-
-
-Introduction
-============
-
-CAIF is a MUX protocol used by ST-Ericsson cellular modems for
-communication between Modem and host. The host processes can open virtual AT
-channels, initiate GPRS Data connections, Video channels and Utility Channels.
-The Utility Channels are general purpose pipes between modem and host.
-
-ST-Ericsson modems support a number of transports between modem
-and host. Currently, UART and Loopback are available for Linux.
-
-
-Architecture
-============
-
-The implementation of CAIF is divided into:
-
-* CAIF Socket Layer and GPRS IP Interface.
-* CAIF Core Protocol Implementation
-* CAIF Link Layer, implemented as NET devices.
-
-::
-
- RTNL
- !
- ! +------+ +------+
- ! +------+! +------+!
- ! ! IP !! !Socket!!
- +-------> !interf!+ ! API !+ <- CAIF Client APIs
- ! +------+ +------!
- ! ! !
- ! +-----------+
- ! !
- ! +------+ <- CAIF Core Protocol
- ! ! CAIF !
- ! ! Core !
- ! +------+
- ! +----------!---------+
- ! ! ! !
- ! +------+ +-----+ +------+
- +--> ! HSI ! ! TTY ! ! USB ! <- Link Layer (Net Devices)
- +------+ +-----+ +------+
-
-
-
-Implementation
-==============
-
-
-CAIF Core Protocol Layer
-------------------------
-
-CAIF Core layer implements the CAIF protocol as defined by ST-Ericsson.
-It implements the CAIF protocol stack in a layered approach, where
-each layer described in the specification is implemented as a separate layer.
-The architecture is inspired by the design patterns "Protocol Layer" and
-"Protocol Packet".
-
-CAIF structure
-^^^^^^^^^^^^^^
-
-The Core CAIF implementation contains:
-
- - Simple implementation of CAIF.
- - Layered architecture (a la Streams), each layer in the CAIF
- specification is implemented in a separate c-file.
- - Clients must call configuration function to add PHY layer.
- - Clients must implement CAIF layer to consume/produce
- CAIF payload with receive and transmit functions.
- - Clients must call configuration function to add and connect the
- Client layer.
- - When receiving / transmitting CAIF Packets (cfpkt), ownership is passed
- to the called function (except for framing layers' receive function)
-
-Layered Architecture
-====================
-
-The CAIF protocol can be divided into two parts: Support functions and Protocol
-Implementation. The support functions include:
-
- - CFPKT CAIF Packet. Implementation of CAIF Protocol Packet. The
- CAIF Packet has functions for creating, destroying and adding content
- and for adding/extracting header and trailers to protocol packets.
-
-The CAIF Protocol implementation contains:
-
- - CFCNFG CAIF Configuration layer. Configures the CAIF Protocol
- Stack and provides a Client interface for adding Link-Layer and
- Driver interfaces on top of the CAIF Stack.
-
- - CFCTRL CAIF Control layer. Encodes and Decodes control messages
- such as enumeration and channel setup. Also matches request and
- response messages.
-
- - CFSERVL General CAIF Service Layer functionality; handles flow
- control and remote shutdown requests.
-
- - CFVEI CAIF VEI layer. Handles CAIF AT Channels on VEI (Virtual
- External Interface). This layer encodes/decodes VEI frames.
-
- - CFDGML CAIF Datagram layer. Handles CAIF Datagram layer (IP
- traffic), encodes/decodes Datagram frames.
-
- - CFMUX CAIF Mux layer. Handles multiplexing between multiple
- physical bearers and multiple channels such as VEI, Datagram, etc.
- The MUX keeps track of the existing CAIF Channels and
- Physical Instances and selects the appropriate instance based
- on Channel-Id and Physical-ID.
-
- - CFFRML CAIF Framing layer. Handles Framing i.e. Frame length
- and frame checksum.
-
- - CFSERL CAIF Serial layer. Handles concatenation/split of frames
- into CAIF Frames with correct length.
-
-::
-
- +---------+
- | Config |
- | CFCNFG |
- +---------+
- !
- +---------+ +---------+ +---------+
- | AT | | Control | | Datagram|
- | CFVEIL | | CFCTRL | | CFDGML |
- +---------+ +---------+ +---------+
- \_____________!______________/
- !
- +---------+
- | MUX |
- | |
- +---------+
- _____!_____
- / \
- +---------+ +---------+
- | CFFRML | | CFFRML |
- | Framing | | Framing |
- +---------+ +---------+
- ! !
- +---------+ +---------+
- | | | Serial |
- | | | CFSERL |
- +---------+ +---------+
-
-
-In this layered approach the following "rules" apply.
-
- - All layers embed the same structure "struct cflayer"
- - A layer does not depend on any other layer's private data.
- - Layers are stacked by setting the pointers::
-
- layer->up , layer->dn
-
- - In order to send data upwards, each layer should do::
-
- layer->up->receive(layer->up, packet);
-
- - In order to send data downwards, each layer should do::
-
- layer->dn->transmit(layer->dn, packet);
-
-
-CAIF Socket and IP interface
-============================
-
-The IP interface and CAIF socket API are implemented on top of the
-CAIF Core protocol. The IP Interface and CAIF socket have an instance of
-'struct cflayer', just like the CAIF Core protocol stack.
-Net device and Socket implement the 'receive()' function defined by
-'struct cflayer', just like the rest of the CAIF stack. In this way, transmit and
-receive of packets is handled as by the rest of the layers: the 'dn->transmit()'
-function is called in order to transmit data.
-
-Configuration of Link Layer
----------------------------
-The Link Layer is implemented as Linux network devices (struct net_device).
-Payload handling and registration is done using standard Linux mechanisms.
-
-The CAIF Protocol relies on a loss-less link layer without implementing
-retransmission. This implies that packet drops must not happen.
-Therefore a flow-control mechanism is implemented where the physical
-interface can initiate flow stop for all CAIF Channels.
diff --git a/Documentation/networking/index.rst b/Documentation/networking/index.rst
index c2406bd8ae0b..2e946924ad3f 100644
--- a/Documentation/networking/index.rst
+++ b/Documentation/networking/index.rst
@@ -17,7 +17,6 @@ Refer to :ref:`netdev-FAQ` for a guide on netdev development process specifics.
diagnostic/index
dsa/index
devlink/index
- caif/index
ethtool-netlink
ieee802154
iso15765-2
diff --git a/Documentation/translations/zh_CN/networking/index.rst b/Documentation/translations/zh_CN/networking/index.rst
index c276c0993c51..333e9f6cafff 100644
--- a/Documentation/translations/zh_CN/networking/index.rst
+++ b/Documentation/translations/zh_CN/networking/index.rst
@@ -42,7 +42,6 @@ 目录:
* diagnostic/index
* dsa/index
* devlink/index
-* caif/index
* ethtool-netlink
* ieee802154
* iso15765-2
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index edaab759dc50..8ec98f6dfef9 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -503,8 +503,6 @@ source "drivers/net/arcnet/Kconfig"
source "drivers/atm/Kconfig"
-source "drivers/net/caif/Kconfig"
-
source "drivers/net/dsa/Kconfig"
source "drivers/net/ethernet/Kconfig"
diff --git a/drivers/net/caif/Kconfig b/drivers/net/caif/Kconfig
deleted file mode 100644
index 709660cb38f8..000000000000
--- a/drivers/net/caif/Kconfig
+++ /dev/null
@@ -1,33 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# CAIF physical drivers
-#
-
-menuconfig CAIF_DRIVERS
- bool "CAIF transport drivers"
- depends on CAIF
- help
- Enable this to see CAIF physical drivers.
-
-if CAIF_DRIVERS
-
-config CAIF_TTY
- tristate "CAIF TTY transport driver"
- depends on CAIF && TTY
- default n
- help
- The CAIF TTY transport driver is a Line Discipline (ldisc)
- identified as N_CAIF. When this ldisc is opened from user space
- it will redirect the TTY's traffic into the CAIF stack.
-
-config CAIF_VIRTIO
- tristate "CAIF virtio transport driver"
- depends on CAIF && HAS_DMA
- select VHOST_RING
- select VIRTIO
- select GENERIC_ALLOCATOR
- default n
- help
- The CAIF driver for CAIF over Virtio.
-
-endif # CAIF_DRIVERS
diff --git a/net/Kconfig b/net/Kconfig
index 62266eaf0e95..5c588dbcbdbd 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -439,7 +439,6 @@ endif # WIRELESS
source "net/rfkill/Kconfig"
source "net/9p/Kconfig"
-source "net/caif/Kconfig"
source "net/ceph/Kconfig"
source "net/nfc/Kconfig"
source "net/psample/Kconfig"
diff --git a/net/caif/Kconfig b/net/caif/Kconfig
deleted file mode 100644
index 87205251cc25..000000000000
--- a/net/caif/Kconfig
+++ /dev/null
@@ -1,54 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-#
-# CAIF net configurations
-#
-
-menuconfig CAIF
- tristate "CAIF support"
- select CRC_CCITT
- default n
- help
- The "Communication CPU to Application CPU Interface" (CAIF) is a packet
- based connection-oriented MUX protocol developed by ST-Ericsson for use
- with its modems. It is accessed from user space as sockets (PF_CAIF).
-
- Say Y (or M) here if you build for a phone product (e.g. Android or
- MeeGo) that uses CAIF as transport. If unsure say N.
-
- If you select to build it as module then CAIF_NETDEV also needs to be
- built as a module. You will also need to say Y (or M) to any CAIF
- physical devices that your platform requires.
-
- See Documentation/networking/caif for a further explanation on how to
- use and configure CAIF.
-
-config CAIF_DEBUG
- bool "Enable Debug"
- depends on CAIF
- default n
- help
- Enable the inclusion of debug code in the CAIF stack.
- Be aware that doing this will impact performance.
- If unsure say N.
-
-config CAIF_NETDEV
- tristate "CAIF GPRS Network device"
- depends on CAIF
- default CAIF
- help
- Say Y if you will be using a CAIF based GPRS network device.
- This can be either built-in or a loadable module.
- If you select to build it as a built-in then the main CAIF device must
- also be a built-in.
- If unsure say Y.
-
-config CAIF_USB
- tristate "CAIF USB support"
- depends on CAIF
- default n
- help
- Say Y if you are using CAIF over USB CDC NCM.
- This can be either built-in or a loadable module.
- If you select to build it as a built-in then the main CAIF device must
- also be a built-in.
- If unsure say N.
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 5b01215f6829..3b2d28127634 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_MHI_NET) += mhi_net.o
# Networking Drivers
#
obj-$(CONFIG_ARCNET) += arcnet/
-obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_CAN) += can/
ifdef CONFIG_NET_DSA
obj-y += dsa/
diff --git a/drivers/net/caif/Makefile b/drivers/net/caif/Makefile
deleted file mode 100644
index 97f664f8016c..000000000000
--- a/drivers/net/caif/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG
-
-# Serial interface
-obj-$(CONFIG_CAIF_TTY) += caif_serial.o
-
-# Virtio interface
-obj-$(CONFIG_CAIF_VIRTIO) += caif_virtio.o
diff --git a/net/Makefile b/net/Makefile
index 90e3d72bf58b..98e182829eff 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -53,7 +53,6 @@ obj-$(CONFIG_IUCV) += iucv/
obj-$(CONFIG_SMC) += smc/
obj-$(CONFIG_RFKILL) += rfkill/
obj-$(CONFIG_NET_9P) += 9p/
-obj-$(CONFIG_CAIF) += caif/
obj-$(CONFIG_DCB) += dcb/
obj-$(CONFIG_6LOWPAN) += 6lowpan/
obj-$(CONFIG_IEEE802154) += ieee802154/
diff --git a/net/caif/Makefile b/net/caif/Makefile
deleted file mode 100644
index 4f6c0517cdfb..000000000000
--- a/net/caif/Makefile
+++ /dev/null
@@ -1,16 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-ccflags-$(CONFIG_CAIF_DEBUG) := -DDEBUG
-
-caif-y := caif_dev.o \
- cfcnfg.o cfmuxl.o cfctrl.o \
- cffrml.o cfveil.o cfdbgl.o\
- cfserl.o cfdgml.o \
- cfrfml.o cfvidl.o cfutill.o \
- cfsrvl.o cfpkt_skbuff.o
-
-obj-$(CONFIG_CAIF) += caif.o
-obj-$(CONFIG_CAIF_NETDEV) += chnl_net.o
-obj-$(CONFIG_CAIF) += caif_socket.o
-obj-$(CONFIG_CAIF_USB) += caif_usb.o
-
-export-y := caif.o
diff --git a/include/linux/virtio_caif.h b/include/linux/virtio_caif.h
deleted file mode 100644
index ea722479510c..000000000000
--- a/include/linux/virtio_caif.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson AB 2012
- * Author: Sjur Brændeland <sjur.brandeland@stericsson.com>
- *
- * This header is BSD licensed so
- * anyone can use the definitions to implement compatible remote processors
- */
-
-#ifndef VIRTIO_CAIF_H
-#define VIRTIO_CAIF_H
-
-#include <linux/types.h>
-struct virtio_caif_transf_config {
- __virtio16 headroom;
- __virtio16 tailroom;
- __virtio32 mtu;
- u8 reserved[4];
-};
-
-struct virtio_caif_config {
- struct virtio_caif_transf_config uplink, downlink;
- u8 reserved[8];
-};
-#endif
diff --git a/include/net/caif/caif_dev.h b/include/net/caif/caif_dev.h
deleted file mode 100644
index b655d8666f55..000000000000
--- a/include/net/caif/caif_dev.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CAIF_DEV_H_
-#define CAIF_DEV_H_
-
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfcnfg.h>
-#include <net/caif/caif_device.h>
-#include <linux/caif/caif_socket.h>
-#include <linux/if.h>
-#include <linux/net.h>
-
-/**
- * struct caif_param - CAIF parameters.
- * @size: Length of data
- * @data: Binary Data Blob
- */
-struct caif_param {
- u16 size;
- u8 data[256];
-};
-
-/**
- * struct caif_connect_request - Request data for CAIF channel setup.
- * @protocol: Type of CAIF protocol to use (at, datagram etc)
- * @sockaddr: Socket address to connect.
- * @priority: Priority of the connection.
- * @link_selector: Link selector (high bandwidth or low latency)
- * @ifindex: kernel index of the interface.
- * @param: Connect Request parameters (CAIF_SO_REQ_PARAM).
- *
- * This struct is used when connecting a CAIF channel.
- * It contains all CAIF channel configuration options.
- */
-struct caif_connect_request {
- enum caif_protocol_type protocol;
- struct sockaddr_caif sockaddr;
- enum caif_channel_priority priority;
- enum caif_link_selector link_selector;
- int ifindex;
- struct caif_param param;
-};
-
-/**
- * caif_connect_client - Connect a client to CAIF Core Stack.
- * @config: Channel setup parameters, specifying what address
- * to connect on the Modem.
- * @client_layer: User implementation of client layer. This layer
- * MUST have receive and control callback functions
- * implemented.
- * @ifindex: Link layer interface index used for this connection.
- * @headroom: Head room needed by CAIF protocol.
- * @tailroom: Tail room needed by CAIF protocol.
- *
- * This function connects a CAIF channel. The Client must implement
- * the struct cflayer. This layer represents the Client layer and holds
- * receive functions and control callback functions. Control callback
- * function will receive information about connect/disconnect responses,
- * flow control etc (see enum caif_control).
- * E.g. CAIF Socket will call this function for each socket it connects
- * and have one client_layer instance for each socket.
- */
-int caif_connect_client(struct net *net,
- struct caif_connect_request *conn_req,
- struct cflayer *client_layer, int *ifindex,
- int *headroom, int *tailroom);
-
-/**
- * caif_disconnect_client - Disconnects a client from the CAIF stack.
- *
- * @client_layer: Client layer to be disconnected.
- */
-int caif_disconnect_client(struct net *net, struct cflayer *client_layer);
-
-
-/**
- * caif_client_register_refcnt - register ref-count functions provided by client.
- *
- * @adapt_layer: Client layer using CAIF Stack.
- * @hold: Function provided by client layer increasing ref-count
- * @put: Function provided by client layer decreasing ref-count
- *
- * Client of the CAIF Stack must register functions for reference counting.
- * These functions are called by the CAIF Stack for every upstream packet,
- * and must therefore be implemented efficiently.
- *
- * Client should call caif_free_client when reference count degrease to zero.
- */
-
-void caif_client_register_refcnt(struct cflayer *adapt_layer,
- void (*hold)(struct cflayer *lyr),
- void (*put)(struct cflayer *lyr));
-/**
- * caif_free_client - Free memory used to manage the client in the CAIF Stack.
- *
- * @client_layer: Client layer to be removed.
- *
- * This function must be called from client layer in order to free memory.
- * Caller must guarantee that no packets are in flight upstream when calling
- * this function.
- */
-void caif_free_client(struct cflayer *adap_layer);
-
-/**
- * struct caif_enroll_dev - Enroll a net-device as a CAIF Link layer
- * @dev: Network device to enroll.
- * @caifdev: Configuration information from CAIF Link Layer
- * @link_support: Link layer support layer
- * @head_room: Head room needed by link support layer
- * @layer: Lowest layer in CAIF stack
- * @rcv_fun: Receive function for CAIF stack.
- *
- * This function enroll a CAIF link layer into CAIF Stack and
- * expects the interface to be able to handle CAIF payload.
- * The link_support layer is used to add any Link Layer specific
- * framing.
- */
-int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
- struct cflayer *link_support, int head_room,
- struct cflayer **layer, int (**rcv_func)(
- struct sk_buff *, struct net_device *,
- struct packet_type *, struct net_device *));
-
-#endif /* CAIF_DEV_H_ */
diff --git a/include/net/caif/caif_device.h b/include/net/caif/caif_device.h
deleted file mode 100644
index 91d1fd5b44a4..000000000000
--- a/include/net/caif/caif_device.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CAIF_DEVICE_H_
-#define CAIF_DEVICE_H_
-#include <linux/kernel.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/caif/caif_socket.h>
-#include <net/caif/caif_device.h>
-
-/**
- * struct caif_dev_common - data shared between CAIF drivers and stack.
- * @flowctrl: Flow Control callback function. This function is
- * supplied by CAIF Core Stack and is used by CAIF
- * Link Layer to send flow-stop to CAIF Core.
- * The flow information will be distributed to all
- * clients of CAIF.
- *
- * @link_select: Profile of device, either high-bandwidth or
- * low-latency. This member is set by CAIF Link
- * Layer Device in order to indicate if this device
- * is a high bandwidth or low latency device.
- *
- * @use_frag: CAIF Frames may be fragmented.
- * Is set by CAIF Link Layer in order to indicate if the
- * interface receives fragmented frames that must be
- * assembled by CAIF Core Layer.
- *
- * @use_fcs: Indicate if Frame CheckSum (fcs) is used.
- * Is set if the physical interface is
- * using Frame Checksum on the CAIF Frames.
- *
- * @use_stx: Indicate STart of frame eXtension (stx) in use.
- * Is set if the CAIF Link Layer expects
- * CAIF Frames to start with the STX byte.
- *
- * This structure is shared between the CAIF drivers and the CAIF stack.
- * It is used by the device to register its behavior.
- * CAIF Core layer must set the member flowctrl in order to supply
- * CAIF Link Layer with the flow control function.
- *
- */
- struct caif_dev_common {
- void (*flowctrl)(struct net_device *net, int on);
- enum caif_link_selector link_select;
- int use_frag;
- int use_fcs;
- int use_stx;
-};
-
-#endif /* CAIF_DEVICE_H_ */
diff --git a/include/net/caif/caif_layer.h b/include/net/caif/caif_layer.h
deleted file mode 100644
index 053e7c6a6a66..000000000000
--- a/include/net/caif/caif_layer.h
+++ /dev/null
@@ -1,277 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CAIF_LAYER_H_
-#define CAIF_LAYER_H_
-
-#include <linux/list.h>
-
-struct cflayer;
-struct cfpkt;
-struct caif_payload_info;
-
-#define CAIF_LAYER_NAME_SZ 16
-
-/**
- * caif_assert() - Assert function for CAIF.
- * @assert: expression to evaluate.
- *
- * This function will print a error message and a do WARN_ON if the
- * assertion fails. Normally this will do a stack up at the current location.
- */
-#define caif_assert(assert) \
-do { \
- if (!(assert)) { \
- pr_err("caif:Assert detected:'%s'\n", #assert); \
- WARN_ON(!(assert)); \
- } \
-} while (0)
-
-/**
- * enum caif_ctrlcmd - CAIF Stack Control Signaling sent in layer.ctrlcmd().
- *
- * @CAIF_CTRLCMD_FLOW_OFF_IND: Flow Control is OFF, transmit function
- * should stop sending data
- *
- * @CAIF_CTRLCMD_FLOW_ON_IND: Flow Control is ON, transmit function
- * can start sending data
- *
- * @CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND: Remote end modem has decided to close
- * down channel
- *
- * @CAIF_CTRLCMD_INIT_RSP: Called initially when the layer below
- * has finished initialization
- *
- * @CAIF_CTRLCMD_DEINIT_RSP: Called when de-initialization is
- * complete
- *
- * @CAIF_CTRLCMD_INIT_FAIL_RSP: Called if initialization fails
- *
- * @_CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND: CAIF Link layer temporarily cannot
- * send more packets.
- * @_CAIF_CTRLCMD_PHYIF_FLOW_ON_IND: Called if CAIF Link layer is able
- * to send packets again.
- * @_CAIF_CTRLCMD_PHYIF_DOWN_IND: Called if CAIF Link layer is going
- * down.
- *
- * These commands are sent upwards in the CAIF stack to the CAIF Client.
- * They are used for signaling originating from the modem or CAIF Link Layer.
- * These are either responses (*_RSP) or events (*_IND).
- */
-enum caif_ctrlcmd {
- CAIF_CTRLCMD_FLOW_OFF_IND,
- CAIF_CTRLCMD_FLOW_ON_IND,
- CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
- CAIF_CTRLCMD_INIT_RSP,
- CAIF_CTRLCMD_DEINIT_RSP,
- CAIF_CTRLCMD_INIT_FAIL_RSP,
- _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
- _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
- _CAIF_CTRLCMD_PHYIF_DOWN_IND,
-};
-
-/**
- * enum caif_modemcmd - Modem Control Signaling, sent from CAIF Client
- * to the CAIF Link Layer or modem.
- *
- * @CAIF_MODEMCMD_FLOW_ON_REQ: Flow Control is ON, transmit function
- * can start sending data.
- *
- * @CAIF_MODEMCMD_FLOW_OFF_REQ: Flow Control is OFF, transmit function
- * should stop sending data.
- *
- * @_CAIF_MODEMCMD_PHYIF_USEFULL: Notify physical layer that it is in use
- *
- * @_CAIF_MODEMCMD_PHYIF_USELESS: Notify physical layer that it is
- * no longer in use.
- *
- * These are requests sent 'downwards' in the stack.
- * Flow ON, OFF can be indicated to the modem.
- */
-enum caif_modemcmd {
- CAIF_MODEMCMD_FLOW_ON_REQ = 0,
- CAIF_MODEMCMD_FLOW_OFF_REQ = 1,
- _CAIF_MODEMCMD_PHYIF_USEFULL = 3,
- _CAIF_MODEMCMD_PHYIF_USELESS = 4
-};
-
-/**
- * enum caif_direction - CAIF Packet Direction.
- * Indicate if a packet is to be sent out or to be received in.
- * @CAIF_DIR_IN: Incoming packet received.
- * @CAIF_DIR_OUT: Outgoing packet to be transmitted.
- */
-enum caif_direction {
- CAIF_DIR_IN = 0,
- CAIF_DIR_OUT = 1
-};
-
-/**
- * struct cflayer - CAIF Stack layer.
- * Defines the framework for the CAIF Core Stack.
- * @up: Pointer up to the layer above.
- * @dn: Pointer down to the layer below.
- * @node: List node used when layer participate in a list.
- * @receive: Packet receive function.
- * @transmit: Packet transmit function.
- * @ctrlcmd: Used for control signalling upwards in the stack.
- * @modemcmd: Used for control signaling downwards in the stack.
- * @id: The identity of this layer
- * @name: Name of the layer.
- *
- * This structure defines the layered structure in CAIF.
- *
- * It defines CAIF layering structure, used by all CAIF Layers and the
- * layers interfacing CAIF.
- *
- * In order to integrate with CAIF an adaptation layer on top of the CAIF stack
- * and PHY layer below the CAIF stack
- * must be implemented. These layer must follow the design principles below.
- *
- * Principles for layering of protocol layers:
- * - All layers must use this structure. If embedding it, then place this
- * structure first in the layer specific structure.
- *
- * - Each layer should not depend on any others layer's private data.
- *
- * - In order to send data upwards do
- * layer->up->receive(layer->up, packet);
- *
- * - In order to send data downwards do
- * layer->dn->transmit(layer->dn, info, packet);
- */
-struct cflayer {
- struct cflayer *up;
- struct cflayer *dn;
- struct list_head node;
-
- /*
- * receive() - Receive Function (non-blocking).
- * Contract: Each layer must implement a receive function passing the
- * CAIF packets upwards in the stack.
- * Packet handling rules:
- * - The CAIF packet (cfpkt) ownership is passed to the
- * called receive function. This means that the
- * packet cannot be accessed after passing it to the
- * above layer using up->receive().
- *
- * - If parsing of the packet fails, the packet must be
- * destroyed and negative error code returned
- * from the function.
- * EXCEPTION: If the framing layer (cffrml) returns
- * -EILSEQ, the packet is not freed.
- *
- * - If parsing succeeds (and above layers return OK) then
- * the function must return a value >= 0.
- *
- * Returns result < 0 indicates an error, 0 or positive value
- * indicates success.
- *
- * @layr: Pointer to the current layer the receive function is
- * implemented for (this pointer).
- * @cfpkt: Pointer to CaifPacket to be handled.
- */
- int (*receive)(struct cflayer *layr, struct cfpkt *cfpkt);
-
- /*
- * transmit() - Transmit Function (non-blocking).
- * Contract: Each layer must implement a transmit function passing the
- * CAIF packet downwards in the stack.
- * Packet handling rules:
- * - The CAIF packet (cfpkt) ownership is passed to the
- * transmit function. This means that the packet
- * cannot be accessed after passing it to the below
- * layer using dn->transmit().
- *
- * - Upon error the packet ownership is still passed on,
- * so the packet shall be freed where error is detected.
- * Callers of the transmit function shall not free packets,
- * but errors shall be returned.
- *
- * - Return value less than zero means error, zero or
- * greater than zero means OK.
- *
- * Returns result < 0 indicates an error, 0 or positive value
- * indicates success.
- *
- * @layr: Pointer to the current layer the receive function
- * isimplemented for (this pointer).
- * @cfpkt: Pointer to CaifPacket to be handled.
- */
- int (*transmit) (struct cflayer *layr, struct cfpkt *cfpkt);
-
- /*
- * cttrlcmd() - Control Function upwards in CAIF Stack (non-blocking).
- * Used for signaling responses (CAIF_CTRLCMD_*_RSP)
- * and asynchronous events from the modem (CAIF_CTRLCMD_*_IND)
- *
- * @layr: Pointer to the current layer the receive function
- * is implemented for (this pointer).
- * @ctrl: Control Command.
- */
- void (*ctrlcmd) (struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
-
- /*
- * modemctrl() - Control Function used for controlling the modem.
- * Used to signal down-wards in the CAIF stack.
- * Returns 0 on success, < 0 upon failure.
- *
- * @layr: Pointer to the current layer the receive function
- * is implemented for (this pointer).
- * @ctrl: Control Command.
- */
- int (*modemcmd) (struct cflayer *layr, enum caif_modemcmd ctrl);
-
- unsigned int id;
- char name[CAIF_LAYER_NAME_SZ];
-};
-
-/**
- * layer_set_up() - Set the up pointer for a specified layer.
- * @layr: Layer where up pointer shall be set.
- * @above: Layer above.
- */
-#define layer_set_up(layr, above) ((layr)->up = (struct cflayer *)(above))
-
-/**
- * layer_set_dn() - Set the down pointer for a specified layer.
- * @layr: Layer where down pointer shall be set.
- * @below: Layer below.
- */
-#define layer_set_dn(layr, below) ((layr)->dn = (struct cflayer *)(below))
-
-/**
- * struct dev_info - Physical Device info information about physical layer.
- * @dev: Pointer to native physical device.
- * @id: Physical ID of the physical connection used by the
- * logical CAIF connection. Used by service layers to
- * identify their physical id to Caif MUX (CFMUXL)so
- * that the MUX can add the correct physical ID to the
- * packet.
- */
-struct dev_info {
- void *dev;
- unsigned int id;
-};
-
-/**
- * struct caif_payload_info - Payload information embedded in packet (sk_buff).
- *
- * @dev_info: Information about the receiving device.
- *
- * @hdr_len: Header length, used to align pay load on 32bit boundary.
- *
- * @channel_id: Channel ID of the logical CAIF connection.
- * Used by mux to insert channel id into the caif packet.
- */
-struct caif_payload_info {
- struct dev_info *dev_info;
- unsigned short hdr_len;
- unsigned short channel_id;
-};
-
-#endif /* CAIF_LAYER_H_ */
diff --git a/include/net/caif/cfcnfg.h b/include/net/caif/cfcnfg.h
deleted file mode 100644
index 8819ff4db35a..000000000000
--- a/include/net/caif/cfcnfg.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFCNFG_H_
-#define CFCNFG_H_
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfctrl.h>
-
-struct cfcnfg;
-
-/**
- * enum cfcnfg_phy_preference - Physical preference HW Abstraction
- *
- * @CFPHYPREF_UNSPECIFIED: Default physical interface
- *
- * @CFPHYPREF_LOW_LAT: Default physical interface for low-latency
- * traffic
- * @CFPHYPREF_HIGH_BW: Default physical interface for high-bandwidth
- * traffic
- * @CFPHYPREF_LOOP: TEST only Loopback interface simulating modem
- * responses.
- *
- */
-enum cfcnfg_phy_preference {
- CFPHYPREF_UNSPECIFIED,
- CFPHYPREF_LOW_LAT,
- CFPHYPREF_HIGH_BW,
- CFPHYPREF_LOOP
-};
-
-/**
- * cfcnfg_create() - Get the CAIF configuration object given network.
- * @net: Network for the CAIF configuration object.
- */
-struct cfcnfg *get_cfcnfg(struct net *net);
-
-/**
- * cfcnfg_create() - Create the CAIF configuration object.
- */
-struct cfcnfg *cfcnfg_create(void);
-
-/**
- * cfcnfg_remove() - Remove the CFCNFG object
- * @cfg: config object
- */
-void cfcnfg_remove(struct cfcnfg *cfg);
-
-/**
- * cfcnfg_add_phy_layer() - Adds a physical layer to the CAIF stack.
- * @cnfg: Pointer to a CAIF configuration object, created by
- * cfcnfg_create().
- * @dev: Pointer to link layer device
- * @phy_layer: Specify the physical layer. The transmit function
- * MUST be set in the structure.
- * @pref: The phy (link layer) preference.
- * @link_support: Protocol implementation for link layer specific protocol.
- * @fcs: Specify if checksum is used in CAIF Framing Layer.
- * @head_room: Head space needed by link specific protocol.
- */
-int
-cfcnfg_add_phy_layer(struct cfcnfg *cnfg,
- struct net_device *dev, struct cflayer *phy_layer,
- enum cfcnfg_phy_preference pref,
- struct cflayer *link_support,
- bool fcs, int head_room);
-
-/**
- * cfcnfg_del_phy_layer - Deletes an phy layer from the CAIF stack.
- *
- * @cnfg: Pointer to a CAIF configuration object, created by
- * cfcnfg_create().
- * @phy_layer: Adaptation layer to be removed.
- */
-int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer);
-
-/**
- * cfcnfg_set_phy_state() - Set the state of the physical interface device.
- * @cnfg: Configuration object
- * @phy_layer: Physical Layer representation
- * @up: State of device
- */
-int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer,
- bool up);
-
-#endif /* CFCNFG_H_ */
diff --git a/include/net/caif/cfctrl.h b/include/net/caif/cfctrl.h
deleted file mode 100644
index 86d17315c8a1..000000000000
--- a/include/net/caif/cfctrl.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFCTRL_H_
-#define CFCTRL_H_
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-
-/* CAIF Control packet commands */
-enum cfctrl_cmd {
- CFCTRL_CMD_LINK_SETUP = 0,
- CFCTRL_CMD_LINK_DESTROY = 1,
- CFCTRL_CMD_LINK_ERR = 2,
- CFCTRL_CMD_ENUM = 3,
- CFCTRL_CMD_SLEEP = 4,
- CFCTRL_CMD_WAKE = 5,
- CFCTRL_CMD_LINK_RECONF = 6,
- CFCTRL_CMD_START_REASON = 7,
- CFCTRL_CMD_RADIO_SET = 8,
- CFCTRL_CMD_MODEM_SET = 9,
- CFCTRL_CMD_MASK = 0xf
-};
-
-/* Channel types */
-enum cfctrl_srv {
- CFCTRL_SRV_DECM = 0,
- CFCTRL_SRV_VEI = 1,
- CFCTRL_SRV_VIDEO = 2,
- CFCTRL_SRV_DBG = 3,
- CFCTRL_SRV_DATAGRAM = 4,
- CFCTRL_SRV_RFM = 5,
- CFCTRL_SRV_UTIL = 6,
- CFCTRL_SRV_MASK = 0xf
-};
-
-#define CFCTRL_RSP_BIT 0x20
-#define CFCTRL_ERR_BIT 0x10
-
-struct cfctrl_rsp {
- void (*linksetup_rsp)(struct cflayer *layer, u8 linkid,
- enum cfctrl_srv serv, u8 phyid,
- struct cflayer *adapt_layer);
- void (*linkdestroy_rsp)(struct cflayer *layer, u8 linkid);
- void (*linkerror_ind)(void);
- void (*enum_rsp)(void);
- void (*sleep_rsp)(void);
- void (*wake_rsp)(void);
- void (*restart_rsp)(void);
- void (*radioset_rsp)(void);
- void (*reject_rsp)(struct cflayer *layer, u8 linkid,
- struct cflayer *client_layer);
-};
-
-/* Link Setup Parameters for CAIF-Links. */
-struct cfctrl_link_param {
- enum cfctrl_srv linktype;/* (T3,T0) Type of Channel */
- u8 priority; /* (P4,P0) Priority of the channel */
- u8 phyid; /* (U2-U0) Physical interface to connect */
- u8 endpoint; /* (E1,E0) Endpoint for data channels */
- u8 chtype; /* (H1,H0) Channel-Type, applies to
- * VEI, DEBUG */
- union {
- struct {
- u8 connid; /* (D7,D0) Video LinkId */
- } video;
-
- struct {
- u32 connid; /* (N31,Ngit0) Connection ID used
- * for Datagram */
- } datagram;
-
- struct {
- u32 connid; /* Connection ID used for RFM */
- char volume[20]; /* Volume to mount for RFM */
- } rfm; /* Configuration for RFM */
-
- struct {
- u16 fifosize_kb; /* Psock FIFO size in KB */
- u16 fifosize_bufs; /* Psock # signal buffers */
- char name[16]; /* Name of the PSOCK service */
- u8 params[255]; /* Link setup Parameters> */
- u16 paramlen; /* Length of Link Setup
- * Parameters */
- } utility; /* Configuration for Utility Links (Psock) */
- } u;
-};
-
-/* This structure is used internally in CFCTRL */
-struct cfctrl_request_info {
- int sequence_no;
- enum cfctrl_cmd cmd;
- u8 channel_id;
- struct cfctrl_link_param param;
- struct cflayer *client_layer;
- struct list_head list;
-};
-
-struct cfctrl {
- struct cfsrvl serv;
- struct cfctrl_rsp res;
- atomic_t req_seq_no;
- atomic_t rsp_seq_no;
- struct list_head list;
- /* Protects from simultaneous access to first_req list */
- spinlock_t info_list_lock;
-#ifndef CAIF_NO_LOOP
- u8 loop_linkid;
- int loop_linkused[256];
- /* Protects simultaneous access to loop_linkid and loop_linkused */
- spinlock_t loop_linkid_lock;
-#endif
-
-};
-
-void cfctrl_enum_req(struct cflayer *cfctrl, u8 physlinkid);
-int cfctrl_linkup_request(struct cflayer *cfctrl,
- struct cfctrl_link_param *param,
- struct cflayer *user_layer);
-int cfctrl_linkdown_req(struct cflayer *cfctrl, u8 linkid,
- struct cflayer *client);
-
-struct cflayer *cfctrl_create(void);
-struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer);
-int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer);
-void cfctrl_remove(struct cflayer *layr);
-
-#endif /* CFCTRL_H_ */
diff --git a/include/net/caif/cffrml.h b/include/net/caif/cffrml.h
deleted file mode 100644
index 1ab8a80ede4d..000000000000
--- a/include/net/caif/cffrml.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFFRML_H_
-#define CFFRML_H_
-#include <net/caif/caif_layer.h>
-#include <linux/netdevice.h>
-
-struct cffrml;
-struct cflayer *cffrml_create(u16 phyid, bool use_fcs);
-void cffrml_free(struct cflayer *layr);
-void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up);
-void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn);
-void cffrml_put(struct cflayer *layr);
-void cffrml_hold(struct cflayer *layr);
-int cffrml_refcnt_read(struct cflayer *layr);
-
-#endif /* CFFRML_H_ */
diff --git a/include/net/caif/cfmuxl.h b/include/net/caif/cfmuxl.h
deleted file mode 100644
index 92ccb2648309..000000000000
--- a/include/net/caif/cfmuxl.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFMUXL_H_
-#define CFMUXL_H_
-#include <net/caif/caif_layer.h>
-
-struct cfsrvl;
-struct cffrml;
-
-struct cflayer *cfmuxl_create(void);
-int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid);
-struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid);
-int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *up, u8 phyid);
-struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 linkid);
-
-#endif /* CFMUXL_H_ */
diff --git a/include/net/caif/cfpkt.h b/include/net/caif/cfpkt.h
deleted file mode 100644
index acf664227d96..000000000000
--- a/include/net/caif/cfpkt.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFPKT_H_
-#define CFPKT_H_
-#include <net/caif/caif_layer.h>
-#include <linux/types.h>
-struct cfpkt;
-
-/* Create a CAIF packet.
- * len: Length of packet to be created
- * @return New packet.
- */
-struct cfpkt *cfpkt_create(u16 len);
-
-/*
- * Destroy a CAIF Packet.
- * pkt Packet to be destroyed.
- */
-void cfpkt_destroy(struct cfpkt *pkt);
-
-/*
- * Extract header from packet.
- *
- * pkt Packet to extract header data from.
- * data Pointer to copy the header data into.
- * len Length of head data to copy.
- * @return zero on success and error code upon failure
- */
-int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len);
-
-static inline u8 cfpkt_extr_head_u8(struct cfpkt *pkt)
-{
- u8 tmp;
-
- cfpkt_extr_head(pkt, &tmp, 1);
-
- return tmp;
-}
-
-static inline u16 cfpkt_extr_head_u16(struct cfpkt *pkt)
-{
- __le16 tmp;
-
- cfpkt_extr_head(pkt, &tmp, 2);
-
- return le16_to_cpu(tmp);
-}
-
-static inline u32 cfpkt_extr_head_u32(struct cfpkt *pkt)
-{
- __le32 tmp;
-
- cfpkt_extr_head(pkt, &tmp, 4);
-
- return le32_to_cpu(tmp);
-}
-
-/*
- * Peek header from packet.
- * Reads data from packet without changing packet.
- *
- * pkt Packet to extract header data from.
- * data Pointer to copy the header data into.
- * len Length of head data to copy.
- * @return zero on success and error code upon failure
- */
-int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len);
-
-/*
- * Extract header from trailer (end of packet).
- *
- * pkt Packet to extract header data from.
- * data Pointer to copy the trailer data into.
- * len Length of header data to copy.
- * @return zero on success and error code upon failure
- */
-int cfpkt_extr_trail(struct cfpkt *pkt, void *data, u16 len);
-
-/*
- * Add header to packet.
- *
- *
- * pkt Packet to add header data to.
- * data Pointer to data to copy into the header.
- * len Length of header data to copy.
- * @return zero on success and error code upon failure
- */
-int cfpkt_add_head(struct cfpkt *pkt, const void *data, u16 len);
-
-/*
- * Add trailer to packet.
- *
- *
- * pkt Packet to add trailer data to.
- * data Pointer to data to copy into the trailer.
- * len Length of trailer data to copy.
- * @return zero on success and error code upon failure
- */
-int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len);
-
-/*
- * Pad trailer on packet.
- * Moves data pointer in packet, no content copied.
- *
- * pkt Packet in which to pad trailer.
- * len Length of padding to add.
- * @return zero on success and error code upon failure
- */
-int cfpkt_pad_trail(struct cfpkt *pkt, u16 len);
-
-/*
- * Add a single byte to packet body (tail).
- *
- * pkt Packet in which to add byte.
- * data Byte to add.
- * @return zero on success and error code upon failure
- */
-int cfpkt_addbdy(struct cfpkt *pkt, const u8 data);
-
-/*
- * Add a data to packet body (tail).
- *
- * pkt Packet in which to add data.
- * data Pointer to data to copy into the packet body.
- * len Length of data to add.
- * @return zero on success and error code upon failure
- */
-int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len);
-
-/*
- * Checks whether there are more data to process in packet.
- * pkt Packet to check.
- * @return true if more data are available in packet false otherwise
- */
-bool cfpkt_more(struct cfpkt *pkt);
-
-/*
- * Checks whether the packet is erroneous,
- * i.e. if it has been attempted to extract more data than available in packet
- * or writing more data than has been allocated in cfpkt_create().
- * pkt Packet to check.
- * @return true on error false otherwise
- */
-bool cfpkt_erroneous(struct cfpkt *pkt);
-
-/*
- * Get the packet length.
- * pkt Packet to get length from.
- * @return Number of bytes in packet.
- */
-u16 cfpkt_getlen(struct cfpkt *pkt);
-
-/*
- * Set the packet length, by adjusting the trailer pointer according to length.
- * pkt Packet to set length.
- * len Packet length.
- * @return Number of bytes in packet.
- */
-int cfpkt_setlen(struct cfpkt *pkt, u16 len);
-
-/*
- * cfpkt_append - Appends a packet's data to another packet.
- * dstpkt: Packet to append data into, WILL BE FREED BY THIS FUNCTION
- * addpkt: Packet to be appended and automatically released,
- * WILL BE FREED BY THIS FUNCTION.
- * expectlen: Packet's expected total length. This should be considered
- * as a hint.
- * NB: Input packets will be destroyed after appending and cannot be used
- * after calling this function.
- * @return The new appended packet.
- */
-struct cfpkt *cfpkt_append(struct cfpkt *dstpkt, struct cfpkt *addpkt,
- u16 expectlen);
-
-/*
- * cfpkt_split - Split a packet into two packets at the specified split point.
- * pkt: Packet to be split (will contain the first part of the data on exit)
- * pos: Position to split packet in two parts.
- * @return The new packet, containing the second part of the data.
- */
-struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos);
-
-/*
- * Iteration function, iterates the packet buffers from start to end.
- *
- * Checksum iteration function used to iterate buffers
- * (we may have packets consisting of a chain of buffers)
- * pkt: Packet to calculate checksum for
- * iter_func: Function pointer to iteration function
- * chks: Checksum calculated so far.
- * buf: Pointer to the buffer to checksum
- * len: Length of buf.
- * data: Initial checksum value.
- * @return Checksum of buffer.
- */
-
-int cfpkt_iterate(struct cfpkt *pkt,
- u16 (*iter_func)(u16 chks, void *buf, u16 len),
- u16 data);
-
-/* Map from a "native" packet (e.g. Linux Socket Buffer) to a CAIF packet.
- * dir - Direction indicating whether this packet is to be sent or received.
- * nativepkt - The native packet to be transformed to a CAIF packet
- * @return The mapped CAIF Packet CFPKT.
- */
-struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt);
-
-/* Map from a CAIF packet to a "native" packet (e.g. Linux Socket Buffer).
- * pkt - The CAIF packet to be transformed into a "native" packet.
- * @return The native packet transformed from a CAIF packet.
- */
-void *cfpkt_tonative(struct cfpkt *pkt);
-
-/*
- * Returns packet information for a packet.
- * pkt Packet to get info from;
- * @return Packet information
- */
-struct caif_payload_info *cfpkt_info(struct cfpkt *pkt);
-
-/** cfpkt_set_prio - set priority for a CAIF packet.
- *
- * @pkt: The CAIF packet to be adjusted.
- * @prio: one of TC_PRIO_ constants.
- */
-void cfpkt_set_prio(struct cfpkt *pkt, int prio);
-
-#endif /* CFPKT_H_ */
diff --git a/include/net/caif/cfserl.h b/include/net/caif/cfserl.h
deleted file mode 100644
index 67cce8757175..000000000000
--- a/include/net/caif/cfserl.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFSERL_H_
-#define CFSERL_H_
-#include <net/caif/caif_layer.h>
-
-struct cflayer *cfserl_create(int instance, bool use_stx);
-void cfserl_release(struct cflayer *layer);
-#endif
diff --git a/include/net/caif/cfsrvl.h b/include/net/caif/cfsrvl.h
deleted file mode 100644
index a000dc45f966..000000000000
--- a/include/net/caif/cfsrvl.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#ifndef CFSRVL_H_
-#define CFSRVL_H_
-#include <linux/list.h>
-#include <linux/stddef.h>
-#include <linux/types.h>
-#include <linux/kref.h>
-#include <linux/rculist.h>
-
-struct cfsrvl {
- struct cflayer layer;
- bool open;
- bool phy_flow_on;
- bool modem_flow_on;
- bool supports_flowctrl;
- void (*release)(struct cflayer *layer);
- struct dev_info dev_info;
- void (*hold)(struct cflayer *lyr);
- void (*put)(struct cflayer *lyr);
- struct rcu_head rcu;
-};
-
-struct cflayer *cfvei_create(u8 linkid, struct dev_info *dev_info);
-struct cflayer *cfdgml_create(u8 linkid, struct dev_info *dev_info);
-struct cflayer *cfutill_create(u8 linkid, struct dev_info *dev_info);
-struct cflayer *cfvidl_create(u8 linkid, struct dev_info *dev_info);
-struct cflayer *cfrfml_create(u8 linkid, struct dev_info *dev_info,
- int mtu_size);
-struct cflayer *cfdbgl_create(u8 linkid, struct dev_info *dev_info);
-
-bool cfsrvl_phyid_match(struct cflayer *layer, int phyid);
-
-void cfsrvl_init(struct cfsrvl *service,
- u8 channel_id,
- struct dev_info *dev_info,
- bool supports_flowctrl);
-bool cfsrvl_ready(struct cfsrvl *service, int *err);
-
-static inline void cfsrvl_get(struct cflayer *layr)
-{
- struct cfsrvl *s = container_of(layr, struct cfsrvl, layer);
- if (layr == NULL || layr->up == NULL || s->hold == NULL)
- return;
-
- s->hold(layr->up);
-}
-
-static inline void cfsrvl_put(struct cflayer *layr)
-{
- struct cfsrvl *s = container_of(layr, struct cfsrvl, layer);
- if (layr == NULL || layr->up == NULL || s->hold == NULL)
- return;
-
- s->put(layr->up);
-}
-#endif /* CFSRVL_H_ */
diff --git a/include/uapi/linux/caif/caif_socket.h b/include/uapi/linux/caif/caif_socket.h
deleted file mode 100644
index d9970bbaa156..000000000000
--- a/include/uapi/linux/caif/caif_socket.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/* linux/caif_socket.h
- * CAIF Definitions for CAIF socket and network layer
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#ifndef _LINUX_CAIF_SOCKET_H
-#define _LINUX_CAIF_SOCKET_H
-
-#include <linux/types.h>
-#include <linux/socket.h>
-
-/**
- * enum caif_link_selector - Physical Link Selection.
- * @CAIF_LINK_HIGH_BANDW: Physical interface for high-bandwidth
- * traffic.
- * @CAIF_LINK_LOW_LATENCY: Physical interface for low-latency
- * traffic.
- *
- * CAIF Link Layers can register their link properties.
- * This enum is used for choosing between CAIF Link Layers when
- * setting up CAIF Channels when multiple CAIF Link Layers exists.
- */
-enum caif_link_selector {
- CAIF_LINK_HIGH_BANDW,
- CAIF_LINK_LOW_LATENCY
-};
-
-/**
- * enum caif_channel_priority - CAIF channel priorities.
- *
- * @CAIF_PRIO_MIN: Min priority for a channel.
- * @CAIF_PRIO_LOW: Low-priority channel.
- * @CAIF_PRIO_NORMAL: Normal/default priority level.
- * @CAIF_PRIO_HIGH: High priority level
- * @CAIF_PRIO_MAX: Max priority for channel
- *
- * Priority can be set on CAIF Channels in order to
- * prioritize between traffic on different CAIF Channels.
- * These priority levels are recommended, but the priority value
- * is not restricted to the values defined in this enum, any value
- * between CAIF_PRIO_MIN and CAIF_PRIO_MAX could be used.
- */
-enum caif_channel_priority {
- CAIF_PRIO_MIN = 0x01,
- CAIF_PRIO_LOW = 0x04,
- CAIF_PRIO_NORMAL = 0x0f,
- CAIF_PRIO_HIGH = 0x14,
- CAIF_PRIO_MAX = 0x1F
-};
-
-/**
- * enum caif_protocol_type - CAIF Channel type.
- * @CAIFPROTO_AT: Classic AT channel.
- * @CAIFPROTO_DATAGRAM: Datagram channel.
- * @CAIFPROTO_DATAGRAM_LOOP: Datagram loopback channel, used for testing.
- * @CAIFPROTO_UTIL: Utility (Psock) channel.
- * @CAIFPROTO_RFM: Remote File Manager
- * @CAIFPROTO_DEBUG: Debug link
- *
- * This enum defines the CAIF Channel type to be used. This defines
- * the service to connect to on the modem.
- */
-enum caif_protocol_type {
- CAIFPROTO_AT,
- CAIFPROTO_DATAGRAM,
- CAIFPROTO_DATAGRAM_LOOP,
- CAIFPROTO_UTIL,
- CAIFPROTO_RFM,
- CAIFPROTO_DEBUG,
- _CAIFPROTO_MAX
-};
-#define CAIFPROTO_MAX _CAIFPROTO_MAX
-
-/**
- * enum caif_at_type - AT Service Endpoint
- * @CAIF_ATTYPE_PLAIN: Connects to a plain vanilla AT channel.
- */
-enum caif_at_type {
- CAIF_ATTYPE_PLAIN = 2
-};
- /**
- * enum caif_debug_type - Content selection for debug connection
- * @CAIF_DEBUG_TRACE_INTERACTIVE: Connection will contain
- * both trace and interactive debug.
- * @CAIF_DEBUG_TRACE: Connection contains trace only.
- * @CAIF_DEBUG_INTERACTIVE: Connection to interactive debug.
- */
-enum caif_debug_type {
- CAIF_DEBUG_TRACE_INTERACTIVE = 0,
- CAIF_DEBUG_TRACE,
- CAIF_DEBUG_INTERACTIVE,
-};
-
-/**
- * enum caif_debug_service - Debug Service Endpoint
- * @CAIF_RADIO_DEBUG_SERVICE: Debug service on the Radio sub-system
- * @CAIF_APP_DEBUG_SERVICE: Debug for the applications sub-system
- */
-enum caif_debug_service {
- CAIF_RADIO_DEBUG_SERVICE = 1,
- CAIF_APP_DEBUG_SERVICE
-};
-
-/**
- * struct sockaddr_caif - the sockaddr structure for CAIF sockets.
- * @family: Address family number, must be AF_CAIF.
- * @u: Union of address data 'switched' by family.
- * :
- * @u.at: Applies when family = CAIFPROTO_AT.
- *
- * @u.at.type: Type of AT link to set up (enum caif_at_type).
- *
- * @u.util: Applies when family = CAIFPROTO_UTIL
- *
- * @u.util.service: Utility service name.
- *
- * @u.dgm: Applies when family = CAIFPROTO_DATAGRAM
- *
- * @u.dgm.connection_id: Datagram connection id.
- *
- * @u.dgm.nsapi: NSAPI of the PDP-Context.
- *
- * @u.rfm: Applies when family = CAIFPROTO_RFM
- *
- * @u.rfm.connection_id: Connection ID for RFM.
- *
- * @u.rfm.volume: Volume to mount.
- *
- * @u.dbg: Applies when family = CAIFPROTO_DEBUG.
- *
- * @u.dbg.type: Type of debug connection to set up
- * (caif_debug_type).
- *
- * @u.dbg.service: Service sub-system to connect (caif_debug_service
- * Description:
- * This structure holds the connect parameters used for setting up a
- * CAIF Channel. It defines the service to connect to on the modem.
- */
-struct sockaddr_caif {
- __kernel_sa_family_t family;
- union {
- struct {
- __u8 type; /* type: enum caif_at_type */
- } at; /* CAIFPROTO_AT */
- struct {
- char service[16];
- } util; /* CAIFPROTO_UTIL */
- union {
- __u32 connection_id;
- __u8 nsapi;
- } dgm; /* CAIFPROTO_DATAGRAM(_LOOP)*/
- struct {
- __u32 connection_id;
- char volume[16];
- } rfm; /* CAIFPROTO_RFM */
- struct {
- __u8 type; /* type:enum caif_debug_type */
- __u8 service; /* service:caif_debug_service */
- } dbg; /* CAIFPROTO_DEBUG */
- } u;
-};
-
-/**
- * enum caif_socket_opts - CAIF option values for getsockopt and setsockopt.
- *
- * @CAIFSO_LINK_SELECT: Selector used if multiple CAIF Link layers are
- * available. Either a high bandwidth
- * link can be selected (CAIF_LINK_HIGH_BANDW) or
- * a low latency link (CAIF_LINK_LOW_LATENCY).
- * This option is of type __u32.
- * Alternatively SO_BINDTODEVICE can be used.
- *
- * @CAIFSO_REQ_PARAM: Used to set the request parameters for a
- * utility channel. (maximum 256 bytes). This
- * option must be set before connecting.
- *
- * @CAIFSO_RSP_PARAM: Gets the response parameters for a utility
- * channel. (maximum 256 bytes). This option
- * is valid after a successful connect.
- *
- *
- * This enum defines the CAIF Socket options to be used on a socket
- * of type PF_CAIF.
- *
- */
-enum caif_socket_opts {
- CAIFSO_LINK_SELECT = 127,
- CAIFSO_REQ_PARAM = 128,
- CAIFSO_RSP_PARAM = 129,
-};
-
-#endif /* _LINUX_CAIF_SOCKET_H */
diff --git a/include/uapi/linux/caif/if_caif.h b/include/uapi/linux/caif/if_caif.h
deleted file mode 100644
index 74bca19403fa..000000000000
--- a/include/uapi/linux/caif/if_caif.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- * License terms: GNU General Public License (GPL) version 2
- */
-
-#ifndef IF_CAIF_H_
-#define IF_CAIF_H_
-#include <linux/sockios.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-
-/**
- * enum ifla_caif - CAIF NetlinkRT parameters.
- * @IFLA_CAIF_IPV4_CONNID: Connection ID for IPv4 PDP Context.
- * The type of attribute is NLA_U32.
- * @IFLA_CAIF_IPV6_CONNID: Connection ID for IPv6 PDP Context.
- * The type of attribute is NLA_U32.
- * @IFLA_CAIF_LOOPBACK: If different from zero, device is doing loopback
- * The type of attribute is NLA_U8.
- *
- * When using RT Netlink to create, destroy or configure a CAIF IP interface,
- * enum ifla_caif is used to specify the configuration attributes.
- */
-enum ifla_caif {
- __IFLA_CAIF_UNSPEC,
- IFLA_CAIF_IPV4_CONNID,
- IFLA_CAIF_IPV6_CONNID,
- IFLA_CAIF_LOOPBACK,
- __IFLA_CAIF_MAX
-};
-#define IFLA_CAIF_MAX (__IFLA_CAIF_MAX-1)
-
-#endif /*IF_CAIF_H_*/
diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c
deleted file mode 100644
index 1873d8287bb9..000000000000
--- a/drivers/net/caif/caif_serial.c
+++ /dev/null
@@ -1,443 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#include <linux/hardirq.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/types.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/tty.h>
-#include <linux/file.h>
-#include <linux/if_arp.h>
-#include <net/caif/caif_device.h>
-#include <net/caif/cfcnfg.h>
-#include <linux/err.h>
-#include <linux/debugfs.h>
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Sjur Brendeland");
-MODULE_DESCRIPTION("CAIF serial device TTY line discipline");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_LDISC(N_CAIF);
-
-#define SEND_QUEUE_LOW 10
-#define SEND_QUEUE_HIGH 100
-#define CAIF_SENDING 1 /* Bit 1 = 0x02*/
-#define CAIF_FLOW_OFF_SENT 4 /* Bit 4 = 0x10 */
-#define MAX_WRITE_CHUNK 4096
-#define ON 1
-#define OFF 0
-#define CAIF_MAX_MTU 4096
-
-static DEFINE_SPINLOCK(ser_lock);
-static LIST_HEAD(ser_list);
-static LIST_HEAD(ser_release_list);
-
-static bool ser_loop;
-module_param(ser_loop, bool, 0444);
-MODULE_PARM_DESC(ser_loop, "Run in simulated loopback mode.");
-
-static bool ser_use_stx = true;
-module_param(ser_use_stx, bool, 0444);
-MODULE_PARM_DESC(ser_use_stx, "STX enabled or not.");
-
-static bool ser_use_fcs = true;
-
-module_param(ser_use_fcs, bool, 0444);
-MODULE_PARM_DESC(ser_use_fcs, "FCS enabled or not.");
-
-static int ser_write_chunk = MAX_WRITE_CHUNK;
-module_param(ser_write_chunk, int, 0444);
-
-MODULE_PARM_DESC(ser_write_chunk, "Maximum size of data written to UART.");
-
-static struct dentry *debugfsdir;
-
-static int caif_net_open(struct net_device *dev);
-static int caif_net_close(struct net_device *dev);
-
-struct ser_device {
- struct caif_dev_common common;
- struct list_head node;
- struct net_device *dev;
- struct sk_buff_head head;
- struct tty_struct *tty;
- bool tx_started;
- unsigned long state;
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debugfs_tty_dir;
- struct debugfs_blob_wrapper tx_blob;
- struct debugfs_blob_wrapper rx_blob;
- u8 rx_data[128];
- u8 tx_data[128];
- u8 tty_status;
-
-#endif
-};
-
-static void caifdev_setup(struct net_device *dev);
-static void ldisc_tx_wakeup(struct tty_struct *tty);
-#ifdef CONFIG_DEBUG_FS
-static inline void update_tty_status(struct ser_device *ser)
-{
- ser->tty_status =
- ser->tty->flow.stopped << 5 |
- ser->tty->flow.tco_stopped << 3 |
- ser->tty->ctrl.packet << 2;
-}
-static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
-{
- ser->debugfs_tty_dir = debugfs_create_dir(tty->name, debugfsdir);
-
- debugfs_create_blob("last_tx_msg", 0400, ser->debugfs_tty_dir,
- &ser->tx_blob);
-
- debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir,
- &ser->rx_blob);
-
- debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir,
- &ser->state);
-
- debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir,
- &ser->tty_status);
-
- ser->tx_blob.data = ser->tx_data;
- ser->tx_blob.size = 0;
- ser->rx_blob.data = ser->rx_data;
- ser->rx_blob.size = 0;
-}
-
-static inline void debugfs_deinit(struct ser_device *ser)
-{
- debugfs_remove_recursive(ser->debugfs_tty_dir);
-}
-
-static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size)
-{
- if (size > sizeof(ser->rx_data))
- size = sizeof(ser->rx_data);
- memcpy(ser->rx_data, data, size);
- ser->rx_blob.data = ser->rx_data;
- ser->rx_blob.size = size;
-}
-#else
-static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
-{
-}
-
-static inline void debugfs_deinit(struct ser_device *ser)
-{
-}
-
-static inline void update_tty_status(struct ser_device *ser)
-{
-}
-
-static inline void debugfs_rx(struct ser_device *ser, const u8 *data, int size)
-{
-}
-#endif
-
-static void ldisc_receive(struct tty_struct *tty, const u8 *data,
- const u8 *flags, size_t count)
-{
- struct sk_buff *skb = NULL;
- struct ser_device *ser;
- int ret;
-
- ser = tty->disc_data;
-
- /*
- * NOTE: flags may contain information about break or overrun.
- * This is not yet handled.
- */
-
-
- /*
- * Workaround for garbage at start of transmission,
- * only enable if STX handling is not enabled.
- */
- if (!ser->common.use_stx && !ser->tx_started) {
- dev_info(&ser->dev->dev,
- "Bytes received before initial transmission -"
- "bytes discarded.\n");
- return;
- }
-
- BUG_ON(ser->dev == NULL);
-
- /* Get a suitable caif packet and copy in data. */
- skb = netdev_alloc_skb(ser->dev, count+1);
- if (skb == NULL)
- return;
- skb_put_data(skb, data, count);
-
- skb->protocol = htons(ETH_P_CAIF);
- skb_reset_mac_header(skb);
- debugfs_rx(ser, data, count);
- /* Push received packet up the stack. */
- ret = netif_rx(skb);
- if (!ret) {
- ser->dev->stats.rx_packets++;
- ser->dev->stats.rx_bytes += count;
- } else
- ++ser->dev->stats.rx_dropped;
- update_tty_status(ser);
-}
-
-static int handle_tx(struct ser_device *ser)
-{
- struct tty_struct *tty;
- struct sk_buff *skb;
- int tty_wr, len, room;
-
- tty = ser->tty;
- ser->tx_started = true;
-
- /* Enter critical section */
- if (test_and_set_bit(CAIF_SENDING, &ser->state))
- return 0;
-
- /* skb_peek is safe because handle_tx is called after skb_queue_tail */
- while ((skb = skb_peek(&ser->head)) != NULL) {
-
- /* Make sure you don't write too much */
- len = skb->len;
- room = tty_write_room(tty);
- if (!room)
- break;
- if (room > ser_write_chunk)
- room = ser_write_chunk;
- if (len > room)
- len = room;
-
- /* Write to tty or loopback */
- if (!ser_loop) {
- tty_wr = tty->ops->write(tty, skb->data, len);
- update_tty_status(ser);
- } else {
- tty_wr = len;
- ldisc_receive(tty, skb->data, NULL, len);
- }
- ser->dev->stats.tx_packets++;
- ser->dev->stats.tx_bytes += tty_wr;
-
- /* Error on TTY ?! */
- if (tty_wr < 0)
- goto error;
- /* Reduce buffer written, and discard if empty */
- skb_pull(skb, tty_wr);
- if (skb->len == 0) {
- struct sk_buff *tmp = skb_dequeue(&ser->head);
- WARN_ON(tmp != skb);
- dev_consume_skb_any(skb);
- }
- }
- /* Send flow off if queue is empty */
- if (ser->head.qlen <= SEND_QUEUE_LOW &&
- test_and_clear_bit(CAIF_FLOW_OFF_SENT, &ser->state) &&
- ser->common.flowctrl != NULL)
- ser->common.flowctrl(ser->dev, ON);
- clear_bit(CAIF_SENDING, &ser->state);
- return 0;
-error:
- clear_bit(CAIF_SENDING, &ser->state);
- return tty_wr;
-}
-
-static netdev_tx_t caif_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ser_device *ser;
-
- ser = netdev_priv(dev);
-
- /* Send flow off once, on high water mark */
- if (ser->head.qlen > SEND_QUEUE_HIGH &&
- !test_and_set_bit(CAIF_FLOW_OFF_SENT, &ser->state) &&
- ser->common.flowctrl != NULL)
-
- ser->common.flowctrl(ser->dev, OFF);
-
- skb_queue_tail(&ser->head, skb);
- return handle_tx(ser);
-}
-
-
-static void ldisc_tx_wakeup(struct tty_struct *tty)
-{
- struct ser_device *ser;
-
- ser = tty->disc_data;
- BUG_ON(ser == NULL);
- WARN_ON(ser->tty != tty);
- handle_tx(ser);
-}
-
-
-static void ser_release(struct work_struct *work)
-{
- struct list_head list;
- struct ser_device *ser, *tmp;
- struct tty_struct *tty;
-
- spin_lock(&ser_lock);
- list_replace_init(&ser_release_list, &list);
- spin_unlock(&ser_lock);
-
- if (!list_empty(&list)) {
- rtnl_lock();
- list_for_each_entry_safe(ser, tmp, &list, node) {
- tty = ser->tty;
- dev_close(ser->dev);
- unregister_netdevice(ser->dev);
- debugfs_deinit(ser);
- tty_kref_put(tty->link);
- tty_kref_put(tty);
- }
- rtnl_unlock();
- }
-}
-
-static DECLARE_WORK(ser_release_work, ser_release);
-
-static int ldisc_open(struct tty_struct *tty)
-{
- struct ser_device *ser;
- struct net_device *dev;
- char name[64];
- int result;
-
- /* No write no play */
- if (tty->ops->write == NULL)
- return -EOPNOTSUPP;
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_TTY_CONFIG))
- return -EPERM;
-
- /* release devices to avoid name collision */
- ser_release(NULL);
-
- result = snprintf(name, sizeof(name), "cf%s", tty->name);
- if (result >= IFNAMSIZ)
- return -EINVAL;
- dev = alloc_netdev(sizeof(*ser), name, NET_NAME_UNKNOWN,
- caifdev_setup);
- if (!dev)
- return -ENOMEM;
-
- ser = netdev_priv(dev);
- ser->tty = tty_kref_get(tty);
- tty_kref_get(tty->link);
- ser->dev = dev;
- debugfs_init(ser, tty);
- tty->receive_room = 4096;
- tty->disc_data = ser;
- set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
- rtnl_lock();
- result = register_netdevice(dev);
- if (result) {
- tty_kref_put(tty->link);
- tty_kref_put(tty);
- rtnl_unlock();
- free_netdev(dev);
- return -ENODEV;
- }
-
- spin_lock(&ser_lock);
- list_add(&ser->node, &ser_list);
- spin_unlock(&ser_lock);
- rtnl_unlock();
- netif_stop_queue(dev);
- update_tty_status(ser);
- return 0;
-}
-
-static void ldisc_close(struct tty_struct *tty)
-{
- struct ser_device *ser = tty->disc_data;
-
- spin_lock(&ser_lock);
- list_move(&ser->node, &ser_release_list);
- spin_unlock(&ser_lock);
- schedule_work(&ser_release_work);
-}
-
-/* The line discipline structure. */
-static struct tty_ldisc_ops caif_ldisc = {
- .owner = THIS_MODULE,
- .num = N_CAIF,
- .name = "n_caif",
- .open = ldisc_open,
- .close = ldisc_close,
- .receive_buf = ldisc_receive,
- .write_wakeup = ldisc_tx_wakeup
-};
-
-static const struct net_device_ops netdev_ops = {
- .ndo_open = caif_net_open,
- .ndo_stop = caif_net_close,
- .ndo_start_xmit = caif_xmit
-};
-
-static void caifdev_setup(struct net_device *dev)
-{
- struct ser_device *serdev = netdev_priv(dev);
-
- dev->features = 0;
- dev->netdev_ops = &netdev_ops;
- dev->type = ARPHRD_CAIF;
- dev->flags = IFF_POINTOPOINT | IFF_NOARP;
- dev->mtu = CAIF_MAX_MTU;
- dev->priv_flags |= IFF_NO_QUEUE;
- dev->needs_free_netdev = true;
- skb_queue_head_init(&serdev->head);
- serdev->common.link_select = CAIF_LINK_LOW_LATENCY;
- serdev->common.use_frag = true;
- serdev->common.use_stx = ser_use_stx;
- serdev->common.use_fcs = ser_use_fcs;
- serdev->dev = dev;
-}
-
-
-static int caif_net_open(struct net_device *dev)
-{
- netif_wake_queue(dev);
- return 0;
-}
-
-static int caif_net_close(struct net_device *dev)
-{
- netif_stop_queue(dev);
- return 0;
-}
-
-static int __init caif_ser_init(void)
-{
- int ret;
-
- ret = tty_register_ldisc(&caif_ldisc);
- if (ret < 0)
- pr_err("cannot register CAIF ldisc=%d err=%d\n", N_CAIF, ret);
-
- debugfsdir = debugfs_create_dir("caif_serial", NULL);
- return ret;
-}
-
-static void __exit caif_ser_exit(void)
-{
- spin_lock(&ser_lock);
- list_splice(&ser_list, &ser_release_list);
- spin_unlock(&ser_lock);
- ser_release(NULL);
- cancel_work_sync(&ser_release_work);
- tty_unregister_ldisc(&caif_ldisc);
- debugfs_remove_recursive(debugfsdir);
-}
-
-module_init(caif_ser_init);
-module_exit(caif_ser_exit);
diff --git a/drivers/net/caif/caif_virtio.c b/drivers/net/caif/caif_virtio.c
deleted file mode 100644
index 8ac1a4b8e055..000000000000
--- a/drivers/net/caif/caif_virtio.c
+++ /dev/null
@@ -1,791 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2013
- * Authors: Vicram Arv
- * Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
- * Sjur Brendeland
- */
-#include <linux/module.h>
-#include <linux/if_arp.h>
-#include <linux/virtio.h>
-#include <linux/vringh.h>
-#include <linux/debugfs.h>
-#include <linux/spinlock.h>
-#include <linux/genalloc.h>
-#include <linux/interrupt.h>
-#include <linux/netdevice.h>
-#include <linux/rtnetlink.h>
-#include <linux/virtio_ids.h>
-#include <linux/virtio_caif.h>
-#include <linux/virtio_ring.h>
-#include <linux/dma-mapping.h>
-#include <net/caif/caif_dev.h>
-#include <linux/virtio_config.h>
-
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Vicram Arv");
-MODULE_AUTHOR("Sjur Brendeland");
-MODULE_DESCRIPTION("Virtio CAIF Driver");
-
-/* NAPI schedule quota */
-#define CFV_DEFAULT_QUOTA 32
-
-/* Defaults used if virtio config space is unavailable */
-#define CFV_DEF_MTU_SIZE 4096
-#define CFV_DEF_HEADROOM 32
-#define CFV_DEF_TAILROOM 32
-
-/* Required IP header alignment */
-#define IP_HDR_ALIGN 4
-
-/* struct cfv_napi_contxt - NAPI context info
- * @riov: IOV holding data read from the ring. Note that riov may
- * still hold data when cfv_rx_poll() returns.
- * @head: Last descriptor ID we received from vringh_getdesc_kern.
- * We use this to put descriptor back on the used ring. USHRT_MAX is
- * used to indicate invalid head-id.
- */
-struct cfv_napi_context {
- struct vringh_kiov riov;
- unsigned short head;
-};
-
-/* struct cfv_stats - statistics for debugfs
- * @rx_napi_complete: Number of NAPI completions (RX)
- * @rx_napi_resched: Number of calls where the full quota was used (RX)
- * @rx_nomem: Number of SKB alloc failures (RX)
- * @rx_kicks: Number of RX kicks
- * @tx_full_ring: Number times TX ring was full
- * @tx_no_mem: Number of times TX went out of memory
- * @tx_flow_on: Number of flow on (TX)
- * @tx_kicks: Number of TX kicks
- */
-struct cfv_stats {
- u32 rx_napi_complete;
- u32 rx_napi_resched;
- u32 rx_nomem;
- u32 rx_kicks;
- u32 tx_full_ring;
- u32 tx_no_mem;
- u32 tx_flow_on;
- u32 tx_kicks;
-};
-
-/* struct cfv_info - Caif Virtio control structure
- * @cfdev: caif common header
- * @vdev: Associated virtio device
- * @vr_rx: rx/downlink host vring
- * @vq_tx: tx/uplink virtqueue
- * @ndev: CAIF link layer device
- * @watermark_tx: indicates number of free descriptors we need
- * to reopen the tx-queues after overload.
- * @tx_lock: protects vq_tx from concurrent use
- * @tx_release_tasklet: Tasklet for freeing consumed TX buffers
- * @napi: Napi context used in cfv_rx_poll()
- * @ctx: Context data used in cfv_rx_poll()
- * @tx_hr: transmit headroom
- * @rx_hr: receive headroom
- * @tx_tr: transmit tail room
- * @rx_tr: receive tail room
- * @mtu: transmit max size
- * @mru: receive max size
- * @allocsz: size of dma memory reserved for TX buffers
- * @alloc_addr: virtual address to dma memory for TX buffers
- * @alloc_dma: dma address to dma memory for TX buffers
- * @genpool: Gen Pool used for allocating TX buffers
- * @reserved_mem: Pointer to memory reserve allocated from genpool
- * @reserved_size: Size of memory reserve allocated from genpool
- * @stats: Statistics exposed in sysfs
- * @debugfs: Debugfs dentry for statistic counters
- */
-struct cfv_info {
- struct caif_dev_common cfdev;
- struct virtio_device *vdev;
- struct vringh *vr_rx;
- struct virtqueue *vq_tx;
- struct net_device *ndev;
- unsigned int watermark_tx;
- /* Protect access to vq_tx */
- spinlock_t tx_lock;
- struct tasklet_struct tx_release_tasklet;
- struct napi_struct napi;
- struct cfv_napi_context ctx;
- u16 tx_hr;
- u16 rx_hr;
- u16 tx_tr;
- u16 rx_tr;
- u32 mtu;
- u32 mru;
- size_t allocsz;
- void *alloc_addr;
- dma_addr_t alloc_dma;
- struct gen_pool *genpool;
- unsigned long reserved_mem;
- size_t reserved_size;
- struct cfv_stats stats;
- struct dentry *debugfs;
-};
-
-/* struct buf_info - maintains transmit buffer data handle
- * @size: size of transmit buffer
- * @dma_handle: handle to allocated dma device memory area
- * @vaddr: virtual address mapping to allocated memory area
- */
-struct buf_info {
- size_t size;
- u8 *vaddr;
-};
-
-/* Called from virtio device, in IRQ context */
-static void cfv_release_cb(struct virtqueue *vq_tx)
-{
- struct cfv_info *cfv = vq_tx->vdev->priv;
-
- ++cfv->stats.tx_kicks;
- tasklet_schedule(&cfv->tx_release_tasklet);
-}
-
-static void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info)
-{
- if (!buf_info)
- return;
- gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr,
- buf_info->size);
- kfree(buf_info);
-}
-
-/* This is invoked whenever the remote processor completed processing
- * a TX msg we just sent, and the buffer is put back to the used ring.
- */
-static void cfv_release_used_buf(struct virtqueue *vq_tx)
-{
- struct cfv_info *cfv = vq_tx->vdev->priv;
- unsigned long flags;
-
- BUG_ON(vq_tx != cfv->vq_tx);
-
- for (;;) {
- unsigned int len;
- struct buf_info *buf_info;
-
- /* Get used buffer from used ring to recycle used descriptors */
- spin_lock_irqsave(&cfv->tx_lock, flags);
- buf_info = virtqueue_get_buf(vq_tx, &len);
- spin_unlock_irqrestore(&cfv->tx_lock, flags);
-
- /* Stop looping if there are no more buffers to free */
- if (!buf_info)
- break;
-
- free_buf_info(cfv, buf_info);
-
- /* watermark_tx indicates if we previously stopped the tx
- * queues. If we have enough free stots in the virtio ring,
- * re-establish memory reserved and open up tx queues.
- */
- if (cfv->vq_tx->num_free <= cfv->watermark_tx)
- continue;
-
- /* Re-establish memory reserve */
- if (cfv->reserved_mem == 0 && cfv->genpool)
- cfv->reserved_mem =
- gen_pool_alloc(cfv->genpool,
- cfv->reserved_size);
-
- /* Open up the tx queues */
- if (cfv->reserved_mem) {
- cfv->watermark_tx =
- virtqueue_get_vring_size(cfv->vq_tx);
- netif_tx_wake_all_queues(cfv->ndev);
- /* Buffers are recycled in cfv_netdev_tx, so
- * disable notifications when queues are opened.
- */
- virtqueue_disable_cb(cfv->vq_tx);
- ++cfv->stats.tx_flow_on;
- } else {
- /* if no memory reserve, wait for more free slots */
- WARN_ON(cfv->watermark_tx >
- virtqueue_get_vring_size(cfv->vq_tx));
- cfv->watermark_tx +=
- virtqueue_get_vring_size(cfv->vq_tx) / 4;
- }
- }
-}
-
-/* Allocate a SKB and copy packet data to it */
-static struct sk_buff *cfv_alloc_and_copy_skb(int *err,
- struct cfv_info *cfv,
- u8 *frm, u32 frm_len)
-{
- struct sk_buff *skb;
- u32 cfpkt_len, pad_len;
-
- *err = 0;
- /* Verify that packet size with down-link header and mtu size */
- if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) {
- netdev_err(cfv->ndev,
- "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n",
- frm_len, cfv->mru, cfv->rx_hr,
- cfv->rx_tr);
- *err = -EPROTO;
- return NULL;
- }
-
- cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr);
- pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1);
-
- skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len);
- if (!skb) {
- *err = -ENOMEM;
- return NULL;
- }
-
- skb_reserve(skb, cfv->rx_hr + pad_len);
-
- skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len);
- return skb;
-}
-
-/* Get packets from the host vring */
-static int cfv_rx_poll(struct napi_struct *napi, int quota)
-{
- struct cfv_info *cfv = container_of(napi, struct cfv_info, napi);
- int rxcnt = 0;
- int err = 0;
- void *buf;
- struct sk_buff *skb;
- struct vringh_kiov *riov = &cfv->ctx.riov;
- unsigned int skb_len;
-
- do {
- skb = NULL;
-
- /* Put the previous iovec back on the used ring and
- * fetch a new iovec if we have processed all elements.
- */
- if (riov->i == riov->used) {
- if (cfv->ctx.head != USHRT_MAX) {
- vringh_complete_kern(cfv->vr_rx,
- cfv->ctx.head,
- 0);
- cfv->ctx.head = USHRT_MAX;
- }
-
- err = vringh_getdesc_kern(
- cfv->vr_rx,
- riov,
- NULL,
- &cfv->ctx.head,
- GFP_ATOMIC);
-
- if (err <= 0)
- goto exit;
- }
-
- buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base);
- /* TODO: Add check on valid buffer address */
-
- skb = cfv_alloc_and_copy_skb(&err, cfv, buf,
- riov->iov[riov->i].iov_len);
- if (unlikely(err))
- goto exit;
-
- /* Push received packet up the stack. */
- skb_len = skb->len;
- skb->protocol = htons(ETH_P_CAIF);
- skb_reset_mac_header(skb);
- skb->dev = cfv->ndev;
- err = netif_receive_skb(skb);
- if (unlikely(err)) {
- ++cfv->ndev->stats.rx_dropped;
- } else {
- ++cfv->ndev->stats.rx_packets;
- cfv->ndev->stats.rx_bytes += skb_len;
- }
-
- ++riov->i;
- ++rxcnt;
- } while (rxcnt < quota);
-
- ++cfv->stats.rx_napi_resched;
- goto out;
-
-exit:
- switch (err) {
- case 0:
- ++cfv->stats.rx_napi_complete;
-
- /* Really out of packets? (stolen from virtio_net)*/
- napi_complete(napi);
- if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) &&
- napi_schedule_prep(napi)) {
- vringh_notify_disable_kern(cfv->vr_rx);
- __napi_schedule(napi);
- }
- break;
-
- case -ENOMEM:
- ++cfv->stats.rx_nomem;
- dev_kfree_skb(skb);
- /* Stop NAPI poll on OOM, we hope to be polled later */
- napi_complete(napi);
- vringh_notify_enable_kern(cfv->vr_rx);
- break;
-
- default:
- /* We're doomed, any modem fault is fatal */
- netdev_warn(cfv->ndev, "Bad ring, disable device\n");
- cfv->ndev->stats.rx_dropped = riov->used - riov->i;
- napi_complete(napi);
- vringh_notify_disable_kern(cfv->vr_rx);
- netif_carrier_off(cfv->ndev);
- break;
- }
-out:
- if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0)
- vringh_notify(cfv->vr_rx);
- return rxcnt;
-}
-
-static void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx)
-{
- struct cfv_info *cfv = vdev->priv;
-
- ++cfv->stats.rx_kicks;
- vringh_notify_disable_kern(cfv->vr_rx);
- napi_schedule(&cfv->napi);
-}
-
-static void cfv_destroy_genpool(struct cfv_info *cfv)
-{
- if (cfv->alloc_addr)
- dma_free_coherent(cfv->vdev->dev.parent->parent,
- cfv->allocsz, cfv->alloc_addr,
- cfv->alloc_dma);
-
- if (!cfv->genpool)
- return;
- gen_pool_free(cfv->genpool, cfv->reserved_mem,
- cfv->reserved_size);
- gen_pool_destroy(cfv->genpool);
- cfv->genpool = NULL;
-}
-
-static int cfv_create_genpool(struct cfv_info *cfv)
-{
- int err;
-
- /* dma_alloc can only allocate whole pages, and we need a more
- * fine graned allocation so we use genpool. We ask for space needed
- * by IP and a full ring. If the dma allcoation fails we retry with a
- * smaller allocation size.
- */
- err = -ENOMEM;
- cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) *
- (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10;
- if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu)
- return -EINVAL;
-
- for (;;) {
- if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) {
- netdev_info(cfv->ndev, "Not enough device memory\n");
- return -ENOMEM;
- }
-
- cfv->alloc_addr = dma_alloc_coherent(
- cfv->vdev->dev.parent->parent,
- cfv->allocsz, &cfv->alloc_dma,
- GFP_ATOMIC);
- if (cfv->alloc_addr)
- break;
-
- cfv->allocsz = (cfv->allocsz * 3) >> 2;
- }
-
- netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n",
- cfv->allocsz);
-
- /* Allocate on 128 bytes boundaries (1 << 7)*/
- cfv->genpool = gen_pool_create(7, -1);
- if (!cfv->genpool)
- goto err;
-
- err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr,
- (phys_addr_t)virt_to_phys(cfv->alloc_addr),
- cfv->allocsz, -1);
- if (err)
- goto err;
-
- /* Reserve some memory for low memory situations. If we hit the roof
- * in the memory pool, we stop TX flow and release the reserve.
- */
- cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu;
- cfv->reserved_mem = gen_pool_alloc(cfv->genpool,
- cfv->reserved_size);
- if (!cfv->reserved_mem) {
- err = -ENOMEM;
- goto err;
- }
-
- cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx);
- return 0;
-err:
- cfv_destroy_genpool(cfv);
- return err;
-}
-
-/* Enable the CAIF interface and allocate the memory-pool */
-static int cfv_netdev_open(struct net_device *netdev)
-{
- struct cfv_info *cfv = netdev_priv(netdev);
-
- if (cfv_create_genpool(cfv))
- return -ENOMEM;
-
- netif_carrier_on(netdev);
- napi_enable(&cfv->napi);
-
- /* Schedule NAPI to read any pending packets */
- napi_schedule(&cfv->napi);
- return 0;
-}
-
-/* Disable the CAIF interface and free the memory-pool */
-static int cfv_netdev_close(struct net_device *netdev)
-{
- struct cfv_info *cfv = netdev_priv(netdev);
- unsigned long flags;
- struct buf_info *buf_info;
-
- /* Disable interrupts, queues and NAPI polling */
- netif_carrier_off(netdev);
- virtqueue_disable_cb(cfv->vq_tx);
- vringh_notify_disable_kern(cfv->vr_rx);
- napi_disable(&cfv->napi);
-
- /* Release any TX buffers on both used and available rings */
- cfv_release_used_buf(cfv->vq_tx);
- spin_lock_irqsave(&cfv->tx_lock, flags);
- while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx)))
- free_buf_info(cfv, buf_info);
- spin_unlock_irqrestore(&cfv->tx_lock, flags);
-
- /* Release all dma allocated memory and destroy the pool */
- cfv_destroy_genpool(cfv);
- return 0;
-}
-
-/* Allocate a buffer in dma-memory and copy skb to it */
-static struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv,
- struct sk_buff *skb,
- struct scatterlist *sg)
-{
- struct caif_payload_info *info = (void *)&skb->cb;
- struct buf_info *buf_info = NULL;
- u8 pad_len, hdr_ofs;
-
- if (!cfv->genpool)
- goto err;
-
- if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) {
- netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n",
- cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu);
- goto err;
- }
-
- buf_info = kmalloc_obj(struct buf_info, GFP_ATOMIC);
- if (unlikely(!buf_info))
- goto err;
-
- /* Make the IP header aligned in the buffer */
- hdr_ofs = cfv->tx_hr + info->hdr_len;
- pad_len = hdr_ofs & (IP_HDR_ALIGN - 1);
- buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len;
-
- /* allocate dma memory buffer */
- buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size);
- if (unlikely(!buf_info->vaddr))
- goto err;
-
- /* copy skbuf contents to send buffer */
- skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len);
- sg_init_one(sg, buf_info->vaddr + pad_len,
- skb->len + cfv->tx_hr + cfv->rx_hr);
-
- return buf_info;
-err:
- kfree(buf_info);
- return NULL;
-}
-
-/* Put the CAIF packet on the virtio ring and kick the receiver */
-static netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev)
-{
- struct cfv_info *cfv = netdev_priv(netdev);
- struct buf_info *buf_info;
- struct scatterlist sg;
- unsigned long flags;
- bool flow_off = false;
- int ret;
-
- /* garbage collect released buffers */
- cfv_release_used_buf(cfv->vq_tx);
- spin_lock_irqsave(&cfv->tx_lock, flags);
-
- /* Flow-off check takes into account number of cpus to make sure
- * virtqueue will not be overfilled in any possible smp conditions.
- *
- * Flow-on is triggered when sufficient buffers are freed
- */
- if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) {
- flow_off = true;
- cfv->stats.tx_full_ring++;
- }
-
- /* If we run out of memory, we release the memory reserve and retry
- * allocation.
- */
- buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg);
- if (unlikely(!buf_info)) {
- cfv->stats.tx_no_mem++;
- flow_off = true;
-
- if (cfv->reserved_mem && cfv->genpool) {
- gen_pool_free(cfv->genpool, cfv->reserved_mem,
- cfv->reserved_size);
- cfv->reserved_mem = 0;
- buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg);
- }
- }
-
- if (unlikely(flow_off)) {
- /* Turn flow on when a 1/4 of the descriptors are released */
- cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4;
- /* Enable notifications of recycled TX buffers */
- virtqueue_enable_cb(cfv->vq_tx);
- netif_tx_stop_all_queues(netdev);
- }
-
- if (unlikely(!buf_info)) {
- /* If the memory reserve does it's job, this shouldn't happen */
- netdev_warn(cfv->ndev, "Out of gen_pool memory\n");
- goto err;
- }
-
- ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC);
- if (unlikely((ret < 0))) {
- /* If flow control works, this shouldn't happen */
- netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n",
- ret);
- goto err;
- }
-
- /* update netdev statistics */
- cfv->ndev->stats.tx_packets++;
- cfv->ndev->stats.tx_bytes += skb->len;
- spin_unlock_irqrestore(&cfv->tx_lock, flags);
-
- /* tell the remote processor it has a pending message to read */
- virtqueue_kick(cfv->vq_tx);
-
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-err:
- spin_unlock_irqrestore(&cfv->tx_lock, flags);
- cfv->ndev->stats.tx_dropped++;
- free_buf_info(cfv, buf_info);
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
-}
-
-static void cfv_tx_release_tasklet(struct tasklet_struct *t)
-{
- struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet);
- cfv_release_used_buf(cfv->vq_tx);
-}
-
-static const struct net_device_ops cfv_netdev_ops = {
- .ndo_open = cfv_netdev_open,
- .ndo_stop = cfv_netdev_close,
- .ndo_start_xmit = cfv_netdev_tx,
-};
-
-static void cfv_netdev_setup(struct net_device *netdev)
-{
- netdev->netdev_ops = &cfv_netdev_ops;
- netdev->type = ARPHRD_CAIF;
- netdev->tx_queue_len = 100;
- netdev->flags = IFF_POINTOPOINT | IFF_NOARP;
- netdev->mtu = CFV_DEF_MTU_SIZE;
- netdev->needs_free_netdev = true;
-}
-
-/* Create debugfs counters for the device */
-static inline void debugfs_init(struct cfv_info *cfv)
-{
- cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL);
-
- debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs,
- &cfv->stats.rx_napi_complete);
- debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs,
- &cfv->stats.rx_napi_resched);
- debugfs_create_u32("rx-nomem", 0400, cfv->debugfs,
- &cfv->stats.rx_nomem);
- debugfs_create_u32("rx-kicks", 0400, cfv->debugfs,
- &cfv->stats.rx_kicks);
- debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs,
- &cfv->stats.tx_full_ring);
- debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs,
- &cfv->stats.tx_no_mem);
- debugfs_create_u32("tx-kicks", 0400, cfv->debugfs,
- &cfv->stats.tx_kicks);
- debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs,
- &cfv->stats.tx_flow_on);
-}
-
-/* Setup CAIF for the a virtio device */
-static int cfv_probe(struct virtio_device *vdev)
-{
- vrh_callback_t *vrh_cbs = cfv_recv;
- const char *cfv_netdev_name = "cfvrt";
- struct net_device *netdev;
- struct cfv_info *cfv;
- int err;
-
- netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name,
- NET_NAME_UNKNOWN, cfv_netdev_setup);
- if (!netdev)
- return -ENOMEM;
-
- cfv = netdev_priv(netdev);
- cfv->vdev = vdev;
- cfv->ndev = netdev;
-
- spin_lock_init(&cfv->tx_lock);
-
- /* Get the RX virtio ring. This is a "host side vring". */
- err = -ENODEV;
- if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs)
- goto err;
-
- err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs);
- if (err)
- goto err;
-
- /* Get the TX virtio ring. This is a "guest side vring". */
- cfv->vq_tx = virtio_find_single_vq(vdev, cfv_release_cb, "output");
- if (IS_ERR(cfv->vq_tx)) {
- err = PTR_ERR(cfv->vq_tx);
- goto err;
- }
-
- /* Get the CAIF configuration from virtio config space, if available */
- if (vdev->config->get) {
- virtio_cread(vdev, struct virtio_caif_transf_config, headroom,
- &cfv->tx_hr);
- virtio_cread(vdev, struct virtio_caif_transf_config, headroom,
- &cfv->rx_hr);
- virtio_cread(vdev, struct virtio_caif_transf_config, tailroom,
- &cfv->tx_tr);
- virtio_cread(vdev, struct virtio_caif_transf_config, tailroom,
- &cfv->rx_tr);
- virtio_cread(vdev, struct virtio_caif_transf_config, mtu,
- &cfv->mtu);
- virtio_cread(vdev, struct virtio_caif_transf_config, mtu,
- &cfv->mru);
- } else {
- cfv->tx_hr = CFV_DEF_HEADROOM;
- cfv->rx_hr = CFV_DEF_HEADROOM;
- cfv->tx_tr = CFV_DEF_TAILROOM;
- cfv->rx_tr = CFV_DEF_TAILROOM;
- cfv->mtu = CFV_DEF_MTU_SIZE;
- cfv->mru = CFV_DEF_MTU_SIZE;
- }
-
- netdev->needed_headroom = cfv->tx_hr;
- netdev->needed_tailroom = cfv->tx_tr;
-
- /* Disable buffer release interrupts unless we have stopped TX queues */
- virtqueue_disable_cb(cfv->vq_tx);
-
- netdev->mtu = cfv->mtu - cfv->tx_tr;
- vdev->priv = cfv;
-
- /* Initialize NAPI poll context data */
- vringh_kiov_init(&cfv->ctx.riov, NULL, 0);
- cfv->ctx.head = USHRT_MAX;
- netif_napi_add_weight(netdev, &cfv->napi, cfv_rx_poll,
- CFV_DEFAULT_QUOTA);
-
- tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet);
-
- /* Carrier is off until netdevice is opened */
- netif_carrier_off(netdev);
-
- /* serialize netdev register + virtio_device_ready() with ndo_open() */
- rtnl_lock();
-
- /* register Netdev */
- err = register_netdevice(netdev);
- if (err) {
- rtnl_unlock();
- dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err);
- goto err;
- }
-
- virtio_device_ready(vdev);
-
- rtnl_unlock();
-
- debugfs_init(cfv);
-
- return 0;
-err:
- netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err);
-
- if (cfv->vr_rx)
- vdev->vringh_config->del_vrhs(cfv->vdev);
- if (cfv->vq_tx)
- vdev->config->del_vqs(cfv->vdev);
- free_netdev(netdev);
- return err;
-}
-
-static void cfv_remove(struct virtio_device *vdev)
-{
- struct cfv_info *cfv = vdev->priv;
-
- rtnl_lock();
- dev_close(cfv->ndev);
- rtnl_unlock();
-
- tasklet_kill(&cfv->tx_release_tasklet);
- debugfs_remove_recursive(cfv->debugfs);
-
- vringh_kiov_cleanup(&cfv->ctx.riov);
- virtio_reset_device(vdev);
- vdev->vringh_config->del_vrhs(cfv->vdev);
- cfv->vr_rx = NULL;
- vdev->config->del_vqs(cfv->vdev);
- unregister_netdev(cfv->ndev);
-}
-
-static struct virtio_device_id id_table[] = {
- { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID },
- { 0 },
-};
-
-static unsigned int features[] = {
-};
-
-static struct virtio_driver caif_virtio_driver = {
- .feature_table = features,
- .feature_table_size = ARRAY_SIZE(features),
- .driver.name = KBUILD_MODNAME,
- .id_table = id_table,
- .probe = cfv_probe,
- .remove = cfv_remove,
-};
-
-module_virtio_driver(caif_virtio_driver);
-MODULE_DEVICE_TABLE(virtio, id_table);
diff --git a/net/caif/caif_dev.c b/net/caif/caif_dev.c
deleted file mode 100644
index 922de3d611c0..000000000000
--- a/net/caif/caif_dev.c
+++ /dev/null
@@ -1,586 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * CAIF Interface registration.
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- *
- * Borrowed heavily from file: pn_dev.c. Thanks to Remi Denis-Courmont
- * and Sakari Ailus <sakari.ailus@nokia.com>
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/if_arp.h>
-#include <linux/net.h>
-#include <linux/netdevice.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <net/netns/generic.h>
-#include <net/net_namespace.h>
-#include <net/pkt_sched.h>
-#include <net/caif/caif_device.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/caif_dev.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cfcnfg.h>
-#include <net/caif/cfserl.h>
-
-MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol support");
-MODULE_LICENSE("GPL");
-
-/* Used for local tracking of the CAIF net devices */
-struct caif_device_entry {
- struct cflayer layer;
- struct list_head list;
- struct net_device *netdev;
- int __percpu *pcpu_refcnt;
- spinlock_t flow_lock;
- struct sk_buff *xoff_skb;
- void (*xoff_skb_dtor)(struct sk_buff *skb);
- bool xoff;
-};
-
-struct caif_device_entry_list {
- struct list_head list;
- /* Protects simulanous deletes in list */
- struct mutex lock;
-};
-
-struct caif_net {
- struct cfcnfg *cfg;
- struct caif_device_entry_list caifdevs;
-};
-
-static unsigned int caif_net_id;
-static int q_high = 50; /* Percent */
-
-struct cfcnfg *get_cfcnfg(struct net *net)
-{
- struct caif_net *caifn;
- caifn = net_generic(net, caif_net_id);
- return caifn->cfg;
-}
-EXPORT_SYMBOL(get_cfcnfg);
-
-static struct caif_device_entry_list *caif_device_list(struct net *net)
-{
- struct caif_net *caifn;
- caifn = net_generic(net, caif_net_id);
- return &caifn->caifdevs;
-}
-
-static void caifd_put(struct caif_device_entry *e)
-{
- this_cpu_dec(*e->pcpu_refcnt);
-}
-
-static void caifd_hold(struct caif_device_entry *e)
-{
- this_cpu_inc(*e->pcpu_refcnt);
-}
-
-static int caifd_refcnt_read(struct caif_device_entry *e)
-{
- int i, refcnt = 0;
- for_each_possible_cpu(i)
- refcnt += *per_cpu_ptr(e->pcpu_refcnt, i);
- return refcnt;
-}
-
-/* Allocate new CAIF device. */
-static struct caif_device_entry *caif_device_alloc(struct net_device *dev)
-{
- struct caif_device_entry *caifd;
-
- caifd = kzalloc_obj(*caifd);
- if (!caifd)
- return NULL;
- caifd->pcpu_refcnt = alloc_percpu(int);
- if (!caifd->pcpu_refcnt) {
- kfree(caifd);
- return NULL;
- }
- caifd->netdev = dev;
- dev_hold(dev);
- return caifd;
-}
-
-static struct caif_device_entry *caif_get(struct net_device *dev)
-{
- struct caif_device_entry_list *caifdevs =
- caif_device_list(dev_net(dev));
- struct caif_device_entry *caifd;
-
- list_for_each_entry_rcu(caifd, &caifdevs->list, list,
- lockdep_rtnl_is_held()) {
- if (caifd->netdev == dev)
- return caifd;
- }
- return NULL;
-}
-
-static void caif_flow_cb(struct sk_buff *skb)
-{
- struct caif_device_entry *caifd;
- void (*dtor)(struct sk_buff *skb) = NULL;
- bool send_xoff;
-
- WARN_ON(skb->dev == NULL);
-
- rcu_read_lock();
- caifd = caif_get(skb->dev);
-
- WARN_ON(caifd == NULL);
- if (!caifd) {
- rcu_read_unlock();
- return;
- }
-
- caifd_hold(caifd);
- rcu_read_unlock();
-
- spin_lock_bh(&caifd->flow_lock);
- send_xoff = caifd->xoff;
- caifd->xoff = false;
- dtor = caifd->xoff_skb_dtor;
-
- if (WARN_ON(caifd->xoff_skb != skb))
- skb = NULL;
-
- caifd->xoff_skb = NULL;
- caifd->xoff_skb_dtor = NULL;
-
- spin_unlock_bh(&caifd->flow_lock);
-
- if (dtor && skb)
- dtor(skb);
-
- if (send_xoff)
- caifd->layer.up->
- ctrlcmd(caifd->layer.up,
- _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND,
- caifd->layer.id);
- caifd_put(caifd);
-}
-
-static int transmit(struct cflayer *layer, struct cfpkt *pkt)
-{
- int err, high = 0, qlen = 0;
- struct caif_device_entry *caifd =
- container_of(layer, struct caif_device_entry, layer);
- struct sk_buff *skb;
- struct netdev_queue *txq;
-
- rcu_read_lock_bh();
-
- skb = cfpkt_tonative(pkt);
- skb->dev = caifd->netdev;
- skb_reset_network_header(skb);
- skb->protocol = htons(ETH_P_CAIF);
-
- /* Check if we need to handle xoff */
- if (likely(caifd->netdev->priv_flags & IFF_NO_QUEUE))
- goto noxoff;
-
- if (unlikely(caifd->xoff))
- goto noxoff;
-
- if (likely(!netif_queue_stopped(caifd->netdev))) {
- struct Qdisc *sch;
-
- /* If we run with a TX queue, check if the queue is too long*/
- txq = netdev_get_tx_queue(skb->dev, 0);
- sch = rcu_dereference_bh(txq->qdisc);
- if (likely(qdisc_is_empty(sch)))
- goto noxoff;
-
- /* can check for explicit qdisc len value only !NOLOCK,
- * always set flow off otherwise
- */
- high = (caifd->netdev->tx_queue_len * q_high) / 100;
- if (!(sch->flags & TCQ_F_NOLOCK) && likely(sch->q.qlen < high))
- goto noxoff;
- }
-
- /* Hold lock while accessing xoff */
- spin_lock_bh(&caifd->flow_lock);
- if (caifd->xoff) {
- spin_unlock_bh(&caifd->flow_lock);
- goto noxoff;
- }
-
- /*
- * Handle flow off, we do this by temporary hi-jacking this
- * skb's destructor function, and replace it with our own
- * flow-on callback. The callback will set flow-on and call
- * the original destructor.
- */
-
- pr_debug("queue has stopped(%d) or is full (%d > %d)\n",
- netif_queue_stopped(caifd->netdev),
- qlen, high);
- caifd->xoff = true;
- caifd->xoff_skb = skb;
- caifd->xoff_skb_dtor = skb->destructor;
- skb->destructor = caif_flow_cb;
- spin_unlock_bh(&caifd->flow_lock);
-
- caifd->layer.up->ctrlcmd(caifd->layer.up,
- _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
- caifd->layer.id);
-noxoff:
- rcu_read_unlock_bh();
-
- err = dev_queue_xmit(skb);
- if (err > 0)
- err = -EIO;
-
- return err;
-}
-
-/*
- * Stuff received packets into the CAIF stack.
- * On error, returns non-zero and releases the skb.
- */
-static int receive(struct sk_buff *skb, struct net_device *dev,
- struct packet_type *pkttype, struct net_device *orig_dev)
-{
- struct cfpkt *pkt;
- struct caif_device_entry *caifd;
- int err;
-
- pkt = cfpkt_fromnative(CAIF_DIR_IN, skb);
-
- rcu_read_lock();
- caifd = caif_get(dev);
-
- if (!caifd || !caifd->layer.up || !caifd->layer.up->receive ||
- !netif_oper_up(caifd->netdev)) {
- rcu_read_unlock();
- kfree_skb(skb);
- return NET_RX_DROP;
- }
-
- /* Hold reference to netdevice while using CAIF stack */
- caifd_hold(caifd);
- rcu_read_unlock();
-
- err = caifd->layer.up->receive(caifd->layer.up, pkt);
-
- /* For -EILSEQ the packet is not freed so free it now */
- if (err == -EILSEQ)
- cfpkt_destroy(pkt);
-
- /* Release reference to stack upwards */
- caifd_put(caifd);
-
- if (err != 0)
- err = NET_RX_DROP;
- return err;
-}
-
-static struct packet_type caif_packet_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_CAIF),
- .func = receive,
-};
-
-static void dev_flowctrl(struct net_device *dev, int on)
-{
- struct caif_device_entry *caifd;
-
- rcu_read_lock();
-
- caifd = caif_get(dev);
- if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
- rcu_read_unlock();
- return;
- }
-
- caifd_hold(caifd);
- rcu_read_unlock();
-
- caifd->layer.up->ctrlcmd(caifd->layer.up,
- on ?
- _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND :
- _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND,
- caifd->layer.id);
- caifd_put(caifd);
-}
-
-int caif_enroll_dev(struct net_device *dev, struct caif_dev_common *caifdev,
- struct cflayer *link_support, int head_room,
- struct cflayer **layer,
- int (**rcv_func)(struct sk_buff *, struct net_device *,
- struct packet_type *,
- struct net_device *))
-{
- struct caif_device_entry *caifd;
- enum cfcnfg_phy_preference pref;
- struct cfcnfg *cfg = get_cfcnfg(dev_net(dev));
- struct caif_device_entry_list *caifdevs;
- int res;
-
- caifdevs = caif_device_list(dev_net(dev));
- caifd = caif_device_alloc(dev);
- if (!caifd)
- return -ENOMEM;
- *layer = &caifd->layer;
- spin_lock_init(&caifd->flow_lock);
-
- switch (caifdev->link_select) {
- case CAIF_LINK_HIGH_BANDW:
- pref = CFPHYPREF_HIGH_BW;
- break;
- case CAIF_LINK_LOW_LATENCY:
- pref = CFPHYPREF_LOW_LAT;
- break;
- default:
- pref = CFPHYPREF_HIGH_BW;
- break;
- }
- mutex_lock(&caifdevs->lock);
- list_add_rcu(&caifd->list, &caifdevs->list);
-
- strscpy(caifd->layer.name, dev->name,
- sizeof(caifd->layer.name));
- caifd->layer.transmit = transmit;
- res = cfcnfg_add_phy_layer(cfg,
- dev,
- &caifd->layer,
- pref,
- link_support,
- caifdev->use_fcs,
- head_room);
- mutex_unlock(&caifdevs->lock);
- if (rcv_func)
- *rcv_func = receive;
- return res;
-}
-EXPORT_SYMBOL(caif_enroll_dev);
-
-/* notify Caif of device events */
-static int caif_device_notify(struct notifier_block *me, unsigned long what,
- void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct caif_device_entry *caifd = NULL;
- struct caif_dev_common *caifdev;
- struct cfcnfg *cfg;
- struct cflayer *layer, *link_support;
- int head_room = 0;
- struct caif_device_entry_list *caifdevs;
- int res;
-
- cfg = get_cfcnfg(dev_net(dev));
- caifdevs = caif_device_list(dev_net(dev));
-
- caifd = caif_get(dev);
- if (caifd == NULL && dev->type != ARPHRD_CAIF)
- return 0;
-
- switch (what) {
- case NETDEV_REGISTER:
- if (caifd != NULL)
- break;
-
- caifdev = netdev_priv(dev);
-
- link_support = NULL;
- if (caifdev->use_frag) {
- head_room = 1;
- link_support = cfserl_create(dev->ifindex,
- caifdev->use_stx);
- if (!link_support) {
- pr_warn("Out of memory\n");
- break;
- }
- }
- res = caif_enroll_dev(dev, caifdev, link_support, head_room,
- &layer, NULL);
- if (res)
- cfserl_release(link_support);
- caifdev->flowctrl = dev_flowctrl;
- break;
-
- case NETDEV_UP:
- rcu_read_lock();
-
- caifd = caif_get(dev);
- if (caifd == NULL) {
- rcu_read_unlock();
- break;
- }
-
- caifd->xoff = false;
- cfcnfg_set_phy_state(cfg, &caifd->layer, true);
- rcu_read_unlock();
-
- break;
-
- case NETDEV_DOWN:
- rcu_read_lock();
-
- caifd = caif_get(dev);
- if (!caifd || !caifd->layer.up || !caifd->layer.up->ctrlcmd) {
- rcu_read_unlock();
- return -EINVAL;
- }
-
- cfcnfg_set_phy_state(cfg, &caifd->layer, false);
- caifd_hold(caifd);
- rcu_read_unlock();
-
- caifd->layer.up->ctrlcmd(caifd->layer.up,
- _CAIF_CTRLCMD_PHYIF_DOWN_IND,
- caifd->layer.id);
-
- spin_lock_bh(&caifd->flow_lock);
-
- /*
- * Replace our xoff-destructor with original destructor.
- * We trust that skb->destructor *always* is called before
- * the skb reference is invalid. The hijacked SKB destructor
- * takes the flow_lock so manipulating the skb->destructor here
- * should be safe.
- */
- if (caifd->xoff_skb_dtor != NULL && caifd->xoff_skb != NULL)
- caifd->xoff_skb->destructor = caifd->xoff_skb_dtor;
-
- caifd->xoff = false;
- caifd->xoff_skb_dtor = NULL;
- caifd->xoff_skb = NULL;
-
- spin_unlock_bh(&caifd->flow_lock);
- caifd_put(caifd);
- break;
-
- case NETDEV_UNREGISTER:
- mutex_lock(&caifdevs->lock);
-
- caifd = caif_get(dev);
- if (caifd == NULL) {
- mutex_unlock(&caifdevs->lock);
- break;
- }
- list_del_rcu(&caifd->list);
-
- /*
- * NETDEV_UNREGISTER is called repeatedly until all reference
- * counts for the net-device are released. If references to
- * caifd is taken, simply ignore NETDEV_UNREGISTER and wait for
- * the next call to NETDEV_UNREGISTER.
- *
- * If any packets are in flight down the CAIF Stack,
- * cfcnfg_del_phy_layer will return nonzero.
- * If no packets are in flight, the CAIF Stack associated
- * with the net-device un-registering is freed.
- */
-
- if (caifd_refcnt_read(caifd) != 0 ||
- cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0) {
-
- pr_info("Wait for device inuse\n");
- /* Enrole device if CAIF Stack is still in use */
- list_add_rcu(&caifd->list, &caifdevs->list);
- mutex_unlock(&caifdevs->lock);
- break;
- }
-
- synchronize_rcu();
- dev_put(caifd->netdev);
- free_percpu(caifd->pcpu_refcnt);
- kfree(caifd);
-
- mutex_unlock(&caifdevs->lock);
- break;
- }
- return 0;
-}
-
-static struct notifier_block caif_device_notifier = {
- .notifier_call = caif_device_notify,
- .priority = 0,
-};
-
-/* Per-namespace Caif devices handling */
-static int caif_init_net(struct net *net)
-{
- struct caif_net *caifn = net_generic(net, caif_net_id);
- INIT_LIST_HEAD(&caifn->caifdevs.list);
- mutex_init(&caifn->caifdevs.lock);
-
- caifn->cfg = cfcnfg_create();
- if (!caifn->cfg)
- return -ENOMEM;
-
- return 0;
-}
-
-static void caif_exit_net(struct net *net)
-{
- struct caif_device_entry *caifd, *tmp;
- struct caif_device_entry_list *caifdevs =
- caif_device_list(net);
- struct cfcnfg *cfg = get_cfcnfg(net);
-
- rtnl_lock();
- mutex_lock(&caifdevs->lock);
-
- list_for_each_entry_safe(caifd, tmp, &caifdevs->list, list) {
- int i = 0;
- list_del_rcu(&caifd->list);
- cfcnfg_set_phy_state(cfg, &caifd->layer, false);
-
- while (i < 10 &&
- (caifd_refcnt_read(caifd) != 0 ||
- cfcnfg_del_phy_layer(cfg, &caifd->layer) != 0)) {
-
- pr_info("Wait for device inuse\n");
- msleep(250);
- i++;
- }
- synchronize_rcu();
- dev_put(caifd->netdev);
- free_percpu(caifd->pcpu_refcnt);
- kfree(caifd);
- }
- cfcnfg_remove(cfg);
-
- mutex_unlock(&caifdevs->lock);
- rtnl_unlock();
-}
-
-static struct pernet_operations caif_net_ops = {
- .init = caif_init_net,
- .exit = caif_exit_net,
- .id = &caif_net_id,
- .size = sizeof(struct caif_net),
-};
-
-/* Initialize Caif devices list */
-static int __init caif_device_init(void)
-{
- int result;
-
- result = register_pernet_subsys(&caif_net_ops);
-
- if (result)
- return result;
-
- register_netdevice_notifier(&caif_device_notifier);
- dev_add_pack(&caif_packet_type);
-
- return result;
-}
-
-static void __exit caif_device_exit(void)
-{
- unregister_netdevice_notifier(&caif_device_notifier);
- dev_remove_pack(&caif_packet_type);
- unregister_pernet_subsys(&caif_net_ops);
-}
-
-module_init(caif_device_init);
-module_exit(caif_device_exit);
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
deleted file mode 100644
index af218742af5a..000000000000
--- a/net/caif/caif_socket.c
+++ /dev/null
@@ -1,1114 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/filter.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/sched/signal.h>
-#include <linux/spinlock.h>
-#include <linux/mutex.h>
-#include <linux/list.h>
-#include <linux/wait.h>
-#include <linux/poll.h>
-#include <linux/tcp.h>
-#include <linux/uaccess.h>
-#include <linux/debugfs.h>
-#include <linux/caif/caif_socket.h>
-#include <linux/pkt_sched.h>
-#include <net/sock.h>
-#include <net/tcp_states.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/caif_dev.h>
-#include <net/caif/cfpkt.h>
-
-MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol socket support (AF_CAIF)");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_NETPROTO(AF_CAIF);
-
-/*
- * CAIF state is re-using the TCP socket states.
- * caif_states stored in sk_state reflect the state as reported by
- * the CAIF stack, while sk_socket->state is the state of the socket.
- */
-enum caif_states {
- CAIF_CONNECTED = TCP_ESTABLISHED,
- CAIF_CONNECTING = TCP_SYN_SENT,
- CAIF_DISCONNECTED = TCP_CLOSE
-};
-
-#define TX_FLOW_ON_BIT 1
-#define RX_FLOW_ON_BIT 2
-
-struct caifsock {
- struct sock sk; /* must be first member */
- struct cflayer layer;
- unsigned long flow_state;
- struct caif_connect_request conn_req;
- struct mutex readlock;
- struct dentry *debugfs_socket_dir;
- int headroom, tailroom, maxframe;
-};
-
-static int rx_flow_is_on(struct caifsock *cf_sk)
-{
- return test_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state);
-}
-
-static int tx_flow_is_on(struct caifsock *cf_sk)
-{
- return test_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state);
-}
-
-static void set_rx_flow_off(struct caifsock *cf_sk)
-{
- clear_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state);
-}
-
-static void set_rx_flow_on(struct caifsock *cf_sk)
-{
- set_bit(RX_FLOW_ON_BIT, &cf_sk->flow_state);
-}
-
-static void set_tx_flow_off(struct caifsock *cf_sk)
-{
- clear_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state);
-}
-
-static void set_tx_flow_on(struct caifsock *cf_sk)
-{
- set_bit(TX_FLOW_ON_BIT, &cf_sk->flow_state);
-}
-
-static void caif_read_lock(struct sock *sk)
-{
- struct caifsock *cf_sk;
- cf_sk = container_of(sk, struct caifsock, sk);
- mutex_lock(&cf_sk->readlock);
-}
-
-static void caif_read_unlock(struct sock *sk)
-{
- struct caifsock *cf_sk;
- cf_sk = container_of(sk, struct caifsock, sk);
- mutex_unlock(&cf_sk->readlock);
-}
-
-static int sk_rcvbuf_lowwater(struct caifsock *cf_sk)
-{
- /* A quarter of full buffer is used a low water mark */
- return cf_sk->sk.sk_rcvbuf / 4;
-}
-
-static void caif_flow_ctrl(struct sock *sk, int mode)
-{
- struct caifsock *cf_sk;
- cf_sk = container_of(sk, struct caifsock, sk);
- if (cf_sk->layer.dn && cf_sk->layer.dn->modemcmd)
- cf_sk->layer.dn->modemcmd(cf_sk->layer.dn, mode);
-}
-
-/*
- * Copied from sock.c:sock_queue_rcv_skb(), but changed so packets are
- * not dropped, but CAIF is sending flow off instead.
- */
-static void caif_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
-{
- int err;
- unsigned long flags;
- struct sk_buff_head *list = &sk->sk_receive_queue;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- bool queued = false;
-
- if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=
- (unsigned int)sk->sk_rcvbuf && rx_flow_is_on(cf_sk)) {
- net_dbg_ratelimited("sending flow OFF (queue len = %d %d)\n",
- atomic_read(&cf_sk->sk.sk_rmem_alloc),
- sk_rcvbuf_lowwater(cf_sk));
- set_rx_flow_off(cf_sk);
- caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
- }
-
- err = sk_filter(sk, skb);
- if (err)
- goto out;
-
- if (!sk_rmem_schedule(sk, skb, skb->truesize) && rx_flow_is_on(cf_sk)) {
- set_rx_flow_off(cf_sk);
- net_dbg_ratelimited("sending flow OFF due to rmem_schedule\n");
- caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_OFF_REQ);
- }
- skb->dev = NULL;
- skb_set_owner_r(skb, sk);
- spin_lock_irqsave(&list->lock, flags);
- queued = !sock_flag(sk, SOCK_DEAD);
- if (queued)
- __skb_queue_tail(list, skb);
- spin_unlock_irqrestore(&list->lock, flags);
-out:
- if (queued)
- sk->sk_data_ready(sk);
- else
- kfree_skb(skb);
-}
-
-/* Packet Receive Callback function called from CAIF Stack */
-static int caif_sktrecv_cb(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct caifsock *cf_sk;
- struct sk_buff *skb;
-
- cf_sk = container_of(layr, struct caifsock, layer);
- skb = cfpkt_tonative(pkt);
-
- if (unlikely(cf_sk->sk.sk_state != CAIF_CONNECTED)) {
- kfree_skb(skb);
- return 0;
- }
- caif_queue_rcv_skb(&cf_sk->sk, skb);
- return 0;
-}
-
-static void cfsk_hold(struct cflayer *layr)
-{
- struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
- sock_hold(&cf_sk->sk);
-}
-
-static void cfsk_put(struct cflayer *layr)
-{
- struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
- sock_put(&cf_sk->sk);
-}
-
-/* Packet Control Callback function called from CAIF */
-static void caif_ctrl_cb(struct cflayer *layr,
- enum caif_ctrlcmd flow,
- int phyid)
-{
- struct caifsock *cf_sk = container_of(layr, struct caifsock, layer);
- switch (flow) {
- case CAIF_CTRLCMD_FLOW_ON_IND:
- /* OK from modem to start sending again */
- set_tx_flow_on(cf_sk);
- cf_sk->sk.sk_state_change(&cf_sk->sk);
- break;
-
- case CAIF_CTRLCMD_FLOW_OFF_IND:
- /* Modem asks us to shut up */
- set_tx_flow_off(cf_sk);
- cf_sk->sk.sk_state_change(&cf_sk->sk);
- break;
-
- case CAIF_CTRLCMD_INIT_RSP:
- /* We're now connected */
- caif_client_register_refcnt(&cf_sk->layer,
- cfsk_hold, cfsk_put);
- cf_sk->sk.sk_state = CAIF_CONNECTED;
- set_tx_flow_on(cf_sk);
- cf_sk->sk.sk_shutdown = 0;
- cf_sk->sk.sk_state_change(&cf_sk->sk);
- break;
-
- case CAIF_CTRLCMD_DEINIT_RSP:
- /* We're now disconnected */
- cf_sk->sk.sk_state = CAIF_DISCONNECTED;
- cf_sk->sk.sk_state_change(&cf_sk->sk);
- break;
-
- case CAIF_CTRLCMD_INIT_FAIL_RSP:
- /* Connect request failed */
- cf_sk->sk.sk_err = ECONNREFUSED;
- cf_sk->sk.sk_state = CAIF_DISCONNECTED;
- cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
- /*
- * Socket "standards" seems to require POLLOUT to
- * be set at connect failure.
- */
- set_tx_flow_on(cf_sk);
- cf_sk->sk.sk_state_change(&cf_sk->sk);
- break;
-
- case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
- /* Modem has closed this connection, or device is down. */
- cf_sk->sk.sk_shutdown = SHUTDOWN_MASK;
- cf_sk->sk.sk_err = ECONNRESET;
- set_rx_flow_on(cf_sk);
- sk_error_report(&cf_sk->sk);
- break;
-
- default:
- pr_debug("Unexpected flow command %d\n", flow);
- }
-}
-
-static void caif_check_flow_release(struct sock *sk)
-{
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
-
- if (rx_flow_is_on(cf_sk))
- return;
-
- if (atomic_read(&sk->sk_rmem_alloc) <= sk_rcvbuf_lowwater(cf_sk)) {
- set_rx_flow_on(cf_sk);
- caif_flow_ctrl(sk, CAIF_MODEMCMD_FLOW_ON_REQ);
- }
-}
-
-/*
- * Copied from unix_dgram_recvmsg, but removed credit checks,
- * changed locking, address handling and added MSG_TRUNC.
- */
-static int caif_seqpkt_recvmsg(struct socket *sock, struct msghdr *m,
- size_t len, int flags)
-
-{
- struct sock *sk = sock->sk;
- struct sk_buff *skb;
- int ret;
- int copylen;
-
- ret = -EOPNOTSUPP;
- if (flags & MSG_OOB)
- goto read_error;
-
- skb = skb_recv_datagram(sk, flags, &ret);
- if (!skb)
- goto read_error;
- copylen = skb->len;
- if (len < copylen) {
- m->msg_flags |= MSG_TRUNC;
- copylen = len;
- }
-
- ret = skb_copy_datagram_msg(skb, 0, m, copylen);
- if (ret)
- goto out_free;
-
- ret = (flags & MSG_TRUNC) ? skb->len : copylen;
-out_free:
- skb_free_datagram(sk, skb);
- caif_check_flow_release(sk);
- return ret;
-
-read_error:
- return ret;
-}
-
-
-/* Copied from unix_stream_wait_data, identical except for lock call. */
-static long caif_stream_data_wait(struct sock *sk, long timeo)
-{
- DEFINE_WAIT(wait);
- lock_sock(sk);
-
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
-
- if (!skb_queue_empty(&sk->sk_receive_queue) ||
- sk->sk_err ||
- sk->sk_state != CAIF_CONNECTED ||
- sock_flag(sk, SOCK_DEAD) ||
- (sk->sk_shutdown & RCV_SHUTDOWN) ||
- signal_pending(current) ||
- !timeo)
- break;
-
- sk_set_bit(SOCKWQ_ASYNC_WAITDATA, sk);
- release_sock(sk);
- timeo = schedule_timeout(timeo);
- lock_sock(sk);
-
- if (sock_flag(sk, SOCK_DEAD))
- break;
-
- sk_clear_bit(SOCKWQ_ASYNC_WAITDATA, sk);
- }
-
- finish_wait(sk_sleep(sk), &wait);
- release_sock(sk);
- return timeo;
-}
-
-
-/*
- * Copied from unix_stream_recvmsg, but removed credit checks,
- * changed locking calls, changed address handling.
- */
-static int caif_stream_recvmsg(struct socket *sock, struct msghdr *msg,
- size_t size, int flags)
-{
- struct sock *sk = sock->sk;
- int copied = 0;
- int target;
- int err = 0;
- long timeo;
-
- err = -EOPNOTSUPP;
- if (flags&MSG_OOB)
- goto out;
-
- /*
- * Lock the socket to prevent queue disordering
- * while sleeps in memcpy_tomsg
- */
- err = -EAGAIN;
- if (sk->sk_state == CAIF_CONNECTING)
- goto out;
-
- caif_read_lock(sk);
- target = sock_rcvlowat(sk, flags&MSG_WAITALL, size);
- timeo = sock_rcvtimeo(sk, flags&MSG_DONTWAIT);
-
- do {
- int chunk;
- struct sk_buff *skb;
-
- lock_sock(sk);
- if (sock_flag(sk, SOCK_DEAD)) {
- err = -ECONNRESET;
- goto unlock;
- }
- skb = skb_dequeue(&sk->sk_receive_queue);
- caif_check_flow_release(sk);
-
- if (skb == NULL) {
- if (copied >= target)
- goto unlock;
- /*
- * POSIX 1003.1g mandates this order.
- */
- err = sock_error(sk);
- if (err)
- goto unlock;
- err = -ECONNRESET;
- if (sk->sk_shutdown & RCV_SHUTDOWN)
- goto unlock;
-
- err = -EPIPE;
- if (sk->sk_state != CAIF_CONNECTED)
- goto unlock;
- if (sock_flag(sk, SOCK_DEAD))
- goto unlock;
-
- release_sock(sk);
-
- err = -EAGAIN;
- if (!timeo)
- break;
-
- caif_read_unlock(sk);
-
- timeo = caif_stream_data_wait(sk, timeo);
-
- if (signal_pending(current)) {
- err = sock_intr_errno(timeo);
- goto out;
- }
- caif_read_lock(sk);
- continue;
-unlock:
- release_sock(sk);
- break;
- }
- release_sock(sk);
- chunk = min_t(unsigned int, skb->len, size);
- if (memcpy_to_msg(msg, skb->data, chunk)) {
- skb_queue_head(&sk->sk_receive_queue, skb);
- if (copied == 0)
- copied = -EFAULT;
- break;
- }
- copied += chunk;
- size -= chunk;
-
- /* Mark read part of skb as used */
- if (!(flags & MSG_PEEK)) {
- skb_pull(skb, chunk);
-
- /* put the skb back if we didn't use it up. */
- if (skb->len) {
- skb_queue_head(&sk->sk_receive_queue, skb);
- break;
- }
- kfree_skb(skb);
-
- } else {
- /*
- * It is questionable, see note in unix_dgram_recvmsg.
- */
- /* put message back and return */
- skb_queue_head(&sk->sk_receive_queue, skb);
- break;
- }
- } while (size);
- caif_read_unlock(sk);
-
-out:
- return copied ? : err;
-}
-
-/*
- * Copied from sock.c:sock_wait_for_wmem, but change to wait for
- * CAIF flow-on and sock_writable.
- */
-static long caif_wait_for_flow_on(struct caifsock *cf_sk,
- int wait_writeable, long timeo, int *err)
-{
- struct sock *sk = &cf_sk->sk;
- DEFINE_WAIT(wait);
- for (;;) {
- *err = 0;
- if (tx_flow_is_on(cf_sk) &&
- (!wait_writeable || sock_writeable(&cf_sk->sk)))
- break;
- *err = -ETIMEDOUT;
- if (!timeo)
- break;
- *err = -ERESTARTSYS;
- if (signal_pending(current))
- break;
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- *err = -ECONNRESET;
- if (sk->sk_shutdown & SHUTDOWN_MASK)
- break;
- *err = -sk->sk_err;
- if (sk->sk_err)
- break;
- *err = -EPIPE;
- if (cf_sk->sk.sk_state != CAIF_CONNECTED)
- break;
- timeo = schedule_timeout(timeo);
- }
- finish_wait(sk_sleep(sk), &wait);
- return timeo;
-}
-
-/*
- * Transmit a SKB. The device may temporarily request re-transmission
- * by returning EAGAIN.
- */
-static int transmit_skb(struct sk_buff *skb, struct caifsock *cf_sk,
- int noblock, long timeo)
-{
- struct cfpkt *pkt;
-
- pkt = cfpkt_fromnative(CAIF_DIR_OUT, skb);
- memset(skb->cb, 0, sizeof(struct caif_payload_info));
- cfpkt_set_prio(pkt, cf_sk->sk.sk_priority);
-
- if (cf_sk->layer.dn == NULL) {
- kfree_skb(skb);
- return -EINVAL;
- }
-
- return cf_sk->layer.dn->transmit(cf_sk->layer.dn, pkt);
-}
-
-/* Copied from af_unix:unix_dgram_sendmsg, and adapted to CAIF */
-static int caif_seqpkt_sendmsg(struct socket *sock, struct msghdr *msg,
- size_t len)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- int buffer_size;
- int ret = 0;
- struct sk_buff *skb = NULL;
- int noblock;
- long timeo;
- caif_assert(cf_sk);
- ret = sock_error(sk);
- if (ret)
- goto err;
-
- ret = -EOPNOTSUPP;
- if (msg->msg_flags&MSG_OOB)
- goto err;
-
- ret = -EOPNOTSUPP;
- if (msg->msg_namelen)
- goto err;
-
- noblock = msg->msg_flags & MSG_DONTWAIT;
-
- timeo = sock_sndtimeo(sk, noblock);
- timeo = caif_wait_for_flow_on(container_of(sk, struct caifsock, sk),
- 1, timeo, &ret);
-
- if (ret)
- goto err;
- ret = -EPIPE;
- if (cf_sk->sk.sk_state != CAIF_CONNECTED ||
- sock_flag(sk, SOCK_DEAD) ||
- (sk->sk_shutdown & RCV_SHUTDOWN))
- goto err;
-
- /* Error if trying to write more than maximum frame size. */
- ret = -EMSGSIZE;
- if (len > cf_sk->maxframe && cf_sk->sk.sk_protocol != CAIFPROTO_RFM)
- goto err;
-
- buffer_size = len + cf_sk->headroom + cf_sk->tailroom;
-
- ret = -ENOMEM;
- skb = sock_alloc_send_skb(sk, buffer_size, noblock, &ret);
-
- if (!skb || skb_tailroom(skb) < buffer_size)
- goto err;
-
- skb_reserve(skb, cf_sk->headroom);
-
- ret = memcpy_from_msg(skb_put(skb, len), msg, len);
-
- if (ret)
- goto err;
- ret = transmit_skb(skb, cf_sk, noblock, timeo);
- if (ret < 0)
- /* skb is already freed */
- return ret;
-
- return len;
-err:
- kfree_skb(skb);
- return ret;
-}
-
-/*
- * Copied from unix_stream_sendmsg and adapted to CAIF:
- * Changed removed permission handling and added waiting for flow on
- * and other minor adaptations.
- */
-static int caif_stream_sendmsg(struct socket *sock, struct msghdr *msg,
- size_t len)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- int err, size;
- struct sk_buff *skb;
- int sent = 0;
- long timeo;
-
- err = -EOPNOTSUPP;
- if (unlikely(msg->msg_flags&MSG_OOB))
- goto out_err;
-
- if (unlikely(msg->msg_namelen))
- goto out_err;
-
- timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
- timeo = caif_wait_for_flow_on(cf_sk, 1, timeo, &err);
-
- if (unlikely(sk->sk_shutdown & SEND_SHUTDOWN))
- goto pipe_err;
-
- while (sent < len) {
-
- size = len-sent;
-
- if (size > cf_sk->maxframe)
- size = cf_sk->maxframe;
-
- /* If size is more than half of sndbuf, chop up message */
- if (size > ((sk->sk_sndbuf >> 1) - 64))
- size = (sk->sk_sndbuf >> 1) - 64;
-
- if (size > SKB_MAX_ALLOC)
- size = SKB_MAX_ALLOC;
-
- skb = sock_alloc_send_skb(sk,
- size + cf_sk->headroom +
- cf_sk->tailroom,
- msg->msg_flags&MSG_DONTWAIT,
- &err);
- if (skb == NULL)
- goto out_err;
-
- skb_reserve(skb, cf_sk->headroom);
- /*
- * If you pass two values to the sock_alloc_send_skb
- * it tries to grab the large buffer with GFP_NOFS
- * (which can fail easily), and if it fails grab the
- * fallback size buffer which is under a page and will
- * succeed. [Alan]
- */
- size = min_t(int, size, skb_tailroom(skb));
-
- err = memcpy_from_msg(skb_put(skb, size), msg, size);
- if (err) {
- kfree_skb(skb);
- goto out_err;
- }
- err = transmit_skb(skb, cf_sk,
- msg->msg_flags&MSG_DONTWAIT, timeo);
- if (err < 0)
- /* skb is already freed */
- goto pipe_err;
-
- sent += size;
- }
-
- return sent;
-
-pipe_err:
- if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
- send_sig(SIGPIPE, current, 0);
- err = -EPIPE;
-out_err:
- return sent ? : err;
-}
-
-static int setsockopt(struct socket *sock, int lvl, int opt, sockptr_t ov,
- unsigned int ol)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- int linksel;
-
- if (cf_sk->sk.sk_socket->state != SS_UNCONNECTED)
- return -ENOPROTOOPT;
-
- switch (opt) {
- case CAIFSO_LINK_SELECT:
- if (ol < sizeof(int))
- return -EINVAL;
- if (lvl != SOL_CAIF)
- goto bad_sol;
- if (copy_from_sockptr(&linksel, ov, sizeof(int)))
- return -EINVAL;
- lock_sock(&(cf_sk->sk));
- cf_sk->conn_req.link_selector = linksel;
- release_sock(&cf_sk->sk);
- return 0;
-
- case CAIFSO_REQ_PARAM:
- if (lvl != SOL_CAIF)
- goto bad_sol;
- if (cf_sk->sk.sk_protocol != CAIFPROTO_UTIL)
- return -ENOPROTOOPT;
- lock_sock(&(cf_sk->sk));
- if (ol > sizeof(cf_sk->conn_req.param.data) ||
- copy_from_sockptr(&cf_sk->conn_req.param.data, ov, ol)) {
- release_sock(&cf_sk->sk);
- return -EINVAL;
- }
- cf_sk->conn_req.param.size = ol;
- release_sock(&cf_sk->sk);
- return 0;
-
- default:
- return -ENOPROTOOPT;
- }
-
- return 0;
-bad_sol:
- return -ENOPROTOOPT;
-
-}
-
-/*
- * caif_connect() - Connect a CAIF Socket
- * Copied and modified af_irda.c:irda_connect().
- *
- * Note : by consulting "errno", the user space caller may learn the cause
- * of the failure. Most of them are visible in the function, others may come
- * from subroutines called and are listed here :
- * o -EAFNOSUPPORT: bad socket family or type.
- * o -ESOCKTNOSUPPORT: bad socket type or protocol
- * o -EINVAL: bad socket address, or CAIF link type
- * o -ECONNREFUSED: remote end refused the connection.
- * o -EINPROGRESS: connect request sent but timed out (or non-blocking)
- * o -EISCONN: already connected.
- * o -ETIMEDOUT: Connection timed out (send timeout)
- * o -ENODEV: No link layer to send request
- * o -ECONNRESET: Received Shutdown indication or lost link layer
- * o -ENOMEM: Out of memory
- *
- * State Strategy:
- * o sk_state: holds the CAIF_* protocol state, it's updated by
- * caif_ctrl_cb.
- * o sock->state: holds the SS_* socket state and is updated by connect and
- * disconnect.
- */
-static int caif_connect(struct socket *sock, struct sockaddr_unsized *uaddr,
- int addr_len, int flags)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- long timeo;
- int err;
- int ifindex, headroom, tailroom;
- unsigned int mtu;
- struct net_device *dev;
-
- lock_sock(sk);
-
- err = -EINVAL;
- if (addr_len < offsetofend(struct sockaddr, sa_family))
- goto out;
-
- err = -EAFNOSUPPORT;
- if (uaddr->sa_family != AF_CAIF)
- goto out;
-
- switch (sock->state) {
- case SS_UNCONNECTED:
- /* Normal case, a fresh connect */
- caif_assert(sk->sk_state == CAIF_DISCONNECTED);
- break;
- case SS_CONNECTING:
- switch (sk->sk_state) {
- case CAIF_CONNECTED:
- sock->state = SS_CONNECTED;
- err = -EISCONN;
- goto out;
- case CAIF_DISCONNECTED:
- /* Reconnect allowed */
- break;
- case CAIF_CONNECTING:
- err = -EALREADY;
- if (flags & O_NONBLOCK)
- goto out;
- goto wait_connect;
- }
- break;
- case SS_CONNECTED:
- caif_assert(sk->sk_state == CAIF_CONNECTED ||
- sk->sk_state == CAIF_DISCONNECTED);
- if (sk->sk_shutdown & SHUTDOWN_MASK) {
- /* Allow re-connect after SHUTDOWN_IND */
- caif_disconnect_client(sock_net(sk), &cf_sk->layer);
- caif_free_client(&cf_sk->layer);
- break;
- }
- /* No reconnect on a seqpacket socket */
- err = -EISCONN;
- goto out;
- case SS_DISCONNECTING:
- case SS_FREE:
- caif_assert(1); /*Should never happen */
- break;
- }
- sk->sk_state = CAIF_DISCONNECTED;
- sock->state = SS_UNCONNECTED;
- sk_stream_kill_queues(&cf_sk->sk);
-
- err = -EINVAL;
- if (addr_len != sizeof(struct sockaddr_caif))
- goto out;
-
- memcpy(&cf_sk->conn_req.sockaddr, uaddr,
- sizeof(struct sockaddr_caif));
-
- /* Move to connecting socket, start sending Connect Requests */
- sock->state = SS_CONNECTING;
- sk->sk_state = CAIF_CONNECTING;
-
- /* Check priority value comming from socket */
- /* if priority value is out of range it will be ajusted */
- if (cf_sk->sk.sk_priority > CAIF_PRIO_MAX)
- cf_sk->conn_req.priority = CAIF_PRIO_MAX;
- else if (cf_sk->sk.sk_priority < CAIF_PRIO_MIN)
- cf_sk->conn_req.priority = CAIF_PRIO_MIN;
- else
- cf_sk->conn_req.priority = cf_sk->sk.sk_priority;
-
- /*ifindex = id of the interface.*/
- cf_sk->conn_req.ifindex = cf_sk->sk.sk_bound_dev_if;
-
- cf_sk->layer.receive = caif_sktrecv_cb;
-
- err = caif_connect_client(sock_net(sk), &cf_sk->conn_req,
- &cf_sk->layer, &ifindex, &headroom, &tailroom);
-
- if (err < 0) {
- cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
- cf_sk->sk.sk_state = CAIF_DISCONNECTED;
- goto out;
- }
-
- err = -ENODEV;
- rcu_read_lock();
- dev = dev_get_by_index_rcu(sock_net(sk), ifindex);
- if (!dev) {
- rcu_read_unlock();
- goto out;
- }
- cf_sk->headroom = LL_RESERVED_SPACE_EXTRA(dev, headroom);
- mtu = dev->mtu;
- rcu_read_unlock();
-
- cf_sk->tailroom = tailroom;
- cf_sk->maxframe = mtu - (headroom + tailroom);
- if (cf_sk->maxframe < 1) {
- pr_warn("CAIF Interface MTU too small (%d)\n", dev->mtu);
- err = -ENODEV;
- goto out;
- }
-
- err = -EINPROGRESS;
-wait_connect:
-
- if (sk->sk_state != CAIF_CONNECTED && (flags & O_NONBLOCK))
- goto out;
-
- timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
-
- release_sock(sk);
- err = -ERESTARTSYS;
- timeo = wait_event_interruptible_timeout(*sk_sleep(sk),
- sk->sk_state != CAIF_CONNECTING,
- timeo);
- lock_sock(sk);
- if (timeo < 0)
- goto out; /* -ERESTARTSYS */
-
- err = -ETIMEDOUT;
- if (timeo == 0 && sk->sk_state != CAIF_CONNECTED)
- goto out;
- if (sk->sk_state != CAIF_CONNECTED) {
- sock->state = SS_UNCONNECTED;
- err = sock_error(sk);
- if (!err)
- err = -ECONNREFUSED;
- goto out;
- }
- sock->state = SS_CONNECTED;
- err = 0;
-out:
- release_sock(sk);
- return err;
-}
-
-/*
- * caif_release() - Disconnect a CAIF Socket
- * Copied and modified af_irda.c:irda_release().
- */
-static int caif_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
-
- if (!sk)
- return 0;
-
- set_tx_flow_off(cf_sk);
-
- /*
- * Ensure that packets are not queued after this point in time.
- * caif_queue_rcv_skb checks SOCK_DEAD holding the queue lock,
- * this ensures no packets when sock is dead.
- */
- spin_lock_bh(&sk->sk_receive_queue.lock);
- sock_set_flag(sk, SOCK_DEAD);
- spin_unlock_bh(&sk->sk_receive_queue.lock);
- sock->sk = NULL;
-
- WARN_ON(IS_ERR(cf_sk->debugfs_socket_dir));
- debugfs_remove_recursive(cf_sk->debugfs_socket_dir);
-
- lock_sock(&(cf_sk->sk));
- sk->sk_state = CAIF_DISCONNECTED;
- sk->sk_shutdown = SHUTDOWN_MASK;
-
- caif_disconnect_client(sock_net(sk), &cf_sk->layer);
- cf_sk->sk.sk_socket->state = SS_DISCONNECTING;
- wake_up_interruptible_poll(sk_sleep(sk), EPOLLERR|EPOLLHUP);
-
- sock_orphan(sk);
- sk_stream_kill_queues(&cf_sk->sk);
- release_sock(sk);
- sock_put(sk);
- return 0;
-}
-
-/* Copied from af_unix.c:unix_poll(), added CAIF tx_flow handling */
-static __poll_t caif_poll(struct file *file,
- struct socket *sock, poll_table *wait)
-{
- struct sock *sk = sock->sk;
- __poll_t mask;
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
-
- sock_poll_wait(file, sock, wait);
- mask = 0;
-
- /* exceptional events? */
- if (sk->sk_err)
- mask |= EPOLLERR;
- if (sk->sk_shutdown == SHUTDOWN_MASK)
- mask |= EPOLLHUP;
- if (sk->sk_shutdown & RCV_SHUTDOWN)
- mask |= EPOLLRDHUP;
-
- /* readable? */
- if (!skb_queue_empty_lockless(&sk->sk_receive_queue) ||
- (sk->sk_shutdown & RCV_SHUTDOWN))
- mask |= EPOLLIN | EPOLLRDNORM;
-
- /*
- * we set writable also when the other side has shut down the
- * connection. This prevents stuck sockets.
- */
- if (sock_writeable(sk) && tx_flow_is_on(cf_sk))
- mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
-
- return mask;
-}
-
-static const struct proto_ops caif_seqpacket_ops = {
- .family = PF_CAIF,
- .owner = THIS_MODULE,
- .release = caif_release,
- .bind = sock_no_bind,
- .connect = caif_connect,
- .socketpair = sock_no_socketpair,
- .accept = sock_no_accept,
- .getname = sock_no_getname,
- .poll = caif_poll,
- .ioctl = sock_no_ioctl,
- .listen = sock_no_listen,
- .shutdown = sock_no_shutdown,
- .setsockopt = setsockopt,
- .sendmsg = caif_seqpkt_sendmsg,
- .recvmsg = caif_seqpkt_recvmsg,
- .mmap = sock_no_mmap,
-};
-
-static const struct proto_ops caif_stream_ops = {
- .family = PF_CAIF,
- .owner = THIS_MODULE,
- .release = caif_release,
- .bind = sock_no_bind,
- .connect = caif_connect,
- .socketpair = sock_no_socketpair,
- .accept = sock_no_accept,
- .getname = sock_no_getname,
- .poll = caif_poll,
- .ioctl = sock_no_ioctl,
- .listen = sock_no_listen,
- .shutdown = sock_no_shutdown,
- .setsockopt = setsockopt,
- .sendmsg = caif_stream_sendmsg,
- .recvmsg = caif_stream_recvmsg,
- .mmap = sock_no_mmap,
-};
-
-/* This function is called when a socket is finally destroyed. */
-static void caif_sock_destructor(struct sock *sk)
-{
- struct caifsock *cf_sk = container_of(sk, struct caifsock, sk);
- caif_assert(!refcount_read(&sk->sk_wmem_alloc));
- caif_assert(sk_unhashed(sk));
- caif_assert(!sk->sk_socket);
- if (!sock_flag(sk, SOCK_DEAD)) {
- pr_debug("Attempt to release alive CAIF socket: %p\n", sk);
- return;
- }
- sk_stream_kill_queues(&cf_sk->sk);
- WARN_ON_ONCE(sk->sk_forward_alloc);
- caif_free_client(&cf_sk->layer);
-}
-
-static int caif_create(struct net *net, struct socket *sock, int protocol,
- int kern)
-{
- struct sock *sk = NULL;
- struct caifsock *cf_sk = NULL;
- static struct proto prot = {.name = "PF_CAIF",
- .owner = THIS_MODULE,
- .obj_size = sizeof(struct caifsock),
- .useroffset = offsetof(struct caifsock, conn_req.param),
- .usersize = sizeof_field(struct caifsock, conn_req.param)
- };
-
- if (!capable(CAP_SYS_ADMIN) && !capable(CAP_NET_ADMIN))
- return -EPERM;
- /*
- * The sock->type specifies the socket type to use.
- * The CAIF socket is a packet stream in the sense
- * that it is packet based. CAIF trusts the reliability
- * of the link, no resending is implemented.
- */
- if (sock->type == SOCK_SEQPACKET)
- sock->ops = &caif_seqpacket_ops;
- else if (sock->type == SOCK_STREAM)
- sock->ops = &caif_stream_ops;
- else
- return -ESOCKTNOSUPPORT;
-
- if (protocol < 0 || protocol >= CAIFPROTO_MAX)
- return -EPROTONOSUPPORT;
- /*
- * Set the socket state to unconnected. The socket state
- * is really not used at all in the net/core or socket.c but the
- * initialization makes sure that sock->state is not uninitialized.
- */
- sk = sk_alloc(net, PF_CAIF, GFP_KERNEL, &prot, kern);
- if (!sk)
- return -ENOMEM;
-
- cf_sk = container_of(sk, struct caifsock, sk);
-
- /* Store the protocol */
- sk->sk_protocol = (unsigned char) protocol;
-
- /* Initialize default priority for well-known cases */
- switch (protocol) {
- case CAIFPROTO_AT:
- sk->sk_priority = TC_PRIO_CONTROL;
- break;
- case CAIFPROTO_RFM:
- sk->sk_priority = TC_PRIO_INTERACTIVE_BULK;
- break;
- default:
- sk->sk_priority = TC_PRIO_BESTEFFORT;
- }
-
- /*
- * Lock in order to try to stop someone from opening the socket
- * too early.
- */
- lock_sock(&(cf_sk->sk));
-
- /* Initialize the nozero default sock structure data. */
- sock_init_data(sock, sk);
- sk->sk_destruct = caif_sock_destructor;
-
- mutex_init(&cf_sk->readlock); /* single task reading lock */
- cf_sk->layer.ctrlcmd = caif_ctrl_cb;
- cf_sk->sk.sk_socket->state = SS_UNCONNECTED;
- cf_sk->sk.sk_state = CAIF_DISCONNECTED;
-
- set_tx_flow_off(cf_sk);
- set_rx_flow_on(cf_sk);
-
- /* Set default options on configuration */
- cf_sk->conn_req.link_selector = CAIF_LINK_LOW_LATENCY;
- cf_sk->conn_req.protocol = protocol;
- release_sock(&cf_sk->sk);
- return 0;
-}
-
-
-static const struct net_proto_family caif_family_ops = {
- .family = PF_CAIF,
- .create = caif_create,
- .owner = THIS_MODULE,
-};
-
-static int __init caif_sktinit_module(void)
-{
- return sock_register(&caif_family_ops);
-}
-
-static void __exit caif_sktexit_module(void)
-{
- sock_unregister(PF_CAIF);
-}
-module_init(caif_sktinit_module);
-module_exit(caif_sktexit_module);
diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c
deleted file mode 100644
index 4d44960d4c2f..000000000000
--- a/net/caif/caif_usb.c
+++ /dev/null
@@ -1,216 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * CAIF USB handler
- * Copyright (C) ST-Ericsson AB 2011
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/slab.h>
-#include <linux/mii.h>
-#include <linux/usb.h>
-#include <linux/usb/usbnet.h>
-#include <linux/etherdevice.h>
-#include <net/netns/generic.h>
-#include <net/caif/caif_dev.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cfcnfg.h>
-
-MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol USB support");
-MODULE_LICENSE("GPL");
-
-#define CFUSB_PAD_DESCR_SZ 1 /* Alignment descriptor length */
-#define CFUSB_ALIGNMENT 4 /* Number of bytes to align. */
-#define CFUSB_MAX_HEADLEN (CFUSB_PAD_DESCR_SZ + CFUSB_ALIGNMENT-1)
-#define STE_USB_VID 0x04cc /* USB Product ID for ST-Ericsson */
-#define STE_USB_PID_CAIF 0x230f /* Product id for CAIF Modems */
-
-struct cfusbl {
- struct cflayer layer;
- u8 tx_eth_hdr[ETH_HLEN];
-};
-
-static bool pack_added;
-
-static int cfusbl_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 hpad;
-
- /* Remove padding. */
- cfpkt_extr_head(pkt, &hpad, 1);
- cfpkt_extr_head(pkt, NULL, hpad);
- return layr->up->receive(layr->up, pkt);
-}
-
-static int cfusbl_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct caif_payload_info *info;
- u8 hpad;
- u8 zeros[CFUSB_ALIGNMENT];
- struct sk_buff *skb;
- struct cfusbl *usbl = container_of(layr, struct cfusbl, layer);
-
- skb = cfpkt_tonative(pkt);
-
- skb_reset_network_header(skb);
- skb->protocol = htons(ETH_P_IP);
-
- info = cfpkt_info(pkt);
- hpad = (info->hdr_len + CFUSB_PAD_DESCR_SZ) & (CFUSB_ALIGNMENT - 1);
-
- if (skb_headroom(skb) < ETH_HLEN + CFUSB_PAD_DESCR_SZ + hpad) {
- pr_warn("Headroom too small\n");
- kfree_skb(skb);
- return -EIO;
- }
- memset(zeros, 0, hpad);
-
- cfpkt_add_head(pkt, zeros, hpad);
- cfpkt_add_head(pkt, &hpad, 1);
- cfpkt_add_head(pkt, usbl->tx_eth_hdr, sizeof(usbl->tx_eth_hdr));
- return layr->dn->transmit(layr->dn, pkt);
-}
-
-static void cfusbl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
-{
- if (layr->up && layr->up->ctrlcmd)
- layr->up->ctrlcmd(layr->up, ctrl, layr->id);
-}
-
-static struct cflayer *cfusbl_create(int phyid, const u8 ethaddr[ETH_ALEN],
- u8 braddr[ETH_ALEN])
-{
- struct cfusbl *this = kmalloc_obj(struct cfusbl, GFP_ATOMIC);
-
- if (!this)
- return NULL;
-
- caif_assert(offsetof(struct cfusbl, layer) == 0);
-
- memset(&this->layer, 0, sizeof(this->layer));
- this->layer.receive = cfusbl_receive;
- this->layer.transmit = cfusbl_transmit;
- this->layer.ctrlcmd = cfusbl_ctrlcmd;
- snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "usb%d", phyid);
- this->layer.id = phyid;
-
- /*
- * Construct TX ethernet header:
- * 0-5 destination address
- * 5-11 source address
- * 12-13 protocol type
- */
- ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], braddr);
- ether_addr_copy(&this->tx_eth_hdr[ETH_ALEN], ethaddr);
- this->tx_eth_hdr[12] = cpu_to_be16(ETH_P_802_EX1) & 0xff;
- this->tx_eth_hdr[13] = (cpu_to_be16(ETH_P_802_EX1) >> 8) & 0xff;
- pr_debug("caif ethernet TX-header dst:%pM src:%pM type:%02x%02x\n",
- this->tx_eth_hdr, this->tx_eth_hdr + ETH_ALEN,
- this->tx_eth_hdr[12], this->tx_eth_hdr[13]);
-
- return (struct cflayer *) this;
-}
-
-static void cfusbl_release(struct cflayer *layer)
-{
- kfree(layer);
-}
-
-static struct packet_type caif_usb_type __read_mostly = {
- .type = cpu_to_be16(ETH_P_802_EX1),
-};
-
-static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
- void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct caif_dev_common common;
- struct cflayer *layer, *link_support;
- struct usbnet *usbnet;
- struct usb_device *usbdev;
- int res;
-
- if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED)
- return 0;
-
- /* Check whether we have a NCM device, and find its VID/PID. */
- if (!(dev->dev.parent && dev->dev.parent->driver &&
- strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0))
- return 0;
-
- usbnet = netdev_priv(dev);
- usbdev = usbnet->udev;
-
- pr_debug("USB CDC NCM device VID:0x%4x PID:0x%4x\n",
- le16_to_cpu(usbdev->descriptor.idVendor),
- le16_to_cpu(usbdev->descriptor.idProduct));
-
- /* Check for VID/PID that supports CAIF */
- if (!(le16_to_cpu(usbdev->descriptor.idVendor) == STE_USB_VID &&
- le16_to_cpu(usbdev->descriptor.idProduct) == STE_USB_PID_CAIF))
- return 0;
-
- if (what == NETDEV_UNREGISTER)
- module_put(THIS_MODULE);
-
- if (what != NETDEV_REGISTER)
- return 0;
-
- __module_get(THIS_MODULE);
-
- memset(&common, 0, sizeof(common));
- common.use_frag = false;
- common.use_fcs = false;
- common.use_stx = false;
- common.link_select = CAIF_LINK_HIGH_BANDW;
- common.flowctrl = NULL;
-
- link_support = cfusbl_create(dev->ifindex, dev->dev_addr,
- dev->broadcast);
-
- if (!link_support)
- return -ENOMEM;
-
- if (dev->num_tx_queues > 1)
- pr_warn("USB device uses more than one tx queue\n");
-
- res = caif_enroll_dev(dev, &common, link_support, CFUSB_MAX_HEADLEN,
- &layer, &caif_usb_type.func);
- if (res)
- goto err;
-
- if (!pack_added)
- dev_add_pack(&caif_usb_type);
- pack_added = true;
-
- strscpy(layer->name, dev->name, sizeof(layer->name));
-
- return 0;
-err:
- cfusbl_release(link_support);
- return res;
-}
-
-static struct notifier_block caif_device_notifier = {
- .notifier_call = cfusbl_device_notify,
- .priority = 0,
-};
-
-static int __init cfusbl_init(void)
-{
- return register_netdevice_notifier(&caif_device_notifier);
-}
-
-static void __exit cfusbl_exit(void)
-{
- unregister_netdevice_notifier(&caif_device_notifier);
- dev_remove_pack(&caif_usb_type);
-}
-
-module_init(cfusbl_init);
-module_exit(cfusbl_exit);
diff --git a/net/caif/cfcnfg.c b/net/caif/cfcnfg.c
deleted file mode 100644
index 8a80914783e8..000000000000
--- a/net/caif/cfcnfg.c
+++ /dev/null
@@ -1,612 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/stddef.h>
-#include <linux/slab.h>
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cfcnfg.h>
-#include <net/caif/cfctrl.h>
-#include <net/caif/cfmuxl.h>
-#include <net/caif/cffrml.h>
-#include <net/caif/cfserl.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/caif_dev.h>
-
-#define container_obj(layr) container_of(layr, struct cfcnfg, layer)
-
-/* Information about CAIF physical interfaces held by Config Module in order
- * to manage physical interfaces
- */
-struct cfcnfg_phyinfo {
- struct list_head node;
- bool up;
-
- /* Pointer to the layer below the MUX (framing layer) */
- struct cflayer *frm_layer;
- /* Pointer to the lowest actual physical layer */
- struct cflayer *phy_layer;
- /* Unique identifier of the physical interface */
- unsigned int id;
- /* Preference of the physical in interface */
- enum cfcnfg_phy_preference pref;
-
- /* Information about the physical device */
- struct dev_info dev_info;
-
- /* Interface index */
- int ifindex;
-
- /* Protocol head room added for CAIF link layer */
- int head_room;
-
- /* Use Start of frame checksum */
- bool use_fcs;
-};
-
-struct cfcnfg {
- struct cflayer layer;
- struct cflayer *ctrl;
- struct cflayer *mux;
- struct list_head phys;
- struct mutex lock;
-};
-
-static void cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id,
- enum cfctrl_srv serv, u8 phyid,
- struct cflayer *adapt_layer);
-static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id);
-static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
- struct cflayer *adapt_layer);
-static void cfctrl_resp_func(void);
-static void cfctrl_enum_resp(void);
-
-struct cfcnfg *cfcnfg_create(void)
-{
- struct cfcnfg *this;
- struct cfctrl_rsp *resp;
-
- might_sleep();
-
- /* Initiate this layer */
- this = kzalloc_obj(struct cfcnfg, GFP_ATOMIC);
- if (!this)
- return NULL;
- this->mux = cfmuxl_create();
- if (!this->mux)
- goto out_of_mem;
- this->ctrl = cfctrl_create();
- if (!this->ctrl)
- goto out_of_mem;
- /* Initiate response functions */
- resp = cfctrl_get_respfuncs(this->ctrl);
- resp->enum_rsp = cfctrl_enum_resp;
- resp->linkerror_ind = cfctrl_resp_func;
- resp->linkdestroy_rsp = cfcnfg_linkdestroy_rsp;
- resp->sleep_rsp = cfctrl_resp_func;
- resp->wake_rsp = cfctrl_resp_func;
- resp->restart_rsp = cfctrl_resp_func;
- resp->radioset_rsp = cfctrl_resp_func;
- resp->linksetup_rsp = cfcnfg_linkup_rsp;
- resp->reject_rsp = cfcnfg_reject_rsp;
- INIT_LIST_HEAD(&this->phys);
-
- cfmuxl_set_uplayer(this->mux, this->ctrl, 0);
- layer_set_dn(this->ctrl, this->mux);
- layer_set_up(this->ctrl, this);
- mutex_init(&this->lock);
-
- return this;
-out_of_mem:
- synchronize_rcu();
-
- kfree(this->mux);
- kfree(this->ctrl);
- kfree(this);
- return NULL;
-}
-
-void cfcnfg_remove(struct cfcnfg *cfg)
-{
- might_sleep();
- if (cfg) {
- synchronize_rcu();
-
- kfree(cfg->mux);
- cfctrl_remove(cfg->ctrl);
- kfree(cfg);
- }
-}
-
-static void cfctrl_resp_func(void)
-{
-}
-
-static struct cfcnfg_phyinfo *cfcnfg_get_phyinfo_rcu(struct cfcnfg *cnfg,
- u8 phyid)
-{
- struct cfcnfg_phyinfo *phy;
-
- list_for_each_entry_rcu(phy, &cnfg->phys, node)
- if (phy->id == phyid)
- return phy;
- return NULL;
-}
-
-static void cfctrl_enum_resp(void)
-{
-}
-
-static struct dev_info *cfcnfg_get_phyid(struct cfcnfg *cnfg,
- enum cfcnfg_phy_preference phy_pref)
-{
- /* Try to match with specified preference */
- struct cfcnfg_phyinfo *phy;
-
- list_for_each_entry_rcu(phy, &cnfg->phys, node) {
- if (phy->up && phy->pref == phy_pref &&
- phy->frm_layer != NULL)
-
- return &phy->dev_info;
- }
-
- /* Otherwise just return something */
- list_for_each_entry_rcu(phy, &cnfg->phys, node)
- if (phy->up)
- return &phy->dev_info;
-
- return NULL;
-}
-
-static int cfcnfg_get_id_from_ifi(struct cfcnfg *cnfg, int ifi)
-{
- struct cfcnfg_phyinfo *phy;
-
- list_for_each_entry_rcu(phy, &cnfg->phys, node)
- if (phy->ifindex == ifi && phy->up)
- return phy->id;
- return -ENODEV;
-}
-
-int caif_disconnect_client(struct net *net, struct cflayer *adap_layer)
-{
- u8 channel_id;
- struct cfcnfg *cfg = get_cfcnfg(net);
-
- caif_assert(adap_layer != NULL);
- cfctrl_cancel_req(cfg->ctrl, adap_layer);
- channel_id = adap_layer->id;
- if (channel_id != 0) {
- struct cflayer *servl;
- servl = cfmuxl_remove_uplayer(cfg->mux, channel_id);
- cfctrl_linkdown_req(cfg->ctrl, channel_id, adap_layer);
- if (servl != NULL)
- layer_set_up(servl, NULL);
- } else
- pr_debug("nothing to disconnect\n");
-
- /* Do RCU sync before initiating cleanup */
- synchronize_rcu();
- if (adap_layer->ctrlcmd != NULL)
- adap_layer->ctrlcmd(adap_layer, CAIF_CTRLCMD_DEINIT_RSP, 0);
- return 0;
-
-}
-EXPORT_SYMBOL(caif_disconnect_client);
-
-static void cfcnfg_linkdestroy_rsp(struct cflayer *layer, u8 channel_id)
-{
-}
-
-static const int protohead[CFCTRL_SRV_MASK] = {
- [CFCTRL_SRV_VEI] = 4,
- [CFCTRL_SRV_DATAGRAM] = 7,
- [CFCTRL_SRV_UTIL] = 4,
- [CFCTRL_SRV_RFM] = 3,
- [CFCTRL_SRV_DBG] = 3,
-};
-
-
-static int caif_connect_req_to_link_param(struct cfcnfg *cnfg,
- struct caif_connect_request *s,
- struct cfctrl_link_param *l)
-{
- struct dev_info *dev_info;
- enum cfcnfg_phy_preference pref;
- int res;
-
- memset(l, 0, sizeof(*l));
- /* In caif protocol low value is high priority */
- l->priority = CAIF_PRIO_MAX - s->priority + 1;
-
- if (s->ifindex != 0) {
- res = cfcnfg_get_id_from_ifi(cnfg, s->ifindex);
- if (res < 0)
- return res;
- l->phyid = res;
- } else {
- switch (s->link_selector) {
- case CAIF_LINK_HIGH_BANDW:
- pref = CFPHYPREF_HIGH_BW;
- break;
- case CAIF_LINK_LOW_LATENCY:
- pref = CFPHYPREF_LOW_LAT;
- break;
- default:
- return -EINVAL;
- }
- dev_info = cfcnfg_get_phyid(cnfg, pref);
- if (dev_info == NULL)
- return -ENODEV;
- l->phyid = dev_info->id;
- }
- switch (s->protocol) {
- case CAIFPROTO_AT:
- l->linktype = CFCTRL_SRV_VEI;
- l->endpoint = (s->sockaddr.u.at.type >> 2) & 0x3;
- l->chtype = s->sockaddr.u.at.type & 0x3;
- break;
- case CAIFPROTO_DATAGRAM:
- l->linktype = CFCTRL_SRV_DATAGRAM;
- l->chtype = 0x00;
- l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
- break;
- case CAIFPROTO_DATAGRAM_LOOP:
- l->linktype = CFCTRL_SRV_DATAGRAM;
- l->chtype = 0x03;
- l->endpoint = 0x00;
- l->u.datagram.connid = s->sockaddr.u.dgm.connection_id;
- break;
- case CAIFPROTO_RFM:
- l->linktype = CFCTRL_SRV_RFM;
- l->u.datagram.connid = s->sockaddr.u.rfm.connection_id;
- strscpy(l->u.rfm.volume, s->sockaddr.u.rfm.volume,
- sizeof(l->u.rfm.volume));
- break;
- case CAIFPROTO_UTIL:
- l->linktype = CFCTRL_SRV_UTIL;
- l->endpoint = 0x00;
- l->chtype = 0x00;
- strscpy(l->u.utility.name, s->sockaddr.u.util.service,
- sizeof(l->u.utility.name));
- caif_assert(sizeof(l->u.utility.name) > 10);
- l->u.utility.paramlen = s->param.size;
- if (l->u.utility.paramlen > sizeof(l->u.utility.params))
- l->u.utility.paramlen = sizeof(l->u.utility.params);
-
- memcpy(l->u.utility.params, s->param.data,
- l->u.utility.paramlen);
-
- break;
- case CAIFPROTO_DEBUG:
- l->linktype = CFCTRL_SRV_DBG;
- l->endpoint = s->sockaddr.u.dbg.service;
- l->chtype = s->sockaddr.u.dbg.type;
- break;
- default:
- return -EINVAL;
- }
- return 0;
-}
-
-int caif_connect_client(struct net *net, struct caif_connect_request *conn_req,
- struct cflayer *adap_layer, int *ifindex,
- int *proto_head, int *proto_tail)
-{
- struct cflayer *frml;
- struct cfcnfg_phyinfo *phy;
- int err;
- struct cfctrl_link_param param;
- struct cfcnfg *cfg = get_cfcnfg(net);
-
- rcu_read_lock();
- err = caif_connect_req_to_link_param(cfg, conn_req, ¶m);
- if (err)
- goto unlock;
-
- phy = cfcnfg_get_phyinfo_rcu(cfg, param.phyid);
- if (!phy) {
- err = -ENODEV;
- goto unlock;
- }
- err = -EINVAL;
-
- if (adap_layer == NULL) {
- pr_err("adap_layer is zero\n");
- goto unlock;
- }
- if (adap_layer->receive == NULL) {
- pr_err("adap_layer->receive is NULL\n");
- goto unlock;
- }
- if (adap_layer->ctrlcmd == NULL) {
- pr_err("adap_layer->ctrlcmd == NULL\n");
- goto unlock;
- }
-
- err = -ENODEV;
- frml = phy->frm_layer;
- if (frml == NULL) {
- pr_err("Specified PHY type does not exist!\n");
- goto unlock;
- }
- caif_assert(param.phyid == phy->id);
- caif_assert(phy->frm_layer->id ==
- param.phyid);
- caif_assert(phy->phy_layer->id ==
- param.phyid);
-
- *ifindex = phy->ifindex;
- *proto_tail = 2;
- *proto_head = protohead[param.linktype] + phy->head_room;
-
- rcu_read_unlock();
-
- /* FIXME: ENUMERATE INITIALLY WHEN ACTIVATING PHYSICAL INTERFACE */
- cfctrl_enum_req(cfg->ctrl, param.phyid);
- return cfctrl_linkup_request(cfg->ctrl, ¶m, adap_layer);
-
-unlock:
- rcu_read_unlock();
- return err;
-}
-EXPORT_SYMBOL(caif_connect_client);
-
-static void cfcnfg_reject_rsp(struct cflayer *layer, u8 channel_id,
- struct cflayer *adapt_layer)
-{
- if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
- adapt_layer->ctrlcmd(adapt_layer,
- CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
-}
-
-static void
-cfcnfg_linkup_rsp(struct cflayer *layer, u8 channel_id, enum cfctrl_srv serv,
- u8 phyid, struct cflayer *adapt_layer)
-{
- struct cfcnfg *cnfg = container_obj(layer);
- struct cflayer *servicel = NULL;
- struct cfcnfg_phyinfo *phyinfo;
- struct net_device *netdev;
-
- if (channel_id == 0) {
- pr_warn("received channel_id zero\n");
- if (adapt_layer != NULL && adapt_layer->ctrlcmd != NULL)
- adapt_layer->ctrlcmd(adapt_layer,
- CAIF_CTRLCMD_INIT_FAIL_RSP, 0);
- return;
- }
-
- rcu_read_lock();
-
- if (adapt_layer == NULL) {
- pr_debug("link setup response but no client exist, send linkdown back\n");
- cfctrl_linkdown_req(cnfg->ctrl, channel_id, NULL);
- goto unlock;
- }
-
- caif_assert(cnfg != NULL);
- caif_assert(phyid != 0);
-
- phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
- if (phyinfo == NULL) {
- pr_err("ERROR: Link Layer Device disappeared while connecting\n");
- goto unlock;
- }
-
- caif_assert(phyinfo != NULL);
- caif_assert(phyinfo->id == phyid);
- caif_assert(phyinfo->phy_layer != NULL);
- caif_assert(phyinfo->phy_layer->id == phyid);
-
- adapt_layer->id = channel_id;
-
- switch (serv) {
- case CFCTRL_SRV_VEI:
- servicel = cfvei_create(channel_id, &phyinfo->dev_info);
- break;
- case CFCTRL_SRV_DATAGRAM:
- servicel = cfdgml_create(channel_id,
- &phyinfo->dev_info);
- break;
- case CFCTRL_SRV_RFM:
- netdev = phyinfo->dev_info.dev;
- servicel = cfrfml_create(channel_id, &phyinfo->dev_info,
- netdev->mtu);
- break;
- case CFCTRL_SRV_UTIL:
- servicel = cfutill_create(channel_id, &phyinfo->dev_info);
- break;
- case CFCTRL_SRV_VIDEO:
- servicel = cfvidl_create(channel_id, &phyinfo->dev_info);
- break;
- case CFCTRL_SRV_DBG:
- servicel = cfdbgl_create(channel_id, &phyinfo->dev_info);
- break;
- default:
- pr_err("Protocol error. Link setup response - unknown channel type\n");
- goto unlock;
- }
- if (!servicel)
- goto unlock;
- layer_set_dn(servicel, cnfg->mux);
- cfmuxl_set_uplayer(cnfg->mux, servicel, channel_id);
- layer_set_up(servicel, adapt_layer);
- layer_set_dn(adapt_layer, servicel);
-
- rcu_read_unlock();
-
- servicel->ctrlcmd(servicel, CAIF_CTRLCMD_INIT_RSP, 0);
- return;
-unlock:
- rcu_read_unlock();
-}
-
-int
-cfcnfg_add_phy_layer(struct cfcnfg *cnfg,
- struct net_device *dev, struct cflayer *phy_layer,
- enum cfcnfg_phy_preference pref,
- struct cflayer *link_support,
- bool fcs, int head_room)
-{
- struct cflayer *frml;
- struct cfcnfg_phyinfo *phyinfo = NULL;
- int i, res = 0;
- u8 phyid;
-
- mutex_lock(&cnfg->lock);
-
- /* CAIF protocol allow maximum 6 link-layers */
- for (i = 0; i < 7; i++) {
- phyid = (dev->ifindex + i) & 0x7;
- if (phyid == 0)
- continue;
- if (cfcnfg_get_phyinfo_rcu(cnfg, phyid) == NULL)
- goto got_phyid;
- }
- pr_warn("Too many CAIF Link Layers (max 6)\n");
- res = -EEXIST;
- goto out;
-
-got_phyid:
- phyinfo = kzalloc_obj(struct cfcnfg_phyinfo, GFP_ATOMIC);
- if (!phyinfo) {
- res = -ENOMEM;
- goto out;
- }
-
- phy_layer->id = phyid;
- phyinfo->pref = pref;
- phyinfo->id = phyid;
- phyinfo->dev_info.id = phyid;
- phyinfo->dev_info.dev = dev;
- phyinfo->phy_layer = phy_layer;
- phyinfo->ifindex = dev->ifindex;
- phyinfo->head_room = head_room;
- phyinfo->use_fcs = fcs;
-
- frml = cffrml_create(phyid, fcs);
-
- if (!frml) {
- res = -ENOMEM;
- goto out_err;
- }
- phyinfo->frm_layer = frml;
- layer_set_up(frml, cnfg->mux);
-
- if (link_support != NULL) {
- link_support->id = phyid;
- layer_set_dn(frml, link_support);
- layer_set_up(link_support, frml);
- layer_set_dn(link_support, phy_layer);
- layer_set_up(phy_layer, link_support);
- } else {
- layer_set_dn(frml, phy_layer);
- layer_set_up(phy_layer, frml);
- }
-
- list_add_rcu(&phyinfo->node, &cnfg->phys);
-out:
- mutex_unlock(&cnfg->lock);
- return res;
-
-out_err:
- kfree(phyinfo);
- mutex_unlock(&cnfg->lock);
- return res;
-}
-EXPORT_SYMBOL(cfcnfg_add_phy_layer);
-
-int cfcnfg_set_phy_state(struct cfcnfg *cnfg, struct cflayer *phy_layer,
- bool up)
-{
- struct cfcnfg_phyinfo *phyinfo;
-
- rcu_read_lock();
- phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phy_layer->id);
- if (phyinfo == NULL) {
- rcu_read_unlock();
- return -ENODEV;
- }
-
- if (phyinfo->up == up) {
- rcu_read_unlock();
- return 0;
- }
- phyinfo->up = up;
-
- if (up) {
- cffrml_hold(phyinfo->frm_layer);
- cfmuxl_set_dnlayer(cnfg->mux, phyinfo->frm_layer,
- phy_layer->id);
- } else {
- cfmuxl_remove_dnlayer(cnfg->mux, phy_layer->id);
- cffrml_put(phyinfo->frm_layer);
- }
-
- rcu_read_unlock();
- return 0;
-}
-EXPORT_SYMBOL(cfcnfg_set_phy_state);
-
-int cfcnfg_del_phy_layer(struct cfcnfg *cnfg, struct cflayer *phy_layer)
-{
- struct cflayer *frml, *frml_dn;
- u16 phyid;
- struct cfcnfg_phyinfo *phyinfo;
-
- might_sleep();
-
- mutex_lock(&cnfg->lock);
-
- phyid = phy_layer->id;
- phyinfo = cfcnfg_get_phyinfo_rcu(cnfg, phyid);
-
- if (phyinfo == NULL) {
- mutex_unlock(&cnfg->lock);
- return 0;
- }
- caif_assert(phyid == phyinfo->id);
- caif_assert(phy_layer == phyinfo->phy_layer);
- caif_assert(phy_layer->id == phyid);
- caif_assert(phyinfo->frm_layer->id == phyid);
-
- list_del_rcu(&phyinfo->node);
- synchronize_rcu();
-
- /* Fail if reference count is not zero */
- if (cffrml_refcnt_read(phyinfo->frm_layer) != 0) {
- pr_info("Wait for device inuse\n");
- list_add_rcu(&phyinfo->node, &cnfg->phys);
- mutex_unlock(&cnfg->lock);
- return -EAGAIN;
- }
-
- frml = phyinfo->frm_layer;
- frml_dn = frml->dn;
- cffrml_set_uplayer(frml, NULL);
- cffrml_set_dnlayer(frml, NULL);
- if (phy_layer != frml_dn) {
- layer_set_up(frml_dn, NULL);
- layer_set_dn(frml_dn, NULL);
- }
- layer_set_up(phy_layer, NULL);
-
- if (phyinfo->phy_layer != frml_dn)
- kfree(frml_dn);
-
- cffrml_free(frml);
- kfree(phyinfo);
- mutex_unlock(&cnfg->lock);
-
- return 0;
-}
-EXPORT_SYMBOL(cfcnfg_del_phy_layer);
diff --git a/net/caif/cfctrl.c b/net/caif/cfctrl.c
deleted file mode 100644
index c6cc2bfed65d..000000000000
--- a/net/caif/cfctrl.c
+++ /dev/null
@@ -1,631 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/pkt_sched.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cfctrl.h>
-
-#define container_obj(layr) container_of(layr, struct cfctrl, serv.layer)
-#define UTILITY_NAME_LENGTH 16
-#define CFPKT_CTRL_PKT_LEN 20
-
-#ifdef CAIF_NO_LOOP
-static int handle_loop(struct cfctrl *ctrl,
- int cmd, struct cfpkt *pkt){
- return -1;
-}
-#else
-static int handle_loop(struct cfctrl *ctrl,
- int cmd, struct cfpkt *pkt);
-#endif
-static int cfctrl_recv(struct cflayer *layr, struct cfpkt *pkt);
-static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
-
-
-struct cflayer *cfctrl_create(void)
-{
- struct dev_info dev_info;
- struct cfctrl *this =
- kzalloc_obj(struct cfctrl, GFP_ATOMIC);
- if (!this)
- return NULL;
- caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
- memset(&dev_info, 0, sizeof(dev_info));
- dev_info.id = 0xff;
- cfsrvl_init(&this->serv, 0, &dev_info, false);
- atomic_set(&this->req_seq_no, 1);
- atomic_set(&this->rsp_seq_no, 1);
- this->serv.layer.receive = cfctrl_recv;
- sprintf(this->serv.layer.name, "ctrl");
- this->serv.layer.ctrlcmd = cfctrl_ctrlcmd;
-#ifndef CAIF_NO_LOOP
- spin_lock_init(&this->loop_linkid_lock);
- this->loop_linkid = 1;
-#endif
- spin_lock_init(&this->info_list_lock);
- INIT_LIST_HEAD(&this->list);
- return &this->serv.layer;
-}
-
-void cfctrl_remove(struct cflayer *layer)
-{
- struct cfctrl_request_info *p, *tmp;
- struct cfctrl *ctrl = container_obj(layer);
-
- spin_lock_bh(&ctrl->info_list_lock);
- list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
- list_del(&p->list);
- kfree(p);
- }
- spin_unlock_bh(&ctrl->info_list_lock);
- kfree(layer);
-}
-
-static bool param_eq(const struct cfctrl_link_param *p1,
- const struct cfctrl_link_param *p2)
-{
- bool eq =
- p1->linktype == p2->linktype &&
- p1->priority == p2->priority &&
- p1->phyid == p2->phyid &&
- p1->endpoint == p2->endpoint && p1->chtype == p2->chtype;
-
- if (!eq)
- return false;
-
- switch (p1->linktype) {
- case CFCTRL_SRV_VEI:
- return true;
- case CFCTRL_SRV_DATAGRAM:
- return p1->u.datagram.connid == p2->u.datagram.connid;
- case CFCTRL_SRV_RFM:
- return
- p1->u.rfm.connid == p2->u.rfm.connid &&
- strcmp(p1->u.rfm.volume, p2->u.rfm.volume) == 0;
- case CFCTRL_SRV_UTIL:
- return
- p1->u.utility.fifosize_kb == p2->u.utility.fifosize_kb
- && p1->u.utility.fifosize_bufs ==
- p2->u.utility.fifosize_bufs
- && strcmp(p1->u.utility.name, p2->u.utility.name) == 0
- && p1->u.utility.paramlen == p2->u.utility.paramlen
- && memcmp(p1->u.utility.params, p2->u.utility.params,
- p1->u.utility.paramlen) == 0;
-
- case CFCTRL_SRV_VIDEO:
- return p1->u.video.connid == p2->u.video.connid;
- case CFCTRL_SRV_DBG:
- return true;
- case CFCTRL_SRV_DECM:
- return false;
- default:
- return false;
- }
- return false;
-}
-
-static bool cfctrl_req_eq(const struct cfctrl_request_info *r1,
- const struct cfctrl_request_info *r2)
-{
- if (r1->cmd != r2->cmd)
- return false;
- if (r1->cmd == CFCTRL_CMD_LINK_SETUP)
- return param_eq(&r1->param, &r2->param);
- else
- return r1->channel_id == r2->channel_id;
-}
-
-/* Insert request at the end */
-static void cfctrl_insert_req(struct cfctrl *ctrl,
- struct cfctrl_request_info *req)
-{
- spin_lock_bh(&ctrl->info_list_lock);
- atomic_inc(&ctrl->req_seq_no);
- req->sequence_no = atomic_read(&ctrl->req_seq_no);
- list_add_tail(&req->list, &ctrl->list);
- spin_unlock_bh(&ctrl->info_list_lock);
-}
-
-/* Compare and remove request */
-static struct cfctrl_request_info *cfctrl_remove_req(struct cfctrl *ctrl,
- struct cfctrl_request_info *req)
-{
- struct cfctrl_request_info *p, *tmp, *first;
-
- first = list_first_entry(&ctrl->list, struct cfctrl_request_info, list);
-
- list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
- if (cfctrl_req_eq(req, p)) {
- if (p != first)
- pr_warn("Requests are not received in order\n");
-
- atomic_set(&ctrl->rsp_seq_no,
- p->sequence_no);
- list_del(&p->list);
- goto out;
- }
- }
- p = NULL;
-out:
- return p;
-}
-
-struct cfctrl_rsp *cfctrl_get_respfuncs(struct cflayer *layer)
-{
- struct cfctrl *this = container_obj(layer);
- return &this->res;
-}
-
-static void init_info(struct caif_payload_info *info, struct cfctrl *cfctrl)
-{
- info->hdr_len = 0;
- info->channel_id = cfctrl->serv.layer.id;
- info->dev_info = &cfctrl->serv.dev_info;
-}
-
-void cfctrl_enum_req(struct cflayer *layer, u8 physlinkid)
-{
- struct cfpkt *pkt;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cflayer *dn = cfctrl->serv.layer.dn;
-
- if (!dn) {
- pr_debug("not able to send enum request\n");
- return;
- }
- pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt)
- return;
- caif_assert(offsetof(struct cfctrl, serv.layer) == 0);
- init_info(cfpkt_info(pkt), cfctrl);
- cfpkt_info(pkt)->dev_info->id = physlinkid;
- cfctrl->serv.dev_info.id = physlinkid;
- cfpkt_addbdy(pkt, CFCTRL_CMD_ENUM);
- cfpkt_addbdy(pkt, physlinkid);
- cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
- dn->transmit(dn, pkt);
-}
-
-int cfctrl_linkup_request(struct cflayer *layer,
- struct cfctrl_link_param *param,
- struct cflayer *user_layer)
-{
- struct cfctrl *cfctrl = container_obj(layer);
- struct cflayer *dn = cfctrl->serv.layer.dn;
- char utility_name[UTILITY_NAME_LENGTH];
- struct cfctrl_request_info *req;
- struct cfpkt *pkt;
- u32 tmp32;
- u16 tmp16;
- u8 tmp8;
- int ret;
-
- if (!dn) {
- pr_debug("not able to send linkup request\n");
- return -ENODEV;
- }
-
- if (cfctrl_cancel_req(layer, user_layer) > 0) {
- /* Slight Paranoia, check if already connecting */
- pr_err("Duplicate connect request for same client\n");
- WARN_ON(1);
- return -EALREADY;
- }
-
- pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt)
- return -ENOMEM;
- cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_SETUP);
- cfpkt_addbdy(pkt, (param->chtype << 4) | param->linktype);
- cfpkt_addbdy(pkt, (param->priority << 3) | param->phyid);
- cfpkt_addbdy(pkt, param->endpoint & 0x03);
-
- switch (param->linktype) {
- case CFCTRL_SRV_VEI:
- break;
- case CFCTRL_SRV_VIDEO:
- cfpkt_addbdy(pkt, (u8) param->u.video.connid);
- break;
- case CFCTRL_SRV_DBG:
- break;
- case CFCTRL_SRV_DATAGRAM:
- tmp32 = cpu_to_le32(param->u.datagram.connid);
- cfpkt_add_body(pkt, &tmp32, 4);
- break;
- case CFCTRL_SRV_RFM:
- /* Construct a frame, convert DatagramConnectionID to network
- * format long and copy it out...
- */
- tmp32 = cpu_to_le32(param->u.rfm.connid);
- cfpkt_add_body(pkt, &tmp32, 4);
- /* Add volume name, including zero termination... */
- cfpkt_add_body(pkt, param->u.rfm.volume,
- strlen(param->u.rfm.volume) + 1);
- break;
- case CFCTRL_SRV_UTIL:
- tmp16 = cpu_to_le16(param->u.utility.fifosize_kb);
- cfpkt_add_body(pkt, &tmp16, 2);
- tmp16 = cpu_to_le16(param->u.utility.fifosize_bufs);
- cfpkt_add_body(pkt, &tmp16, 2);
- strscpy_pad(utility_name, param->u.utility.name);
- cfpkt_add_body(pkt, utility_name, UTILITY_NAME_LENGTH);
- tmp8 = param->u.utility.paramlen;
- cfpkt_add_body(pkt, &tmp8, 1);
- cfpkt_add_body(pkt, param->u.utility.params,
- param->u.utility.paramlen);
- break;
- default:
- pr_warn("Request setup of bad link type = %d\n",
- param->linktype);
- cfpkt_destroy(pkt);
- return -EINVAL;
- }
- req = kzalloc_obj(*req);
- if (!req) {
- cfpkt_destroy(pkt);
- return -ENOMEM;
- }
-
- req->client_layer = user_layer;
- req->cmd = CFCTRL_CMD_LINK_SETUP;
- req->param = *param;
- cfctrl_insert_req(cfctrl, req);
- init_info(cfpkt_info(pkt), cfctrl);
- /*
- * NOTE:Always send linkup and linkdown request on the same
- * device as the payload. Otherwise old queued up payload
- * might arrive with the newly allocated channel ID.
- */
- cfpkt_info(pkt)->dev_info->id = param->phyid;
- cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
- ret =
- dn->transmit(dn, pkt);
- if (ret < 0) {
- int count;
-
- count = cfctrl_cancel_req(&cfctrl->serv.layer,
- user_layer);
- if (count != 1) {
- pr_err("Could not remove request (%d)", count);
- return -ENODEV;
- }
- }
- return 0;
-}
-
-int cfctrl_linkdown_req(struct cflayer *layer, u8 channelid,
- struct cflayer *client)
-{
- int ret;
- struct cfpkt *pkt;
- struct cfctrl *cfctrl = container_obj(layer);
- struct cflayer *dn = cfctrl->serv.layer.dn;
-
- if (!dn) {
- pr_debug("not able to send link-down request\n");
- return -ENODEV;
- }
- pkt = cfpkt_create(CFPKT_CTRL_PKT_LEN);
- if (!pkt)
- return -ENOMEM;
- cfpkt_addbdy(pkt, CFCTRL_CMD_LINK_DESTROY);
- cfpkt_addbdy(pkt, channelid);
- init_info(cfpkt_info(pkt), cfctrl);
- cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
- ret =
- dn->transmit(dn, pkt);
-#ifndef CAIF_NO_LOOP
- cfctrl->loop_linkused[channelid] = 0;
-#endif
- return ret;
-}
-
-int cfctrl_cancel_req(struct cflayer *layr, struct cflayer *adap_layer)
-{
- struct cfctrl_request_info *p, *tmp;
- struct cfctrl *ctrl = container_obj(layr);
- int found = 0;
- spin_lock_bh(&ctrl->info_list_lock);
-
- list_for_each_entry_safe(p, tmp, &ctrl->list, list) {
- if (p->client_layer == adap_layer) {
- list_del(&p->list);
- kfree(p);
- found++;
- }
- }
-
- spin_unlock_bh(&ctrl->info_list_lock);
- return found;
-}
-
-static int cfctrl_link_setup(struct cfctrl *cfctrl, struct cfpkt *pkt, u8 cmdrsp)
-{
- u8 len;
- u8 linkid = 0;
- enum cfctrl_srv serv;
- enum cfctrl_srv servtype;
- u8 endpoint;
- u8 physlinkid;
- u8 prio;
- u8 tmp;
- u8 *cp;
- int i;
- struct cfctrl_link_param linkparam;
- struct cfctrl_request_info rsp, *req;
-
- memset(&linkparam, 0, sizeof(linkparam));
-
- tmp = cfpkt_extr_head_u8(pkt);
-
- serv = tmp & CFCTRL_SRV_MASK;
- linkparam.linktype = serv;
-
- servtype = tmp >> 4;
- linkparam.chtype = servtype;
-
- tmp = cfpkt_extr_head_u8(pkt);
- physlinkid = tmp & 0x07;
- prio = tmp >> 3;
-
- linkparam.priority = prio;
- linkparam.phyid = physlinkid;
- endpoint = cfpkt_extr_head_u8(pkt);
- linkparam.endpoint = endpoint & 0x03;
-
- switch (serv) {
- case CFCTRL_SRV_VEI:
- case CFCTRL_SRV_DBG:
- if (CFCTRL_ERR_BIT & cmdrsp)
- break;
- /* Link ID */
- linkid = cfpkt_extr_head_u8(pkt);
- break;
- case CFCTRL_SRV_VIDEO:
- tmp = cfpkt_extr_head_u8(pkt);
- linkparam.u.video.connid = tmp;
- if (CFCTRL_ERR_BIT & cmdrsp)
- break;
- /* Link ID */
- linkid = cfpkt_extr_head_u8(pkt);
- break;
-
- case CFCTRL_SRV_DATAGRAM:
- linkparam.u.datagram.connid = cfpkt_extr_head_u32(pkt);
- if (CFCTRL_ERR_BIT & cmdrsp)
- break;
- /* Link ID */
- linkid = cfpkt_extr_head_u8(pkt);
- break;
- case CFCTRL_SRV_RFM:
- /* Construct a frame, convert
- * DatagramConnectionID
- * to network format long and copy it out...
- */
- linkparam.u.rfm.connid = cfpkt_extr_head_u32(pkt);
- cp = (u8 *) linkparam.u.rfm.volume;
- for (tmp = cfpkt_extr_head_u8(pkt);
- cfpkt_more(pkt) && tmp != '\0';
- tmp = cfpkt_extr_head_u8(pkt))
- *cp++ = tmp;
- *cp = '\0';
-
- if (CFCTRL_ERR_BIT & cmdrsp)
- break;
- /* Link ID */
- linkid = cfpkt_extr_head_u8(pkt);
-
- break;
- case CFCTRL_SRV_UTIL:
- /* Construct a frame, convert
- * DatagramConnectionID
- * to network format long and copy it out...
- */
- /* Fifosize KB */
- linkparam.u.utility.fifosize_kb = cfpkt_extr_head_u16(pkt);
- /* Fifosize bufs */
- linkparam.u.utility.fifosize_bufs = cfpkt_extr_head_u16(pkt);
- /* name */
- cp = (u8 *) linkparam.u.utility.name;
- caif_assert(sizeof(linkparam.u.utility.name)
- >= UTILITY_NAME_LENGTH);
- for (i = 0; i < UTILITY_NAME_LENGTH && cfpkt_more(pkt); i++) {
- tmp = cfpkt_extr_head_u8(pkt);
- *cp++ = tmp;
- }
- /* Length */
- len = cfpkt_extr_head_u8(pkt);
- linkparam.u.utility.paramlen = len;
- /* Param Data */
- cp = linkparam.u.utility.params;
- while (cfpkt_more(pkt) && len--) {
- tmp = cfpkt_extr_head_u8(pkt);
- *cp++ = tmp;
- }
- if (CFCTRL_ERR_BIT & cmdrsp)
- break;
- /* Link ID */
- linkid = cfpkt_extr_head_u8(pkt);
- /* Length */
- len = cfpkt_extr_head_u8(pkt);
- /* Param Data */
- cfpkt_extr_head(pkt, NULL, len);
- break;
- default:
- pr_warn("Request setup, invalid type (%d)\n", serv);
- return -1;
- }
-
- rsp.cmd = CFCTRL_CMD_LINK_SETUP;
- rsp.param = linkparam;
- spin_lock_bh(&cfctrl->info_list_lock);
- req = cfctrl_remove_req(cfctrl, &rsp);
-
- if (CFCTRL_ERR_BIT == (CFCTRL_ERR_BIT & cmdrsp) ||
- cfpkt_erroneous(pkt)) {
- pr_err("Invalid O/E bit or parse error "
- "on CAIF control channel\n");
- cfctrl->res.reject_rsp(cfctrl->serv.layer.up, 0,
- req ? req->client_layer : NULL);
- } else {
- cfctrl->res.linksetup_rsp(cfctrl->serv.layer.up, linkid,
- serv, physlinkid,
- req ? req->client_layer : NULL);
- }
-
- kfree(req);
-
- spin_unlock_bh(&cfctrl->info_list_lock);
-
- return 0;
-}
-
-static int cfctrl_recv(struct cflayer *layer, struct cfpkt *pkt)
-{
- u8 cmdrsp;
- u8 cmd;
- int ret = 0;
- u8 linkid = 0;
- struct cfctrl *cfctrl = container_obj(layer);
-
- cmdrsp = cfpkt_extr_head_u8(pkt);
- cmd = cmdrsp & CFCTRL_CMD_MASK;
- if (cmd != CFCTRL_CMD_LINK_ERR
- && CFCTRL_RSP_BIT != (CFCTRL_RSP_BIT & cmdrsp)
- && CFCTRL_ERR_BIT != (CFCTRL_ERR_BIT & cmdrsp)) {
- if (handle_loop(cfctrl, cmd, pkt) != 0)
- cmdrsp |= CFCTRL_ERR_BIT;
- }
-
- switch (cmd) {
- case CFCTRL_CMD_LINK_SETUP:
- ret = cfctrl_link_setup(cfctrl, pkt, cmdrsp);
- break;
- case CFCTRL_CMD_LINK_DESTROY:
- linkid = cfpkt_extr_head_u8(pkt);
- cfctrl->res.linkdestroy_rsp(cfctrl->serv.layer.up, linkid);
- break;
- case CFCTRL_CMD_LINK_ERR:
- pr_err("Frame Error Indication received\n");
- cfctrl->res.linkerror_ind();
- break;
- case CFCTRL_CMD_ENUM:
- cfctrl->res.enum_rsp();
- break;
- case CFCTRL_CMD_SLEEP:
- cfctrl->res.sleep_rsp();
- break;
- case CFCTRL_CMD_WAKE:
- cfctrl->res.wake_rsp();
- break;
- case CFCTRL_CMD_LINK_RECONF:
- cfctrl->res.restart_rsp();
- break;
- case CFCTRL_CMD_RADIO_SET:
- cfctrl->res.radioset_rsp();
- break;
- default:
- pr_err("Unrecognized Control Frame\n");
- ret = -1;
- goto error;
- }
-error:
- cfpkt_destroy(pkt);
- return ret;
-}
-
-static void cfctrl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
-{
- struct cfctrl *this = container_obj(layr);
- switch (ctrl) {
- case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
- case CAIF_CTRLCMD_FLOW_OFF_IND:
- spin_lock_bh(&this->info_list_lock);
- if (!list_empty(&this->list))
- pr_debug("Received flow off in control layer\n");
- spin_unlock_bh(&this->info_list_lock);
- break;
- case _CAIF_CTRLCMD_PHYIF_DOWN_IND: {
- struct cfctrl_request_info *p, *tmp;
-
- /* Find all connect request and report failure */
- spin_lock_bh(&this->info_list_lock);
- list_for_each_entry_safe(p, tmp, &this->list, list) {
- if (p->param.phyid == phyid) {
- list_del(&p->list);
- p->client_layer->ctrlcmd(p->client_layer,
- CAIF_CTRLCMD_INIT_FAIL_RSP,
- phyid);
- kfree(p);
- }
- }
- spin_unlock_bh(&this->info_list_lock);
- break;
- }
- default:
- break;
- }
-}
-
-#ifndef CAIF_NO_LOOP
-static int handle_loop(struct cfctrl *ctrl, int cmd, struct cfpkt *pkt)
-{
- static int last_linkid;
- static int dec;
- u8 linkid, linktype, tmp;
- switch (cmd) {
- case CFCTRL_CMD_LINK_SETUP:
- spin_lock_bh(&ctrl->loop_linkid_lock);
- if (!dec) {
- for (linkid = last_linkid + 1; linkid < 254; linkid++)
- if (!ctrl->loop_linkused[linkid])
- goto found;
- }
- dec = 1;
- for (linkid = last_linkid - 1; linkid > 1; linkid--)
- if (!ctrl->loop_linkused[linkid])
- goto found;
- spin_unlock_bh(&ctrl->loop_linkid_lock);
- return -1;
-found:
- if (linkid < 10)
- dec = 0;
-
- if (!ctrl->loop_linkused[linkid])
- ctrl->loop_linkused[linkid] = 1;
-
- last_linkid = linkid;
-
- cfpkt_add_trail(pkt, &linkid, 1);
- spin_unlock_bh(&ctrl->loop_linkid_lock);
- cfpkt_peek_head(pkt, &linktype, 1);
- if (linktype == CFCTRL_SRV_UTIL) {
- tmp = 0x01;
- cfpkt_add_trail(pkt, &tmp, 1);
- cfpkt_add_trail(pkt, &tmp, 1);
- }
- break;
-
- case CFCTRL_CMD_LINK_DESTROY:
- spin_lock_bh(&ctrl->loop_linkid_lock);
- cfpkt_peek_head(pkt, &linkid, 1);
- ctrl->loop_linkused[linkid] = 0;
- spin_unlock_bh(&ctrl->loop_linkid_lock);
- break;
- default:
- break;
- }
- return 0;
-}
-#endif
diff --git a/net/caif/cfdbgl.c b/net/caif/cfdbgl.c
deleted file mode 100644
index 57ad3f82e004..000000000000
--- a/net/caif/cfdbgl.c
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/slab.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-
-#define container_obj(layr) ((struct cfsrvl *) layr)
-
-static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt);
-
-struct cflayer *cfdbgl_create(u8 channel_id, struct dev_info *dev_info)
-{
- struct cfsrvl *dbg = kzalloc_obj(struct cfsrvl, GFP_ATOMIC);
- if (!dbg)
- return NULL;
- caif_assert(offsetof(struct cfsrvl, layer) == 0);
- cfsrvl_init(dbg, channel_id, dev_info, false);
- dbg->layer.receive = cfdbgl_receive;
- dbg->layer.transmit = cfdbgl_transmit;
- snprintf(dbg->layer.name, CAIF_LAYER_NAME_SZ, "dbg%d", channel_id);
- return &dbg->layer;
-}
-
-static int cfdbgl_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- return layr->up->receive(layr->up, pkt);
-}
-
-static int cfdbgl_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct cfsrvl *service = container_obj(layr);
- struct caif_payload_info *info;
- int ret;
-
- if (!cfsrvl_ready(service, &ret)) {
- cfpkt_destroy(pkt);
- return ret;
- }
-
- /* Add info for MUX-layer to route the packet out */
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- info->dev_info = &service->dev_info;
-
- return layr->dn->transmit(layr->dn, pkt);
-}
diff --git a/net/caif/cfdgml.c b/net/caif/cfdgml.c
deleted file mode 100644
index c451ddd155a7..000000000000
--- a/net/caif/cfdgml.c
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-
-
-#define container_obj(layr) ((struct cfsrvl *) layr)
-
-#define DGM_CMD_BIT 0x80
-#define DGM_FLOW_OFF 0x81
-#define DGM_FLOW_ON 0x80
-#define DGM_MTU 1500
-
-static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt);
-
-struct cflayer *cfdgml_create(u8 channel_id, struct dev_info *dev_info)
-{
- struct cfsrvl *dgm = kzalloc_obj(struct cfsrvl, GFP_ATOMIC);
- if (!dgm)
- return NULL;
- caif_assert(offsetof(struct cfsrvl, layer) == 0);
- cfsrvl_init(dgm, channel_id, dev_info, true);
- dgm->layer.receive = cfdgml_receive;
- dgm->layer.transmit = cfdgml_transmit;
- snprintf(dgm->layer.name, CAIF_LAYER_NAME_SZ, "dgm%d", channel_id);
- return &dgm->layer;
-}
-
-static int cfdgml_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 cmd = -1;
- u8 dgmhdr[3];
- int ret;
- caif_assert(layr->up != NULL);
- caif_assert(layr->receive != NULL);
- caif_assert(layr->ctrlcmd != NULL);
-
- if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
-
- if ((cmd & DGM_CMD_BIT) == 0) {
- if (cfpkt_extr_head(pkt, &dgmhdr, 3) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- ret = layr->up->receive(layr->up, pkt);
- return ret;
- }
-
- switch (cmd) {
- case DGM_FLOW_OFF: /* FLOW OFF */
- layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
- cfpkt_destroy(pkt);
- return 0;
- case DGM_FLOW_ON: /* FLOW ON */
- layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
- cfpkt_destroy(pkt);
- return 0;
- default:
- cfpkt_destroy(pkt);
- pr_info("Unknown datagram control %d (0x%x)\n", cmd, cmd);
- return -EPROTO;
- }
-}
-
-static int cfdgml_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 packet_type;
- u32 zero = 0;
- struct caif_payload_info *info;
- struct cfsrvl *service = container_obj(layr);
- int ret;
-
- if (!cfsrvl_ready(service, &ret)) {
- cfpkt_destroy(pkt);
- return ret;
- }
-
- /* STE Modem cannot handle more than 1500 bytes datagrams */
- if (cfpkt_getlen(pkt) > DGM_MTU) {
- cfpkt_destroy(pkt);
- return -EMSGSIZE;
- }
-
- cfpkt_add_head(pkt, &zero, 3);
- packet_type = 0x08; /* B9 set - UNCLASSIFIED */
- cfpkt_add_head(pkt, &packet_type, 1);
-
- /* Add info for MUX-layer to route the packet out. */
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- /* To optimize alignment, we add up the size of CAIF header
- * before payload.
- */
- info->hdr_len = 4;
- info->dev_info = &service->dev_info;
- return layr->dn->transmit(layr->dn, pkt);
-}
diff --git a/net/caif/cffrml.c b/net/caif/cffrml.c
deleted file mode 100644
index 0f4979d89fcb..000000000000
--- a/net/caif/cffrml.c
+++ /dev/null
@@ -1,204 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * CAIF Framing Layer.
- *
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/crc-ccitt.h>
-#include <linux/netdevice.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cffrml.h>
-
-#define container_obj(layr) container_of(layr, struct cffrml, layer)
-
-struct cffrml {
- struct cflayer layer;
- bool dofcs; /* !< FCS active */
- int __percpu *pcpu_refcnt;
-};
-
-static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt);
-static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
-
-static u32 cffrml_rcv_error;
-static u32 cffrml_rcv_checsum_error;
-struct cflayer *cffrml_create(u16 phyid, bool use_fcs)
-{
- struct cffrml *this = kzalloc_obj(struct cffrml, GFP_ATOMIC);
- if (!this)
- return NULL;
- this->pcpu_refcnt = alloc_percpu(int);
- if (this->pcpu_refcnt == NULL) {
- kfree(this);
- return NULL;
- }
-
- caif_assert(offsetof(struct cffrml, layer) == 0);
-
- this->layer.receive = cffrml_receive;
- this->layer.transmit = cffrml_transmit;
- this->layer.ctrlcmd = cffrml_ctrlcmd;
- snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "frm%d", phyid);
- this->dofcs = use_fcs;
- this->layer.id = phyid;
- return (struct cflayer *) this;
-}
-
-void cffrml_free(struct cflayer *layer)
-{
- struct cffrml *this = container_obj(layer);
- free_percpu(this->pcpu_refcnt);
- kfree(layer);
-}
-
-void cffrml_set_uplayer(struct cflayer *this, struct cflayer *up)
-{
- this->up = up;
-}
-
-void cffrml_set_dnlayer(struct cflayer *this, struct cflayer *dn)
-{
- this->dn = dn;
-}
-
-static u16 cffrml_checksum(u16 chks, void *buf, u16 len)
-{
- /* FIXME: FCS should be moved to glue in order to use OS-Specific
- * solutions
- */
- return crc_ccitt(chks, buf, len);
-}
-
-static int cffrml_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u16 tmp;
- u16 len;
- u16 hdrchks;
- int pktchks;
- struct cffrml *this;
- this = container_obj(layr);
-
- cfpkt_extr_head(pkt, &tmp, 2);
- len = le16_to_cpu(tmp);
-
- /* Subtract for FCS on length if FCS is not used. */
- if (!this->dofcs) {
- if (len < 2) {
- ++cffrml_rcv_error;
- pr_err("Invalid frame length (%d)\n", len);
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- len -= 2;
- }
-
- if (cfpkt_setlen(pkt, len) < 0) {
- ++cffrml_rcv_error;
- pr_err("Framing length error (%d)\n", len);
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- /*
- * Don't do extract if FCS is false, rather do setlen - then we don't
- * get a cache-miss.
- */
- if (this->dofcs) {
- cfpkt_extr_trail(pkt, &tmp, 2);
- hdrchks = le16_to_cpu(tmp);
- pktchks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
- if (pktchks != hdrchks) {
- cfpkt_add_trail(pkt, &tmp, 2);
- ++cffrml_rcv_error;
- ++cffrml_rcv_checsum_error;
- pr_info("Frame checksum error (0x%x != 0x%x)\n",
- hdrchks, pktchks);
- return -EILSEQ;
- }
- }
- if (cfpkt_erroneous(pkt)) {
- ++cffrml_rcv_error;
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
-
- if (layr->up == NULL) {
- pr_err("Layr up is missing!\n");
- cfpkt_destroy(pkt);
- return -EINVAL;
- }
-
- return layr->up->receive(layr->up, pkt);
-}
-
-static int cffrml_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- u16 chks;
- u16 len;
- __le16 data;
-
- struct cffrml *this = container_obj(layr);
- if (this->dofcs) {
- chks = cfpkt_iterate(pkt, cffrml_checksum, 0xffff);
- data = cpu_to_le16(chks);
- cfpkt_add_trail(pkt, &data, 2);
- } else {
- cfpkt_pad_trail(pkt, 2);
- }
- len = cfpkt_getlen(pkt);
- data = cpu_to_le16(len);
- cfpkt_add_head(pkt, &data, 2);
- cfpkt_info(pkt)->hdr_len += 2;
- if (cfpkt_erroneous(pkt)) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
-
- if (layr->dn == NULL) {
- cfpkt_destroy(pkt);
- return -ENODEV;
-
- }
- return layr->dn->transmit(layr->dn, pkt);
-}
-
-static void cffrml_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
-{
- if (layr->up && layr->up->ctrlcmd)
- layr->up->ctrlcmd(layr->up, ctrl, layr->id);
-}
-
-void cffrml_put(struct cflayer *layr)
-{
- struct cffrml *this = container_obj(layr);
- if (layr != NULL && this->pcpu_refcnt != NULL)
- this_cpu_dec(*this->pcpu_refcnt);
-}
-
-void cffrml_hold(struct cflayer *layr)
-{
- struct cffrml *this = container_obj(layr);
- if (layr != NULL && this->pcpu_refcnt != NULL)
- this_cpu_inc(*this->pcpu_refcnt);
-}
-
-int cffrml_refcnt_read(struct cflayer *layr)
-{
- int i, refcnt = 0;
- struct cffrml *this = container_obj(layr);
- for_each_possible_cpu(i)
- refcnt += *per_cpu_ptr(this->pcpu_refcnt, i);
- return refcnt;
-}
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
deleted file mode 100644
index 77a1f31639b7..000000000000
--- a/net/caif/cfmuxl.c
+++ /dev/null
@@ -1,267 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/rculist.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cfmuxl.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cffrml.h>
-
-#define container_obj(layr) container_of(layr, struct cfmuxl, layer)
-
-#define CAIF_CTRL_CHANNEL 0
-#define UP_CACHE_SIZE 8
-#define DN_CACHE_SIZE 8
-
-struct cfmuxl {
- struct cflayer layer;
- struct list_head srvl_list;
- struct list_head frml_list;
- struct cflayer *up_cache[UP_CACHE_SIZE];
- struct cflayer *dn_cache[DN_CACHE_SIZE];
- /*
- * Set when inserting or removing downwards layers.
- */
- spinlock_t transmit_lock;
-
- /*
- * Set when inserting or removing upwards layers.
- */
- spinlock_t receive_lock;
-
-};
-
-static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt);
-static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
-static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
-
-struct cflayer *cfmuxl_create(void)
-{
- struct cfmuxl *this = kzalloc_obj(struct cfmuxl, GFP_ATOMIC);
-
- if (!this)
- return NULL;
- this->layer.receive = cfmuxl_receive;
- this->layer.transmit = cfmuxl_transmit;
- this->layer.ctrlcmd = cfmuxl_ctrlcmd;
- INIT_LIST_HEAD(&this->srvl_list);
- INIT_LIST_HEAD(&this->frml_list);
- spin_lock_init(&this->transmit_lock);
- spin_lock_init(&this->receive_lock);
- snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "mux");
- return &this->layer;
-}
-
-int cfmuxl_set_dnlayer(struct cflayer *layr, struct cflayer *dn, u8 phyid)
-{
- struct cfmuxl *muxl = (struct cfmuxl *) layr;
-
- spin_lock_bh(&muxl->transmit_lock);
- list_add_rcu(&dn->node, &muxl->frml_list);
- spin_unlock_bh(&muxl->transmit_lock);
- return 0;
-}
-
-static struct cflayer *get_from_id(struct list_head *list, u16 id)
-{
- struct cflayer *lyr;
- list_for_each_entry_rcu(lyr, list, node) {
- if (lyr->id == id)
- return lyr;
- }
-
- return NULL;
-}
-
-int cfmuxl_set_uplayer(struct cflayer *layr, struct cflayer *up, u8 linkid)
-{
- struct cfmuxl *muxl = container_obj(layr);
- struct cflayer *old;
-
- spin_lock_bh(&muxl->receive_lock);
-
- /* Two entries with same id is wrong, so remove old layer from mux */
- old = get_from_id(&muxl->srvl_list, linkid);
- if (old != NULL)
- list_del_rcu(&old->node);
-
- list_add_rcu(&up->node, &muxl->srvl_list);
- spin_unlock_bh(&muxl->receive_lock);
-
- return 0;
-}
-
-struct cflayer *cfmuxl_remove_dnlayer(struct cflayer *layr, u8 phyid)
-{
- struct cfmuxl *muxl = container_obj(layr);
- struct cflayer *dn;
- int idx = phyid % DN_CACHE_SIZE;
-
- spin_lock_bh(&muxl->transmit_lock);
- RCU_INIT_POINTER(muxl->dn_cache[idx], NULL);
- dn = get_from_id(&muxl->frml_list, phyid);
- if (dn == NULL)
- goto out;
-
- list_del_rcu(&dn->node);
- caif_assert(dn != NULL);
-out:
- spin_unlock_bh(&muxl->transmit_lock);
- return dn;
-}
-
-static struct cflayer *get_up(struct cfmuxl *muxl, u16 id)
-{
- struct cflayer *up;
- int idx = id % UP_CACHE_SIZE;
- up = rcu_dereference(muxl->up_cache[idx]);
- if (up == NULL || up->id != id) {
- spin_lock_bh(&muxl->receive_lock);
- up = get_from_id(&muxl->srvl_list, id);
- rcu_assign_pointer(muxl->up_cache[idx], up);
- spin_unlock_bh(&muxl->receive_lock);
- }
- return up;
-}
-
-static struct cflayer *get_dn(struct cfmuxl *muxl, struct dev_info *dev_info)
-{
- struct cflayer *dn;
- int idx = dev_info->id % DN_CACHE_SIZE;
- dn = rcu_dereference(muxl->dn_cache[idx]);
- if (dn == NULL || dn->id != dev_info->id) {
- spin_lock_bh(&muxl->transmit_lock);
- dn = get_from_id(&muxl->frml_list, dev_info->id);
- rcu_assign_pointer(muxl->dn_cache[idx], dn);
- spin_unlock_bh(&muxl->transmit_lock);
- }
- return dn;
-}
-
-struct cflayer *cfmuxl_remove_uplayer(struct cflayer *layr, u8 id)
-{
- struct cflayer *up;
- struct cfmuxl *muxl = container_obj(layr);
- int idx = id % UP_CACHE_SIZE;
-
- if (id == 0) {
- pr_warn("Trying to remove control layer\n");
- return NULL;
- }
-
- spin_lock_bh(&muxl->receive_lock);
- up = get_from_id(&muxl->srvl_list, id);
- if (up == NULL)
- goto out;
-
- RCU_INIT_POINTER(muxl->up_cache[idx], NULL);
- list_del_rcu(&up->node);
-out:
- spin_unlock_bh(&muxl->receive_lock);
- return up;
-}
-
-static int cfmuxl_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- int ret;
- struct cfmuxl *muxl = container_obj(layr);
- u8 id;
- struct cflayer *up;
- if (cfpkt_extr_head(pkt, &id, 1) < 0) {
- pr_err("erroneous Caif Packet\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- rcu_read_lock();
- up = get_up(muxl, id);
-
- if (up == NULL) {
- pr_debug("Received data on unknown link ID = %d (0x%x)"
- " up == NULL", id, id);
- cfpkt_destroy(pkt);
- /*
- * Don't return ERROR, since modem misbehaves and sends out
- * flow on before linksetup response.
- */
-
- rcu_read_unlock();
- return /* CFGLU_EPROT; */ 0;
- }
-
- /* We can't hold rcu_lock during receive, so take a ref count instead */
- cfsrvl_get(up);
- rcu_read_unlock();
-
- ret = up->receive(up, pkt);
-
- cfsrvl_put(up);
- return ret;
-}
-
-static int cfmuxl_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct cfmuxl *muxl = container_obj(layr);
- int err;
- u8 linkid;
- struct cflayer *dn;
- struct caif_payload_info *info = cfpkt_info(pkt);
- BUG_ON(!info);
-
- rcu_read_lock();
-
- dn = get_dn(muxl, info->dev_info);
- if (dn == NULL) {
- pr_debug("Send data on unknown phy ID = %d (0x%x)\n",
- info->dev_info->id, info->dev_info->id);
- rcu_read_unlock();
- cfpkt_destroy(pkt);
- return -ENOTCONN;
- }
-
- info->hdr_len += 1;
- linkid = info->channel_id;
- cfpkt_add_head(pkt, &linkid, 1);
-
- /* We can't hold rcu_lock during receive, so take a ref count instead */
- cffrml_hold(dn);
-
- rcu_read_unlock();
-
- err = dn->transmit(dn, pkt);
-
- cffrml_put(dn);
- return err;
-}
-
-static void cfmuxl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
-{
- struct cfmuxl *muxl = container_obj(layr);
- struct cflayer *layer;
-
- rcu_read_lock();
- list_for_each_entry_rcu(layer, &muxl->srvl_list, node) {
-
- if (cfsrvl_phyid_match(layer, phyid) && layer->ctrlcmd) {
-
- if ((ctrl == _CAIF_CTRLCMD_PHYIF_DOWN_IND ||
- ctrl == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND) &&
- layer->id != 0)
- cfmuxl_remove_uplayer(layr, layer->id);
-
- /* NOTE: ctrlcmd is not allowed to block */
- layer->ctrlcmd(layer, ctrl, phyid);
- }
- }
- rcu_read_unlock();
-}
diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c
deleted file mode 100644
index 96236d21b18e..000000000000
--- a/net/caif/cfpkt_skbuff.c
+++ /dev/null
@@ -1,373 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/string.h>
-#include <linux/skbuff.h>
-#include <linux/export.h>
-#include <net/caif/cfpkt.h>
-
-#define PKT_PREFIX 48
-#define PKT_POSTFIX 2
-#define PKT_LEN_WHEN_EXTENDING 128
-#define PKT_ERROR(pkt, errmsg) \
-do { \
- cfpkt_priv(pkt)->erronous = true; \
- skb_reset_tail_pointer(&pkt->skb); \
- pr_warn(errmsg); \
-} while (0)
-
-/*
- * net/caif/ is generic and does not
- * understand SKB, so we do this typecast
- */
-struct cfpkt {
- struct sk_buff skb;
-};
-
-/* Private data inside SKB */
-struct cfpkt_priv_data {
- struct dev_info dev_info;
- bool erronous;
-};
-
-static inline struct cfpkt_priv_data *cfpkt_priv(struct cfpkt *pkt)
-{
- return (struct cfpkt_priv_data *) pkt->skb.cb;
-}
-
-static inline bool is_erronous(struct cfpkt *pkt)
-{
- return cfpkt_priv(pkt)->erronous;
-}
-
-static inline struct sk_buff *pkt_to_skb(struct cfpkt *pkt)
-{
- return &pkt->skb;
-}
-
-static inline struct cfpkt *skb_to_pkt(struct sk_buff *skb)
-{
- return (struct cfpkt *) skb;
-}
-
-struct cfpkt *cfpkt_fromnative(enum caif_direction dir, void *nativepkt)
-{
- struct cfpkt *pkt = skb_to_pkt(nativepkt);
- cfpkt_priv(pkt)->erronous = false;
- return pkt;
-}
-EXPORT_SYMBOL(cfpkt_fromnative);
-
-void *cfpkt_tonative(struct cfpkt *pkt)
-{
- return (void *) pkt;
-}
-EXPORT_SYMBOL(cfpkt_tonative);
-
-static struct cfpkt *cfpkt_create_pfx(u16 len, u16 pfx)
-{
- struct sk_buff *skb;
-
- skb = alloc_skb(len + pfx, GFP_ATOMIC);
- if (unlikely(skb == NULL))
- return NULL;
-
- skb_reserve(skb, pfx);
- return skb_to_pkt(skb);
-}
-
-inline struct cfpkt *cfpkt_create(u16 len)
-{
- return cfpkt_create_pfx(len + PKT_POSTFIX, PKT_PREFIX);
-}
-
-void cfpkt_destroy(struct cfpkt *pkt)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- kfree_skb(skb);
-}
-
-inline bool cfpkt_more(struct cfpkt *pkt)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- return skb->len > 0;
-}
-
-int cfpkt_peek_head(struct cfpkt *pkt, void *data, u16 len)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- if (skb_headlen(skb) >= len) {
- memcpy(data, skb->data, len);
- return 0;
- }
- return !cfpkt_extr_head(pkt, data, len) &&
- !cfpkt_add_head(pkt, data, len);
-}
-
-int cfpkt_extr_head(struct cfpkt *pkt, void *data, u16 len)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- u8 *from;
- if (unlikely(is_erronous(pkt)))
- return -EPROTO;
-
- if (unlikely(len > skb->len)) {
- PKT_ERROR(pkt, "read beyond end of packet\n");
- return -EPROTO;
- }
-
- if (unlikely(len > skb_headlen(skb))) {
- if (unlikely(skb_linearize(skb) != 0)) {
- PKT_ERROR(pkt, "linearize failed\n");
- return -EPROTO;
- }
- }
- from = skb_pull(skb, len);
- from -= len;
- if (data)
- memcpy(data, from, len);
- return 0;
-}
-EXPORT_SYMBOL(cfpkt_extr_head);
-
-int cfpkt_extr_trail(struct cfpkt *pkt, void *dta, u16 len)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- u8 *data = dta;
- u8 *from;
- if (unlikely(is_erronous(pkt)))
- return -EPROTO;
-
- if (unlikely(skb_linearize(skb) != 0)) {
- PKT_ERROR(pkt, "linearize failed\n");
- return -EPROTO;
- }
- if (unlikely(skb->data + len > skb_tail_pointer(skb))) {
- PKT_ERROR(pkt, "read beyond end of packet\n");
- return -EPROTO;
- }
- from = skb_tail_pointer(skb) - len;
- skb_trim(skb, skb->len - len);
- memcpy(data, from, len);
- return 0;
-}
-
-int cfpkt_pad_trail(struct cfpkt *pkt, u16 len)
-{
- return cfpkt_add_body(pkt, NULL, len);
-}
-
-int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- struct sk_buff *lastskb;
- u8 *to;
- u16 addlen = 0;
-
-
- if (unlikely(is_erronous(pkt)))
- return -EPROTO;
-
- lastskb = skb;
-
- /* Check whether we need to add space at the tail */
- if (unlikely(skb_tailroom(skb) < len)) {
- if (likely(len < PKT_LEN_WHEN_EXTENDING))
- addlen = PKT_LEN_WHEN_EXTENDING;
- else
- addlen = len;
- }
-
- /* Check whether we need to change the SKB before writing to the tail */
- if (unlikely((addlen > 0) || skb_cloned(skb) || skb_shared(skb))) {
-
- /* Make sure data is writable */
- if (unlikely(skb_cow_data(skb, addlen, &lastskb) < 0)) {
- PKT_ERROR(pkt, "cow failed\n");
- return -EPROTO;
- }
- }
-
- /* All set to put the last SKB and optionally write data there. */
- to = pskb_put(skb, lastskb, len);
- if (likely(data))
- memcpy(to, data, len);
- return 0;
-}
-
-inline int cfpkt_addbdy(struct cfpkt *pkt, u8 data)
-{
- return cfpkt_add_body(pkt, &data, 1);
-}
-
-int cfpkt_add_head(struct cfpkt *pkt, const void *data2, u16 len)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- struct sk_buff *lastskb;
- u8 *to;
- const u8 *data = data2;
- int ret;
- if (unlikely(is_erronous(pkt)))
- return -EPROTO;
- if (unlikely(skb_headroom(skb) < len)) {
- PKT_ERROR(pkt, "no headroom\n");
- return -EPROTO;
- }
-
- /* Make sure data is writable */
- ret = skb_cow_data(skb, 0, &lastskb);
- if (unlikely(ret < 0)) {
- PKT_ERROR(pkt, "cow failed\n");
- return ret;
- }
-
- to = skb_push(skb, len);
- memcpy(to, data, len);
- return 0;
-}
-EXPORT_SYMBOL(cfpkt_add_head);
-
-inline int cfpkt_add_trail(struct cfpkt *pkt, const void *data, u16 len)
-{
- return cfpkt_add_body(pkt, data, len);
-}
-
-inline u16 cfpkt_getlen(struct cfpkt *pkt)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
- return skb->len;
-}
-
-int cfpkt_iterate(struct cfpkt *pkt,
- u16 (*iter_func)(u16, void *, u16),
- u16 data)
-{
- /*
- * Don't care about the performance hit of linearizing,
- * Checksum should not be used on high-speed interfaces anyway.
- */
- if (unlikely(is_erronous(pkt)))
- return -EPROTO;
- if (unlikely(skb_linearize(&pkt->skb) != 0)) {
- PKT_ERROR(pkt, "linearize failed\n");
- return -EPROTO;
- }
- return iter_func(data, pkt->skb.data, cfpkt_getlen(pkt));
-}
-
-int cfpkt_setlen(struct cfpkt *pkt, u16 len)
-{
- struct sk_buff *skb = pkt_to_skb(pkt);
-
-
- if (unlikely(is_erronous(pkt)))
- return -EPROTO;
-
- if (likely(len <= skb->len)) {
- if (unlikely(skb->data_len))
- ___pskb_trim(skb, len);
- else
- skb_trim(skb, len);
-
- return cfpkt_getlen(pkt);
- }
-
- /* Need to expand SKB */
- if (unlikely(!cfpkt_pad_trail(pkt, len - skb->len)))
- PKT_ERROR(pkt, "skb_pad_trail failed\n");
-
- return cfpkt_getlen(pkt);
-}
-
-struct cfpkt *cfpkt_append(struct cfpkt *dstpkt,
- struct cfpkt *addpkt,
- u16 expectlen)
-{
- struct sk_buff *dst = pkt_to_skb(dstpkt);
- struct sk_buff *add = pkt_to_skb(addpkt);
- u16 addlen = skb_headlen(add);
- u16 neededtailspace;
- struct sk_buff *tmp;
- u16 dstlen;
- u16 createlen;
- if (unlikely(is_erronous(dstpkt) || is_erronous(addpkt))) {
- return dstpkt;
- }
-
- neededtailspace = max(expectlen, addlen);
-
- if (dst->tail + neededtailspace > dst->end) {
- /* Create a dumplicate of 'dst' with more tail space */
- struct cfpkt *tmppkt;
- dstlen = skb_headlen(dst);
- createlen = dstlen + neededtailspace;
- tmppkt = cfpkt_create(createlen + PKT_PREFIX + PKT_POSTFIX);
- if (tmppkt == NULL)
- return NULL;
- tmp = pkt_to_skb(tmppkt);
- skb_put_data(tmp, dst->data, dstlen);
- cfpkt_destroy(dstpkt);
- dst = tmp;
- }
- skb_put_data(dst, add->data, skb_headlen(add));
- cfpkt_destroy(addpkt);
- return skb_to_pkt(dst);
-}
-
-struct cfpkt *cfpkt_split(struct cfpkt *pkt, u16 pos)
-{
- struct sk_buff *skb2;
- struct sk_buff *skb = pkt_to_skb(pkt);
- struct cfpkt *tmppkt;
- u8 *split = skb->data + pos;
- u16 len2nd = skb_tail_pointer(skb) - split;
-
- if (unlikely(is_erronous(pkt)))
- return NULL;
-
- if (skb->data + pos > skb_tail_pointer(skb)) {
- PKT_ERROR(pkt, "trying to split beyond end of packet\n");
- return NULL;
- }
-
- /* Create a new packet for the second part of the data */
- tmppkt = cfpkt_create_pfx(len2nd + PKT_PREFIX + PKT_POSTFIX,
- PKT_PREFIX);
- if (tmppkt == NULL)
- return NULL;
- skb2 = pkt_to_skb(tmppkt);
-
-
- if (skb2 == NULL)
- return NULL;
-
- skb_put_data(skb2, split, len2nd);
-
- /* Reduce the length of the original packet */
- skb_trim(skb, pos);
-
- skb2->priority = skb->priority;
- return skb_to_pkt(skb2);
-}
-
-bool cfpkt_erroneous(struct cfpkt *pkt)
-{
- return cfpkt_priv(pkt)->erronous;
-}
-
-struct caif_payload_info *cfpkt_info(struct cfpkt *pkt)
-{
- return (struct caif_payload_info *)&pkt_to_skb(pkt)->cb;
-}
-EXPORT_SYMBOL(cfpkt_info);
-
-void cfpkt_set_prio(struct cfpkt *pkt, int prio)
-{
- pkt_to_skb(pkt)->priority = prio;
-}
-EXPORT_SYMBOL(cfpkt_set_prio);
diff --git a/net/caif/cfrfml.c b/net/caif/cfrfml.c
deleted file mode 100644
index 93732ebbd1e2..000000000000
--- a/net/caif/cfrfml.c
+++ /dev/null
@@ -1,299 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <linux/unaligned.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-
-#define container_obj(layr) container_of(layr, struct cfrfml, serv.layer)
-#define RFM_SEGMENTATION_BIT 0x01
-#define RFM_HEAD_SIZE 7
-
-static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt);
-
-struct cfrfml {
- struct cfsrvl serv;
- struct cfpkt *incomplete_frm;
- int fragment_size;
- u8 seghead[6];
- u16 pdu_size;
- /* Protects serialized processing of packets */
- spinlock_t sync;
-};
-
-static void cfrfml_release(struct cflayer *layer)
-{
- struct cfsrvl *srvl = container_of(layer, struct cfsrvl, layer);
- struct cfrfml *rfml = container_obj(&srvl->layer);
-
- if (rfml->incomplete_frm)
- cfpkt_destroy(rfml->incomplete_frm);
-
- kfree(srvl);
-}
-
-struct cflayer *cfrfml_create(u8 channel_id, struct dev_info *dev_info,
- int mtu_size)
-{
- int tmp;
- struct cfrfml *this = kzalloc_obj(struct cfrfml, GFP_ATOMIC);
-
- if (!this)
- return NULL;
-
- cfsrvl_init(&this->serv, channel_id, dev_info, false);
- this->serv.release = cfrfml_release;
- this->serv.layer.receive = cfrfml_receive;
- this->serv.layer.transmit = cfrfml_transmit;
-
- /* Round down to closest multiple of 16 */
- tmp = (mtu_size - RFM_HEAD_SIZE - 6) / 16;
- tmp *= 16;
-
- this->fragment_size = tmp;
- spin_lock_init(&this->sync);
- snprintf(this->serv.layer.name, CAIF_LAYER_NAME_SZ,
- "rfm%d", channel_id);
-
- return &this->serv.layer;
-}
-
-static struct cfpkt *rfm_append(struct cfrfml *rfml, char *seghead,
- struct cfpkt *pkt, int *err)
-{
- struct cfpkt *tmppkt;
- *err = -EPROTO;
- /* n-th but not last segment */
-
- if (cfpkt_extr_head(pkt, seghead, 6) < 0)
- return NULL;
-
- /* Verify correct header */
- if (memcmp(seghead, rfml->seghead, 6) != 0)
- return NULL;
-
- tmppkt = cfpkt_append(rfml->incomplete_frm, pkt,
- rfml->pdu_size + RFM_HEAD_SIZE);
-
- /* If cfpkt_append failes input pkts are not freed */
- *err = -ENOMEM;
- if (tmppkt == NULL)
- return NULL;
-
- *err = 0;
- return tmppkt;
-}
-
-static int cfrfml_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 tmp;
- bool segmented;
- int err;
- u8 seghead[6];
- struct cfrfml *rfml;
- struct cfpkt *tmppkt = NULL;
-
- caif_assert(layr->up != NULL);
- caif_assert(layr->receive != NULL);
- rfml = container_obj(layr);
- spin_lock(&rfml->sync);
-
- err = -EPROTO;
- if (cfpkt_extr_head(pkt, &tmp, 1) < 0)
- goto out;
- segmented = tmp & RFM_SEGMENTATION_BIT;
-
- if (segmented) {
- if (rfml->incomplete_frm == NULL) {
- /* Initial Segment */
- if (cfpkt_peek_head(pkt, rfml->seghead, 6) != 0)
- goto out;
-
- rfml->pdu_size = get_unaligned_le16(rfml->seghead+4);
-
- if (cfpkt_erroneous(pkt))
- goto out;
- rfml->incomplete_frm = pkt;
- pkt = NULL;
- } else {
-
- tmppkt = rfm_append(rfml, seghead, pkt, &err);
- if (tmppkt == NULL)
- goto out;
-
- if (cfpkt_erroneous(tmppkt))
- goto out;
-
- rfml->incomplete_frm = tmppkt;
-
-
- if (cfpkt_erroneous(tmppkt))
- goto out;
- }
- err = 0;
- goto out;
- }
-
- if (rfml->incomplete_frm) {
-
- /* Last Segment */
- tmppkt = rfm_append(rfml, seghead, pkt, &err);
- if (tmppkt == NULL)
- goto out;
-
- if (cfpkt_erroneous(tmppkt))
- goto out;
-
- rfml->incomplete_frm = NULL;
- pkt = tmppkt;
- tmppkt = NULL;
-
- /* Verify that length is correct */
- err = -EPROTO;
- if (rfml->pdu_size != cfpkt_getlen(pkt) - RFM_HEAD_SIZE + 1)
- goto out;
- }
-
- err = rfml->serv.layer.up->receive(rfml->serv.layer.up, pkt);
-
-out:
-
- if (err != 0) {
- if (tmppkt)
- cfpkt_destroy(tmppkt);
- if (pkt)
- cfpkt_destroy(pkt);
- if (rfml->incomplete_frm)
- cfpkt_destroy(rfml->incomplete_frm);
- rfml->incomplete_frm = NULL;
-
- pr_info("Connection error %d triggered on RFM link\n", err);
-
- /* Trigger connection error upon failure.*/
- layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
- rfml->serv.dev_info.id);
- }
- spin_unlock(&rfml->sync);
-
- if (unlikely(err == -EAGAIN))
- /* It is not possible to recover after drop of a fragment */
- err = -EIO;
-
- return err;
-}
-
-
-static int cfrfml_transmit_segment(struct cfrfml *rfml, struct cfpkt *pkt)
-{
- caif_assert(cfpkt_getlen(pkt) < rfml->fragment_size + RFM_HEAD_SIZE);
-
- /* Add info for MUX-layer to route the packet out. */
- cfpkt_info(pkt)->channel_id = rfml->serv.layer.id;
-
- /*
- * To optimize alignment, we add up the size of CAIF header before
- * payload.
- */
- cfpkt_info(pkt)->hdr_len = RFM_HEAD_SIZE;
- cfpkt_info(pkt)->dev_info = &rfml->serv.dev_info;
-
- return rfml->serv.layer.dn->transmit(rfml->serv.layer.dn, pkt);
-}
-
-static int cfrfml_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- int err;
- u8 seg;
- u8 head[6];
- struct cfpkt *rearpkt = NULL;
- struct cfpkt *frontpkt = pkt;
- struct cfrfml *rfml = container_obj(layr);
-
- caif_assert(layr->dn != NULL);
- caif_assert(layr->dn->transmit != NULL);
-
- if (!cfsrvl_ready(&rfml->serv, &err))
- goto out;
-
- err = -EPROTO;
- if (cfpkt_getlen(pkt) <= RFM_HEAD_SIZE-1)
- goto out;
-
- err = 0;
- if (cfpkt_getlen(pkt) > rfml->fragment_size + RFM_HEAD_SIZE)
- err = cfpkt_peek_head(pkt, head, 6);
-
- if (err != 0)
- goto out;
-
- while (cfpkt_getlen(frontpkt) > rfml->fragment_size + RFM_HEAD_SIZE) {
-
- seg = 1;
- err = -EPROTO;
-
- if (cfpkt_add_head(frontpkt, &seg, 1) < 0)
- goto out;
- /*
- * On OOM error cfpkt_split returns NULL.
- *
- * NOTE: Segmented pdu is not correctly aligned.
- * This has negative performance impact.
- */
-
- rearpkt = cfpkt_split(frontpkt, rfml->fragment_size);
- if (rearpkt == NULL)
- goto out;
-
- err = cfrfml_transmit_segment(rfml, frontpkt);
-
- if (err != 0) {
- frontpkt = NULL;
- goto out;
- }
-
- frontpkt = rearpkt;
- rearpkt = NULL;
-
- err = -EPROTO;
- if (cfpkt_add_head(frontpkt, head, 6) < 0)
- goto out;
-
- }
-
- seg = 0;
- err = -EPROTO;
-
- if (cfpkt_add_head(frontpkt, &seg, 1) < 0)
- goto out;
-
- err = cfrfml_transmit_segment(rfml, frontpkt);
-
- frontpkt = NULL;
-out:
-
- if (err != 0) {
- pr_info("Connection error %d triggered on RFM link\n", err);
- /* Trigger connection error upon failure.*/
-
- layr->up->ctrlcmd(layr->up, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND,
- rfml->serv.dev_info.id);
-
- if (rearpkt)
- cfpkt_destroy(rearpkt);
-
- if (frontpkt)
- cfpkt_destroy(frontpkt);
- }
-
- return err;
-}
diff --git a/net/caif/cfserl.c b/net/caif/cfserl.c
deleted file mode 100644
index faf78fb754e2..000000000000
--- a/net/caif/cfserl.c
+++ /dev/null
@@ -1,192 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/spinlock.h>
-#include <linux/slab.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/cfserl.h>
-
-#define container_obj(layr) ((struct cfserl *) layr)
-
-#define CFSERL_STX 0x02
-#define SERIAL_MINIUM_PACKET_SIZE 4
-#define SERIAL_MAX_FRAMESIZE 4096
-struct cfserl {
- struct cflayer layer;
- struct cfpkt *incomplete_frm;
- /* Protects parallel processing of incoming packets */
- spinlock_t sync;
- bool usestx;
-};
-
-static int cfserl_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfserl_transmit(struct cflayer *layr, struct cfpkt *pkt);
-static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid);
-
-void cfserl_release(struct cflayer *layer)
-{
- kfree(layer);
-}
-
-struct cflayer *cfserl_create(int instance, bool use_stx)
-{
- struct cfserl *this = kzalloc_obj(struct cfserl, GFP_ATOMIC);
- if (!this)
- return NULL;
- caif_assert(offsetof(struct cfserl, layer) == 0);
- this->layer.receive = cfserl_receive;
- this->layer.transmit = cfserl_transmit;
- this->layer.ctrlcmd = cfserl_ctrlcmd;
- this->usestx = use_stx;
- spin_lock_init(&this->sync);
- snprintf(this->layer.name, CAIF_LAYER_NAME_SZ, "ser1");
- return &this->layer;
-}
-
-static int cfserl_receive(struct cflayer *l, struct cfpkt *newpkt)
-{
- struct cfserl *layr = container_obj(l);
- u16 pkt_len;
- struct cfpkt *pkt = NULL;
- struct cfpkt *tail_pkt = NULL;
- u8 tmp8;
- u16 tmp;
- u8 stx = CFSERL_STX;
- int ret;
- u16 expectlen = 0;
-
- caif_assert(newpkt != NULL);
- spin_lock(&layr->sync);
-
- if (layr->incomplete_frm != NULL) {
- layr->incomplete_frm =
- cfpkt_append(layr->incomplete_frm, newpkt, expectlen);
- pkt = layr->incomplete_frm;
- if (pkt == NULL) {
- spin_unlock(&layr->sync);
- return -ENOMEM;
- }
- } else {
- pkt = newpkt;
- }
- layr->incomplete_frm = NULL;
-
- do {
- /* Search for STX at start of pkt if STX is used */
- if (layr->usestx) {
- cfpkt_extr_head(pkt, &tmp8, 1);
- if (tmp8 != CFSERL_STX) {
- while (cfpkt_more(pkt)
- && tmp8 != CFSERL_STX) {
- cfpkt_extr_head(pkt, &tmp8, 1);
- }
- if (!cfpkt_more(pkt)) {
- cfpkt_destroy(pkt);
- layr->incomplete_frm = NULL;
- spin_unlock(&layr->sync);
- return -EPROTO;
- }
- }
- }
-
- pkt_len = cfpkt_getlen(pkt);
-
- /*
- * pkt_len is the accumulated length of the packet data
- * we have received so far.
- * Exit if frame doesn't hold length.
- */
-
- if (pkt_len < 2) {
- if (layr->usestx)
- cfpkt_add_head(pkt, &stx, 1);
- layr->incomplete_frm = pkt;
- spin_unlock(&layr->sync);
- return 0;
- }
-
- /*
- * Find length of frame.
- * expectlen is the length we need for a full frame.
- */
- cfpkt_peek_head(pkt, &tmp, 2);
- expectlen = le16_to_cpu(tmp) + 2;
- /*
- * Frame error handling
- */
- if (expectlen < SERIAL_MINIUM_PACKET_SIZE
- || expectlen > SERIAL_MAX_FRAMESIZE) {
- if (!layr->usestx) {
- if (pkt != NULL)
- cfpkt_destroy(pkt);
- layr->incomplete_frm = NULL;
- spin_unlock(&layr->sync);
- return -EPROTO;
- }
- continue;
- }
-
- if (pkt_len < expectlen) {
- /* Too little received data */
- if (layr->usestx)
- cfpkt_add_head(pkt, &stx, 1);
- layr->incomplete_frm = pkt;
- spin_unlock(&layr->sync);
- return 0;
- }
-
- /*
- * Enough data for at least one frame.
- * Split the frame, if too long
- */
- if (pkt_len > expectlen)
- tail_pkt = cfpkt_split(pkt, expectlen);
- else
- tail_pkt = NULL;
-
- /* Send the first part of packet upwards.*/
- spin_unlock(&layr->sync);
- ret = layr->layer.up->receive(layr->layer.up, pkt);
- spin_lock(&layr->sync);
- if (ret == -EILSEQ) {
- if (layr->usestx) {
- if (tail_pkt != NULL)
- pkt = cfpkt_append(pkt, tail_pkt, 0);
- /* Start search for next STX if frame failed */
- continue;
- } else {
- cfpkt_destroy(pkt);
- pkt = NULL;
- }
- }
-
- pkt = tail_pkt;
-
- } while (pkt != NULL);
-
- spin_unlock(&layr->sync);
- return 0;
-}
-
-static int cfserl_transmit(struct cflayer *layer, struct cfpkt *newpkt)
-{
- struct cfserl *layr = container_obj(layer);
- u8 tmp8 = CFSERL_STX;
- if (layr->usestx)
- cfpkt_add_head(newpkt, &tmp8, 1);
- return layer->dn->transmit(layer->dn, newpkt);
-}
-
-static void cfserl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
-{
- layr->up->ctrlcmd(layr->up, ctrl, phyid);
-}
diff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c
deleted file mode 100644
index d687fd0b4ed3..000000000000
--- a/net/caif/cfsrvl.c
+++ /dev/null
@@ -1,224 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/pkt_sched.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/caif_dev.h>
-
-#define SRVL_CTRL_PKT_SIZE 1
-#define SRVL_FLOW_OFF 0x81
-#define SRVL_FLOW_ON 0x80
-#define SRVL_SET_PIN 0x82
-
-#define container_obj(layr) container_of(layr, struct cfsrvl, layer)
-
-static void cfservl_ctrlcmd(struct cflayer *layr, enum caif_ctrlcmd ctrl,
- int phyid)
-{
- struct cfsrvl *service = container_obj(layr);
-
- if (layr->up == NULL || layr->up->ctrlcmd == NULL)
- return;
-
- switch (ctrl) {
- case CAIF_CTRLCMD_INIT_RSP:
- service->open = true;
- layr->up->ctrlcmd(layr->up, ctrl, phyid);
- break;
- case CAIF_CTRLCMD_DEINIT_RSP:
- case CAIF_CTRLCMD_INIT_FAIL_RSP:
- service->open = false;
- layr->up->ctrlcmd(layr->up, ctrl, phyid);
- break;
- case _CAIF_CTRLCMD_PHYIF_FLOW_OFF_IND:
- if (phyid != service->dev_info.id)
- break;
- if (service->modem_flow_on)
- layr->up->ctrlcmd(layr->up,
- CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
- service->phy_flow_on = false;
- break;
- case _CAIF_CTRLCMD_PHYIF_FLOW_ON_IND:
- if (phyid != service->dev_info.id)
- return;
- if (service->modem_flow_on) {
- layr->up->ctrlcmd(layr->up,
- CAIF_CTRLCMD_FLOW_ON_IND,
- phyid);
- }
- service->phy_flow_on = true;
- break;
- case CAIF_CTRLCMD_FLOW_OFF_IND:
- if (service->phy_flow_on) {
- layr->up->ctrlcmd(layr->up,
- CAIF_CTRLCMD_FLOW_OFF_IND, phyid);
- }
- service->modem_flow_on = false;
- break;
- case CAIF_CTRLCMD_FLOW_ON_IND:
- if (service->phy_flow_on) {
- layr->up->ctrlcmd(layr->up,
- CAIF_CTRLCMD_FLOW_ON_IND, phyid);
- }
- service->modem_flow_on = true;
- break;
- case _CAIF_CTRLCMD_PHYIF_DOWN_IND:
- /* In case interface is down, let's fake a remove shutdown */
- layr->up->ctrlcmd(layr->up,
- CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, phyid);
- break;
- case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
- layr->up->ctrlcmd(layr->up, ctrl, phyid);
- break;
- default:
- pr_warn("Unexpected ctrl in cfsrvl (%d)\n", ctrl);
- /* We have both modem and phy flow on, send flow on */
- layr->up->ctrlcmd(layr->up, ctrl, phyid);
- service->phy_flow_on = true;
- break;
- }
-}
-
-static int cfservl_modemcmd(struct cflayer *layr, enum caif_modemcmd ctrl)
-{
- struct cfsrvl *service = container_obj(layr);
-
- caif_assert(layr != NULL);
- caif_assert(layr->dn != NULL);
- caif_assert(layr->dn->transmit != NULL);
-
- if (!service->supports_flowctrl)
- return 0;
-
- switch (ctrl) {
- case CAIF_MODEMCMD_FLOW_ON_REQ:
- {
- struct cfpkt *pkt;
- struct caif_payload_info *info;
- u8 flow_on = SRVL_FLOW_ON;
- pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
- if (!pkt)
- return -ENOMEM;
-
- if (cfpkt_add_head(pkt, &flow_on, 1) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- info->hdr_len = 1;
- info->dev_info = &service->dev_info;
- cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
- return layr->dn->transmit(layr->dn, pkt);
- }
- case CAIF_MODEMCMD_FLOW_OFF_REQ:
- {
- struct cfpkt *pkt;
- struct caif_payload_info *info;
- u8 flow_off = SRVL_FLOW_OFF;
- pkt = cfpkt_create(SRVL_CTRL_PKT_SIZE);
- if (!pkt)
- return -ENOMEM;
-
- if (cfpkt_add_head(pkt, &flow_off, 1) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- info->hdr_len = 1;
- info->dev_info = &service->dev_info;
- cfpkt_set_prio(pkt, TC_PRIO_CONTROL);
- return layr->dn->transmit(layr->dn, pkt);
- }
- default:
- break;
- }
- return -EINVAL;
-}
-
-static void cfsrvl_release(struct cflayer *layer)
-{
- struct cfsrvl *service = container_of(layer, struct cfsrvl, layer);
- kfree(service);
-}
-
-void cfsrvl_init(struct cfsrvl *service,
- u8 channel_id,
- struct dev_info *dev_info,
- bool supports_flowctrl)
-{
- caif_assert(offsetof(struct cfsrvl, layer) == 0);
- service->open = false;
- service->modem_flow_on = true;
- service->phy_flow_on = true;
- service->layer.id = channel_id;
- service->layer.ctrlcmd = cfservl_ctrlcmd;
- service->layer.modemcmd = cfservl_modemcmd;
- service->dev_info = *dev_info;
- service->supports_flowctrl = supports_flowctrl;
- service->release = cfsrvl_release;
-}
-
-bool cfsrvl_ready(struct cfsrvl *service, int *err)
-{
- if (!service->open) {
- *err = -ENOTCONN;
- return false;
- }
- return true;
-}
-
-bool cfsrvl_phyid_match(struct cflayer *layer, int phyid)
-{
- struct cfsrvl *servl = container_obj(layer);
- return servl->dev_info.id == phyid;
-}
-
-void caif_free_client(struct cflayer *adap_layer)
-{
- struct cflayer *serv_layer;
- struct cfsrvl *servl;
-
- if (!adap_layer)
- return;
-
- serv_layer = adap_layer->dn;
- if (!serv_layer)
- return;
-
- layer_set_dn(adap_layer, NULL);
- layer_set_up(serv_layer, NULL);
-
- servl = container_obj(serv_layer);
- servl->release(&servl->layer);
-}
-EXPORT_SYMBOL(caif_free_client);
-
-void caif_client_register_refcnt(struct cflayer *adapt_layer,
- void (*hold)(struct cflayer *lyr),
- void (*put)(struct cflayer *lyr))
-{
- struct cfsrvl *service;
-
- if (WARN_ON(adapt_layer == NULL || adapt_layer->dn == NULL))
- return;
- service = container_of(adapt_layer->dn, struct cfsrvl, layer);
- service->hold = hold;
- service->put = put;
-}
-EXPORT_SYMBOL(caif_client_register_refcnt);
diff --git a/net/caif/cfutill.c b/net/caif/cfutill.c
deleted file mode 100644
index 5111090bb2c0..000000000000
--- a/net/caif/cfutill.c
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-
-#define container_obj(layr) ((struct cfsrvl *) layr)
-#define UTIL_PAYLOAD 0x00
-#define UTIL_CMD_BIT 0x80
-#define UTIL_REMOTE_SHUTDOWN 0x82
-#define UTIL_FLOW_OFF 0x81
-#define UTIL_FLOW_ON 0x80
-
-static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt);
-
-struct cflayer *cfutill_create(u8 channel_id, struct dev_info *dev_info)
-{
- struct cfsrvl *util = kzalloc_obj(struct cfsrvl, GFP_ATOMIC);
- if (!util)
- return NULL;
- caif_assert(offsetof(struct cfsrvl, layer) == 0);
- cfsrvl_init(util, channel_id, dev_info, true);
- util->layer.receive = cfutill_receive;
- util->layer.transmit = cfutill_transmit;
- snprintf(util->layer.name, CAIF_LAYER_NAME_SZ, "util1");
- return &util->layer;
-}
-
-static int cfutill_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 cmd = -1;
- struct cfsrvl *service = container_obj(layr);
- caif_assert(layr != NULL);
- caif_assert(layr->up != NULL);
- caif_assert(layr->up->receive != NULL);
- caif_assert(layr->up->ctrlcmd != NULL);
- if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
-
- switch (cmd) {
- case UTIL_PAYLOAD:
- return layr->up->receive(layr->up, pkt);
- case UTIL_FLOW_OFF:
- layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
- cfpkt_destroy(pkt);
- return 0;
- case UTIL_FLOW_ON:
- layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
- cfpkt_destroy(pkt);
- return 0;
- case UTIL_REMOTE_SHUTDOWN: /* Remote Shutdown Request */
- pr_err("REMOTE SHUTDOWN REQUEST RECEIVED\n");
- layr->ctrlcmd(layr, CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND, 0);
- service->open = false;
- cfpkt_destroy(pkt);
- return 0;
- default:
- cfpkt_destroy(pkt);
- pr_warn("Unknown service control %d (0x%x)\n", cmd, cmd);
- return -EPROTO;
- }
-}
-
-static int cfutill_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 zero = 0;
- struct caif_payload_info *info;
- int ret;
- struct cfsrvl *service = container_obj(layr);
- caif_assert(layr != NULL);
- caif_assert(layr->dn != NULL);
- caif_assert(layr->dn->transmit != NULL);
-
- if (!cfsrvl_ready(service, &ret)) {
- cfpkt_destroy(pkt);
- return ret;
- }
-
- cfpkt_add_head(pkt, &zero, 1);
- /* Add info for MUX-layer to route the packet out. */
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- /*
- * To optimize alignment, we add up the size of CAIF header before
- * payload.
- */
- info->hdr_len = 1;
- info->dev_info = &service->dev_info;
- return layr->dn->transmit(layr->dn, pkt);
-}
diff --git a/net/caif/cfveil.c b/net/caif/cfveil.c
deleted file mode 100644
index 53f844c49bbb..000000000000
--- a/net/caif/cfveil.c
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/stddef.h>
-#include <linux/slab.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-
-#define VEI_PAYLOAD 0x00
-#define VEI_CMD_BIT 0x80
-#define VEI_FLOW_OFF 0x81
-#define VEI_FLOW_ON 0x80
-#define VEI_SET_PIN 0x82
-
-#define container_obj(layr) container_of(layr, struct cfsrvl, layer)
-
-static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt);
-
-struct cflayer *cfvei_create(u8 channel_id, struct dev_info *dev_info)
-{
- struct cfsrvl *vei = kzalloc_obj(struct cfsrvl, GFP_ATOMIC);
- if (!vei)
- return NULL;
- caif_assert(offsetof(struct cfsrvl, layer) == 0);
- cfsrvl_init(vei, channel_id, dev_info, true);
- vei->layer.receive = cfvei_receive;
- vei->layer.transmit = cfvei_transmit;
- snprintf(vei->layer.name, CAIF_LAYER_NAME_SZ, "vei%d", channel_id);
- return &vei->layer;
-}
-
-static int cfvei_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 cmd;
- int ret;
- caif_assert(layr->up != NULL);
- caif_assert(layr->receive != NULL);
- caif_assert(layr->ctrlcmd != NULL);
-
-
- if (cfpkt_extr_head(pkt, &cmd, 1) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- switch (cmd) {
- case VEI_PAYLOAD:
- ret = layr->up->receive(layr->up, pkt);
- return ret;
- case VEI_FLOW_OFF:
- layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_OFF_IND, 0);
- cfpkt_destroy(pkt);
- return 0;
- case VEI_FLOW_ON:
- layr->ctrlcmd(layr, CAIF_CTRLCMD_FLOW_ON_IND, 0);
- cfpkt_destroy(pkt);
- return 0;
- case VEI_SET_PIN: /* SET RS232 PIN */
- cfpkt_destroy(pkt);
- return 0;
- default: /* SET RS232 PIN */
- pr_warn("Unknown VEI control packet %d (0x%x)!\n", cmd, cmd);
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
-}
-
-static int cfvei_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- u8 tmp = 0;
- struct caif_payload_info *info;
- int ret;
- struct cfsrvl *service = container_obj(layr);
- if (!cfsrvl_ready(service, &ret))
- goto err;
- caif_assert(layr->dn != NULL);
- caif_assert(layr->dn->transmit != NULL);
-
- if (cfpkt_add_head(pkt, &tmp, 1) < 0) {
- pr_err("Packet is erroneous!\n");
- ret = -EPROTO;
- goto err;
- }
-
- /* Add info-> for MUX-layer to route the packet out. */
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- info->hdr_len = 1;
- info->dev_info = &service->dev_info;
- return layr->dn->transmit(layr->dn, pkt);
-err:
- cfpkt_destroy(pkt);
- return ret;
-}
diff --git a/net/caif/cfvidl.c b/net/caif/cfvidl.c
deleted file mode 100644
index 39e075b0a259..000000000000
--- a/net/caif/cfvidl.c
+++ /dev/null
@@ -1,65 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Author: Sjur Brendeland
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfsrvl.h>
-#include <net/caif/cfpkt.h>
-
-#define container_obj(layr) ((struct cfsrvl *) layr)
-
-static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt);
-static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt);
-
-struct cflayer *cfvidl_create(u8 channel_id, struct dev_info *dev_info)
-{
- struct cfsrvl *vid = kzalloc_obj(struct cfsrvl, GFP_ATOMIC);
- if (!vid)
- return NULL;
- caif_assert(offsetof(struct cfsrvl, layer) == 0);
-
- cfsrvl_init(vid, channel_id, dev_info, false);
- vid->layer.receive = cfvidl_receive;
- vid->layer.transmit = cfvidl_transmit;
- snprintf(vid->layer.name, CAIF_LAYER_NAME_SZ, "vid1");
- return &vid->layer;
-}
-
-static int cfvidl_receive(struct cflayer *layr, struct cfpkt *pkt)
-{
- u32 videoheader;
- if (cfpkt_extr_head(pkt, &videoheader, 4) < 0) {
- pr_err("Packet is erroneous!\n");
- cfpkt_destroy(pkt);
- return -EPROTO;
- }
- return layr->up->receive(layr->up, pkt);
-}
-
-static int cfvidl_transmit(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct cfsrvl *service = container_obj(layr);
- struct caif_payload_info *info;
- u32 videoheader = 0;
- int ret;
-
- if (!cfsrvl_ready(service, &ret)) {
- cfpkt_destroy(pkt);
- return ret;
- }
-
- cfpkt_add_head(pkt, &videoheader, 4);
- /* Add info for MUX-layer to route the packet out */
- info = cfpkt_info(pkt);
- info->channel_id = service->layer.id;
- info->dev_info = &service->dev_info;
- return layr->dn->transmit(layr->dn, pkt);
-}
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
deleted file mode 100644
index fa6a3c2634a8..000000000000
--- a/net/caif/chnl_net.c
+++ /dev/null
@@ -1,531 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson AB 2010
- * Authors: Sjur Brendeland
- * Daniel Martensson
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s(): " fmt, __func__
-
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/if_ether.h>
-#include <linux/ip.h>
-#include <linux/sched.h>
-#include <linux/sockios.h>
-#include <linux/caif/if_caif.h>
-#include <net/rtnetlink.h>
-#include <net/caif/caif_layer.h>
-#include <net/caif/cfpkt.h>
-#include <net/caif/caif_dev.h>
-
-/* GPRS PDP connection has MTU to 1500 */
-#define GPRS_PDP_MTU 1500
-/* 5 sec. connect timeout */
-#define CONNECT_TIMEOUT (5 * HZ)
-#define CAIF_NET_DEFAULT_QUEUE_LEN 500
-#define UNDEF_CONNID 0xffffffff
-
-/*This list is protected by the rtnl lock. */
-static LIST_HEAD(chnl_net_list);
-
-MODULE_DESCRIPTION("ST-Ericsson CAIF modem protocol GPRS network device");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS_RTNL_LINK("caif");
-
-enum caif_states {
- CAIF_CONNECTED = 1,
- CAIF_CONNECTING,
- CAIF_DISCONNECTED,
- CAIF_SHUTDOWN
-};
-
-struct chnl_net {
- struct cflayer chnl;
- struct caif_connect_request conn_req;
- struct list_head list_field;
- struct net_device *netdev;
- wait_queue_head_t netmgmt_wq;
- /* Flow status to remember and control the transmission. */
- bool flowenabled;
- enum caif_states state;
-};
-
-static int chnl_recv_cb(struct cflayer *layr, struct cfpkt *pkt)
-{
- struct sk_buff *skb;
- struct chnl_net *priv;
- int pktlen;
- const u8 *ip_version;
- u8 buf;
-
- priv = container_of(layr, struct chnl_net, chnl);
-
- skb = (struct sk_buff *) cfpkt_tonative(pkt);
-
- /* Get length of CAIF packet. */
- pktlen = skb->len;
-
- /* Pass some minimum information and
- * send the packet to the net stack.
- */
- skb->dev = priv->netdev;
-
- /* check the version of IP */
- ip_version = skb_header_pointer(skb, 0, 1, &buf);
- if (!ip_version) {
- kfree_skb(skb);
- return -EINVAL;
- }
-
- switch (*ip_version >> 4) {
- case 4:
- skb->protocol = htons(ETH_P_IP);
- break;
- case 6:
- skb->protocol = htons(ETH_P_IPV6);
- break;
- default:
- kfree_skb(skb);
- priv->netdev->stats.rx_errors++;
- return -EINVAL;
- }
-
- /* If we change the header in loop mode, the checksum is corrupted. */
- if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
- skb->ip_summed = CHECKSUM_UNNECESSARY;
- else
- skb->ip_summed = CHECKSUM_NONE;
-
- netif_rx(skb);
-
- /* Update statistics. */
- priv->netdev->stats.rx_packets++;
- priv->netdev->stats.rx_bytes += pktlen;
-
- return 0;
-}
-
-static int delete_device(struct chnl_net *dev)
-{
- ASSERT_RTNL();
- if (dev->netdev)
- unregister_netdevice(dev->netdev);
- return 0;
-}
-
-static void close_work(struct work_struct *work)
-{
- struct chnl_net *dev = NULL;
- struct list_head *list_node;
- struct list_head *_tmp;
-
- rtnl_lock();
- list_for_each_safe(list_node, _tmp, &chnl_net_list) {
- dev = list_entry(list_node, struct chnl_net, list_field);
- if (dev->state == CAIF_SHUTDOWN)
- dev_close(dev->netdev);
- }
- rtnl_unlock();
-}
-static DECLARE_WORK(close_worker, close_work);
-
-static void chnl_hold(struct cflayer *lyr)
-{
- struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl);
- dev_hold(priv->netdev);
-}
-
-static void chnl_put(struct cflayer *lyr)
-{
- struct chnl_net *priv = container_of(lyr, struct chnl_net, chnl);
- dev_put(priv->netdev);
-}
-
-static void chnl_flowctrl_cb(struct cflayer *layr, enum caif_ctrlcmd flow,
- int phyid)
-{
- struct chnl_net *priv = container_of(layr, struct chnl_net, chnl);
- pr_debug("NET flowctrl func called flow: %s\n",
- flow == CAIF_CTRLCMD_FLOW_ON_IND ? "ON" :
- flow == CAIF_CTRLCMD_INIT_RSP ? "INIT" :
- flow == CAIF_CTRLCMD_FLOW_OFF_IND ? "OFF" :
- flow == CAIF_CTRLCMD_DEINIT_RSP ? "CLOSE/DEINIT" :
- flow == CAIF_CTRLCMD_INIT_FAIL_RSP ? "OPEN_FAIL" :
- flow == CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND ?
- "REMOTE_SHUTDOWN" : "UNKNOWN CTRL COMMAND");
-
-
-
- switch (flow) {
- case CAIF_CTRLCMD_FLOW_OFF_IND:
- priv->flowenabled = false;
- netif_stop_queue(priv->netdev);
- break;
- case CAIF_CTRLCMD_DEINIT_RSP:
- priv->state = CAIF_DISCONNECTED;
- break;
- case CAIF_CTRLCMD_INIT_FAIL_RSP:
- priv->state = CAIF_DISCONNECTED;
- wake_up_interruptible(&priv->netmgmt_wq);
- break;
- case CAIF_CTRLCMD_REMOTE_SHUTDOWN_IND:
- priv->state = CAIF_SHUTDOWN;
- netif_tx_disable(priv->netdev);
- schedule_work(&close_worker);
- break;
- case CAIF_CTRLCMD_FLOW_ON_IND:
- priv->flowenabled = true;
- netif_wake_queue(priv->netdev);
- break;
- case CAIF_CTRLCMD_INIT_RSP:
- caif_client_register_refcnt(&priv->chnl, chnl_hold, chnl_put);
- priv->state = CAIF_CONNECTED;
- priv->flowenabled = true;
- netif_wake_queue(priv->netdev);
- wake_up_interruptible(&priv->netmgmt_wq);
- break;
- default:
- break;
- }
-}
-
-static netdev_tx_t chnl_net_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct chnl_net *priv;
- struct cfpkt *pkt = NULL;
- int len;
- int result = -1;
- /* Get our private data. */
- priv = netdev_priv(dev);
-
- if (skb->len > priv->netdev->mtu) {
- pr_warn("Size of skb exceeded MTU\n");
- kfree_skb(skb);
- dev->stats.tx_errors++;
- return NETDEV_TX_OK;
- }
-
- if (!priv->flowenabled) {
- pr_debug("dropping packets flow off\n");
- kfree_skb(skb);
- dev->stats.tx_dropped++;
- return NETDEV_TX_OK;
- }
-
- if (priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP)
- swap(ip_hdr(skb)->saddr, ip_hdr(skb)->daddr);
-
- /* Store original SKB length. */
- len = skb->len;
-
- pkt = cfpkt_fromnative(CAIF_DIR_OUT, (void *) skb);
-
- /* Send the packet down the stack. */
- result = priv->chnl.dn->transmit(priv->chnl.dn, pkt);
- if (result) {
- dev->stats.tx_dropped++;
- return NETDEV_TX_OK;
- }
-
- /* Update statistics. */
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += len;
-
- return NETDEV_TX_OK;
-}
-
-static int chnl_net_open(struct net_device *dev)
-{
- struct chnl_net *priv = NULL;
- int result = -1;
- int llifindex, headroom, tailroom, mtu;
- struct net_device *lldev;
- ASSERT_RTNL();
- priv = netdev_priv(dev);
- if (!priv) {
- pr_debug("chnl_net_open: no priv\n");
- return -ENODEV;
- }
-
- if (priv->state != CAIF_CONNECTING) {
- priv->state = CAIF_CONNECTING;
- result = caif_connect_client(dev_net(dev), &priv->conn_req,
- &priv->chnl, &llifindex,
- &headroom, &tailroom);
- if (result != 0) {
- pr_debug("err: "
- "Unable to register and open device,"
- " Err:%d\n",
- result);
- goto error;
- }
-
- lldev = __dev_get_by_index(dev_net(dev), llifindex);
-
- if (lldev == NULL) {
- pr_debug("no interface?\n");
- result = -ENODEV;
- goto error;
- }
-
- dev->needed_tailroom = tailroom + lldev->needed_tailroom;
- dev->hard_header_len = headroom + lldev->hard_header_len +
- lldev->needed_tailroom;
-
- /*
- * MTU, head-room etc is not know before we have a
- * CAIF link layer device available. MTU calculation may
- * override initial RTNL configuration.
- * MTU is minimum of current mtu, link layer mtu pluss
- * CAIF head and tail, and PDP GPRS contexts max MTU.
- */
- mtu = min_t(int, dev->mtu, lldev->mtu - (headroom + tailroom));
- mtu = min_t(int, GPRS_PDP_MTU, mtu);
- dev_set_mtu(dev, mtu);
-
- if (mtu < 100) {
- pr_warn("CAIF Interface MTU too small (%d)\n", mtu);
- result = -ENODEV;
- goto error;
- }
- }
-
- rtnl_unlock(); /* Release RTNL lock during connect wait */
-
- result = wait_event_interruptible_timeout(priv->netmgmt_wq,
- priv->state != CAIF_CONNECTING,
- CONNECT_TIMEOUT);
-
- rtnl_lock();
-
- if (result == -ERESTARTSYS) {
- pr_debug("wait_event_interruptible woken by a signal\n");
- result = -ERESTARTSYS;
- goto error;
- }
-
- if (result == 0) {
- pr_debug("connect timeout\n");
- result = -ETIMEDOUT;
- goto error;
- }
-
- if (priv->state != CAIF_CONNECTED) {
- pr_debug("connect failed\n");
- result = -ECONNREFUSED;
- goto error;
- }
- pr_debug("CAIF Netdevice connected\n");
- return 0;
-
-error:
- caif_disconnect_client(dev_net(dev), &priv->chnl);
- priv->state = CAIF_DISCONNECTED;
- pr_debug("state disconnected\n");
- return result;
-
-}
-
-static int chnl_net_stop(struct net_device *dev)
-{
- struct chnl_net *priv;
-
- ASSERT_RTNL();
- priv = netdev_priv(dev);
- priv->state = CAIF_DISCONNECTED;
- caif_disconnect_client(dev_net(dev), &priv->chnl);
- return 0;
-}
-
-static int chnl_net_init(struct net_device *dev)
-{
- struct chnl_net *priv;
- ASSERT_RTNL();
- priv = netdev_priv(dev);
- INIT_LIST_HEAD(&priv->list_field);
- return 0;
-}
-
-static void chnl_net_uninit(struct net_device *dev)
-{
- struct chnl_net *priv;
- ASSERT_RTNL();
- priv = netdev_priv(dev);
- list_del_init(&priv->list_field);
-}
-
-static const struct net_device_ops netdev_ops = {
- .ndo_open = chnl_net_open,
- .ndo_stop = chnl_net_stop,
- .ndo_init = chnl_net_init,
- .ndo_uninit = chnl_net_uninit,
- .ndo_start_xmit = chnl_net_start_xmit,
-};
-
-static void chnl_net_destructor(struct net_device *dev)
-{
- struct chnl_net *priv = netdev_priv(dev);
- caif_free_client(&priv->chnl);
-}
-
-static void ipcaif_net_setup(struct net_device *dev)
-{
- struct chnl_net *priv;
- dev->netdev_ops = &netdev_ops;
- dev->needs_free_netdev = true;
- dev->priv_destructor = chnl_net_destructor;
- dev->flags |= IFF_NOARP;
- dev->flags |= IFF_POINTOPOINT;
- dev->mtu = GPRS_PDP_MTU;
- dev->tx_queue_len = CAIF_NET_DEFAULT_QUEUE_LEN;
-
- priv = netdev_priv(dev);
- priv->chnl.receive = chnl_recv_cb;
- priv->chnl.ctrlcmd = chnl_flowctrl_cb;
- priv->netdev = dev;
- priv->conn_req.protocol = CAIFPROTO_DATAGRAM;
- priv->conn_req.link_selector = CAIF_LINK_HIGH_BANDW;
- priv->conn_req.priority = CAIF_PRIO_LOW;
- /* Insert illegal value */
- priv->conn_req.sockaddr.u.dgm.connection_id = UNDEF_CONNID;
- priv->flowenabled = false;
-
- init_waitqueue_head(&priv->netmgmt_wq);
-}
-
-
-static int ipcaif_fill_info(struct sk_buff *skb, const struct net_device *dev)
-{
- struct chnl_net *priv;
- u8 loop;
- priv = netdev_priv(dev);
- if (nla_put_u32(skb, IFLA_CAIF_IPV4_CONNID,
- priv->conn_req.sockaddr.u.dgm.connection_id) ||
- nla_put_u32(skb, IFLA_CAIF_IPV6_CONNID,
- priv->conn_req.sockaddr.u.dgm.connection_id))
- goto nla_put_failure;
- loop = priv->conn_req.protocol == CAIFPROTO_DATAGRAM_LOOP;
- if (nla_put_u8(skb, IFLA_CAIF_LOOPBACK, loop))
- goto nla_put_failure;
- return 0;
-nla_put_failure:
- return -EMSGSIZE;
-
-}
-
-static void caif_netlink_parms(struct nlattr *data[],
- struct caif_connect_request *conn_req)
-{
- if (!data) {
- pr_warn("no params data found\n");
- return;
- }
- if (data[IFLA_CAIF_IPV4_CONNID])
- conn_req->sockaddr.u.dgm.connection_id =
- nla_get_u32(data[IFLA_CAIF_IPV4_CONNID]);
- if (data[IFLA_CAIF_IPV6_CONNID])
- conn_req->sockaddr.u.dgm.connection_id =
- nla_get_u32(data[IFLA_CAIF_IPV6_CONNID]);
- if (data[IFLA_CAIF_LOOPBACK]) {
- if (nla_get_u8(data[IFLA_CAIF_LOOPBACK]))
- conn_req->protocol = CAIFPROTO_DATAGRAM_LOOP;
- else
- conn_req->protocol = CAIFPROTO_DATAGRAM;
- }
-}
-
-static int ipcaif_newlink(struct net_device *dev,
- struct rtnl_newlink_params *params,
- struct netlink_ext_ack *extack)
-{
- struct nlattr **data = params->data;
- int ret;
- struct chnl_net *caifdev;
- ASSERT_RTNL();
- caifdev = netdev_priv(dev);
- caif_netlink_parms(data, &caifdev->conn_req);
-
- ret = register_netdevice(dev);
- if (ret)
- pr_warn("device rtml registration failed\n");
- else
- list_add(&caifdev->list_field, &chnl_net_list);
-
- /* Use ifindex as connection id, and use loopback channel default. */
- if (caifdev->conn_req.sockaddr.u.dgm.connection_id == UNDEF_CONNID) {
- caifdev->conn_req.sockaddr.u.dgm.connection_id = dev->ifindex;
- caifdev->conn_req.protocol = CAIFPROTO_DATAGRAM_LOOP;
- }
- return ret;
-}
-
-static int ipcaif_changelink(struct net_device *dev, struct nlattr *tb[],
- struct nlattr *data[],
- struct netlink_ext_ack *extack)
-{
- struct chnl_net *caifdev;
- ASSERT_RTNL();
- caifdev = netdev_priv(dev);
- caif_netlink_parms(data, &caifdev->conn_req);
- netdev_state_change(dev);
- return 0;
-}
-
-static size_t ipcaif_get_size(const struct net_device *dev)
-{
- return
- /* IFLA_CAIF_IPV4_CONNID */
- nla_total_size(4) +
- /* IFLA_CAIF_IPV6_CONNID */
- nla_total_size(4) +
- /* IFLA_CAIF_LOOPBACK */
- nla_total_size(2) +
- 0;
-}
-
-static const struct nla_policy ipcaif_policy[IFLA_CAIF_MAX + 1] = {
- [IFLA_CAIF_IPV4_CONNID] = { .type = NLA_U32 },
- [IFLA_CAIF_IPV6_CONNID] = { .type = NLA_U32 },
- [IFLA_CAIF_LOOPBACK] = { .type = NLA_U8 }
-};
-
-
-static struct rtnl_link_ops ipcaif_link_ops __read_mostly = {
- .kind = "caif",
- .priv_size = sizeof(struct chnl_net),
- .setup = ipcaif_net_setup,
- .maxtype = IFLA_CAIF_MAX,
- .policy = ipcaif_policy,
- .newlink = ipcaif_newlink,
- .changelink = ipcaif_changelink,
- .get_size = ipcaif_get_size,
- .fill_info = ipcaif_fill_info,
-
-};
-
-static int __init chnl_init_module(void)
-{
- return rtnl_link_register(&ipcaif_link_ops);
-}
-
-static void __exit chnl_exit_module(void)
-{
- struct chnl_net *dev = NULL;
- struct list_head *list_node;
- struct list_head *_tmp;
- rtnl_link_unregister(&ipcaif_link_ops);
- rtnl_lock();
- list_for_each_safe(list_node, _tmp, &chnl_net_list) {
- dev = list_entry(list_node, struct chnl_net, list_field);
- list_del_init(list_node);
- delete_device(dev);
- }
- rtnl_unlock();
-}
-
-module_init(chnl_init_module);
-module_exit(chnl_exit_module);
diff --git a/arch/arm/configs/u8500_defconfig b/arch/arm/configs/u8500_defconfig
index e88533b78327..de4af7c750ca 100644
--- a/arch/arm/configs/u8500_defconfig
+++ b/arch/arm/configs/u8500_defconfig
@@ -37,7 +37,6 @@ CONFIG_CFG80211=y
CONFIG_CFG80211_DEBUGFS=y
CONFIG_MAC80211=y
CONFIG_MAC80211_LEDS=y
-CONFIG_CAIF=y
CONFIG_NFC=m
CONFIG_NFC_HCI=m
CONFIG_NFC_SHDLC=y
--
2.53.0
^ permalink raw reply related
* RE: [PATCH v2 net 1/1] net/sched: sch_dualpi2: fix limit/memlimit enforcement when dequeueing L-queue
From: Chia-Yu Chang (Nokia) @ 2026-04-16 18:30 UTC (permalink / raw)
To: Stephen Hemminger
Cc: victor@mojatatu.com, hxzene@gmail.com,
linux-hardening@vger.kernel.org, kees@kernel.org,
gustavoars@kernel.org, jhs@mojatatu.com, jiri@resnulli.us,
davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, linux-kernel@vger.kernel.org,
netdev@vger.kernel.org, horms@kernel.org, ij@kernel.org,
ncardwell@google.com, Koen De Schepper (Nokia),
g.white@cablelabs.com, ingemar.s.johansson@ericsson.com,
mirja.kuehlewind@ericsson.com, cheshire@apple.com, rs.ietf@gmx.at,
Jason_Livingood@comcast.com, vidhi_goel@apple.com
In-Reply-To: <20260416105505.22383f01@phoenix.local>
> -----Original Message-----
> From: Stephen Hemminger <stephen@networkplumber.org>
> Sent: Thursday, April 16, 2026 7:55 PM
> To: Chia-Yu Chang (Nokia) <chia-yu.chang@nokia-bell-labs.com>
> Cc: victor@mojatatu.com; hxzene@gmail.com; linux-hardening@vger.kernel.org; kees@kernel.org; gustavoars@kernel.org; jhs@mojatatu.com; jiri@resnulli.us; davem@davemloft.net; edumazet@google.com; kuba@kernel.org; pabeni@redhat.com; linux-kernel@vger.kernel.org; netdev@vger.kernel.org; horms@kernel.org; ij@kernel.org; ncardwell@google.com; Koen De Schepper (Nokia) <koen.de_schepper@nokia-bell-labs.com>; g.white@cablelabs.com; ingemar.s.johansson@ericsson.com; mirja.kuehlewind@ericsson.com; cheshire@apple.com; rs.ietf@gmx.at; Jason_Livingood@comcast.com; vidhi_goel@apple.com
> Subject: Re: [PATCH v2 net 1/1] net/sched: sch_dualpi2: fix limit/memlimit enforcement when dequeueing L-queue
>
>
> CAUTION: This is an external email. Please be very careful when clicking links or opening attachments. See the URL nok.it/ext for additional information.
>
>
>
> On Thu, 16 Apr 2026 19:09:06 +0200
> chia-yu.chang@nokia-bell-labs.com wrote:
>
> > From: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
> >
> > Fix dualpi2_change() to correctly enforce updated limit and memlimit
> > values after a configuration change of the dualpi2 qdisc.
> >
> > Before this patch, dualpi2_change() always attempted to dequeue
> > packets via the root qdisc (C-queue) when reducing backlog or memory
> > usage, and unconditionally assumed that a valid skb will be returned.
> > When traffic classification results in packets being queued in the
> > L-queue while the C-queue is empty, this leads to a NULL skb
> > dereference during limit or memlimit enforcement.
> >
> > This is fixed by first dequeuing from the C-queue path if it is non-empty.
> > Once the C-queue is empty, packets are dequeued directly from the L-queue.
> > Return values from qdisc_dequeue_internal() are checked for both
> > queues. When dequeuing from the L-queue, the parent qdisc qlen and
> > backlog counters are updated explicitly to keep overall qdisc statistics consistent.
> >
> > Fixes: 320d031ad6e4 ("sched: Struct definition and parsing of dualpi2
> > qdisc")
> > Reported-by: "Kito Xu (veritas501)" <hxzene@gmail.com>
> > Signed-off-by: Chia-Yu Chang <chia-yu.chang@nokia-bell-labs.com>
> > ---
>
> I was a little concerned about the complexity of managing qlen here.
> But could not find anything obvious.
Hi Stephen,
This fix relies on some existing assmuptions of DualPI2.
>
> Turned to AI review and it found some things:
>
> Right fix direction and the reported crash is real. A few issues before this is ready:
>
> 1. The `c_len` construction is fragile. Declared `int`, initialized from a `u32 - u32`. If the invariant `qdisc_qlen(sch) >= qdisc_qlen(q->l_queue)` is ever violated, you get a large positive value, the C-queue branch is taken on an empty C-queue, `qdisc_dequeue_internal()` returns NULL, and the loop breaks out without draining the L-queue -- leaving the qdisc over limit. Simpler and more robust to just compare the two qlens directly and drop the delta variable entirely.
>
In current dequeue_packet() of DualPI2, we also calculate c_len via the same approach (line 524).
As we only have queue length of L-queue and both C- and L-queues, so this is the way we derive the queue length of C-queue.
> 2. Missing else/termination. If both branches' conditions are false (neither `c_len` nor `qdisc_qlen(q->l_queue)`) but the outer `while` still holds because `memory_used > memory_limit`, the loop spins forever. An explicit `else break;` guards against an accounting desync becoming a hang.
>
This shall not happen, but adding an extra else guard indeed is definitely a good suggestion.
> 3. Whitespace: two lines in the L-queue branch use spaces instead of tabs --
>
> + q->memory_used -= skb->truesize;
> + rtnl_qdisc_drop(skb, q->l_queue);
>
> checkpatch will flag this.
Sure, I will fix this, sorry for my miss.
>
> 4. Comment style. The three-line comment at the end of the L-queue branch doesn't follow the net subsystem multi-line comment style (leading ' * ' on continuation lines, closing ' */' on its own line).
> Once the code is cleaner, the comment could also just be dropped or shortened to one line.
>
Thanks, I will fix this as well.
> 5. The accounting in the L-queue branch is correct, but only if you trace the enqueue invariants carefully: L-queue packets are counted in
> *both* `sch` and `q->l_queue` on enqueue (see dualpi2_enqueue_skb lines 413-423), `qdisc_dequeue_internal(q->l_queue, true)` adjusts l_queue's side, and the explicit `--sch->q.qlen` + `qdisc_qstats_backlog_dec(sch, skb)` adjusts sch's side. Separately, the C-queue branch now quietly relies on the post-CVE-2025-39677 semantics of `qdisc_dequeue_internal()` handling parent backlog -- which is why the pre-patch `qdisc_qstats_backlog_dec(sch, skb)` could be removed.
> Neither of these load-bearing invariants is documented in the code or the commit message. Please add an inline comment in the L-queue branch explaining the double-count-on-enqueue, and mention the
> qdisc_dequeue_internal() dependency in the commit log.
Yes, L-queue packets are counted in both parent qdisc (sch) and child qdisc (q->l_queue) during enqueue.
And we re-use the qdisc_dequeue_internal() of sch_generic.h for C-queue case.
> 6. Commit message / subject. Subject reads as if only the L-queue path changed, but the whole drain loop was restructured. Something like
> "sch_dualpi2: drain both C-queue and L-queue in dualpi2_change()" would describe it better. Also, on NULL return from qdisc_dequeue_internal() the loop silently breaks -- if that ever triggers it means qdisc_qlen()
> > 0 but dequeue returned NULL, which is a real invariant violation.
> > Worth a WARN_ON_ONCE().
>
> Suggested shape:
>
> while (qdisc_qlen(sch) > sch->limit ||
> q->memory_used > q->memory_limit) {
> struct sk_buff *skb;
>
> if (qdisc_qlen(sch) > qdisc_qlen(q->l_queue)) {
> skb = qdisc_dequeue_internal(sch, true);
> if (!skb)
> break;
> q->memory_used -= skb->truesize;
> rtnl_qdisc_drop(skb, sch);
> } else if (qdisc_qlen(q->l_queue)) {
> skb = qdisc_dequeue_internal(q->l_queue, true);
> if (!skb)
> break;
> /* L-queue packets are counted in both sch and
> * l_queue on enqueue; qdisc_dequeue_internal()
> * handled l_queue, account sch here.
> */
> sch->q.qlen--;
> qdisc_qstats_backlog_dec(sch, skb);
> q->memory_used -= skb->truesize;
> rtnl_qdisc_drop(skb, q->l_queue);
> qdisc_qstats_drop(sch);
> } else {
> break;
> }
> }
>
>
> As with any AI feedback, expect it to generate hints but also be wrong.
I am ok with this suggestion and I will take action in v3.
But I would say the origianl c_len calculation already existed in dualpi2 of dequeue_packet().
And this is because we maintained parent and child qdisc statistics during normal enqueue and dequeue operations.
Thanks!
Chia-Yu
^ permalink raw reply
* Re: [PATCH net 1/1] mptcp: hold subflow request owners when cloning reqsk
From: Kuniyuki Iwashima @ 2026-04-16 18:48 UTC (permalink / raw)
To: Matthieu Baerts
Cc: Ren Wei, netdev, mptcp, davem, edumazet, kuba, pabeni, horms,
ncardwell, dsahern, martineau, geliang, daniel, kafai, yuantan098,
yifanwucs, tomapufckgml, bird, caoruide123, enjou1224z
In-Reply-To: <5f06a4e8-0b49-4119-94f9-3a8a1e430e0d@kernel.org>
On Thu, Apr 16, 2026 at 10:45 AM Matthieu Baerts <matttbe@kernel.org> wrote:
>
> Hi Ren,
>
> On 15/04/2026 11:31, Ren Wei wrote:
> > From: Ruide Cao <caoruide123@gmail.com>
> >
> > TCP request migration clones pending request sockets with
> > inet_reqsk_clone(). For MPTCP MP_JOIN requests this raw-copies
> > subflow_req->msk, but the cloned request does not take a new reference.
> >
> > Both the original and the cloned request can later drop the same msk in
> > subflow_req_destructor(), and a migrated request may keep a dangling msk
> > pointer after the original owner has already been released.
> >
> > Add a request_sock clone callback and let MPTCP grab a reference for cloned
> > subflow requests that carry an msk. This keeps ownership balanced across
> > both successful migrations and failed clone/insert paths without changing
> > other protocols.
>
> Thank you for this patch!
>
> Indeed, it looks like this path has not been covered by the MPTCP test
> suite so far.
>
> By chance, do you have any simpler reproducer using packetdrill? (the
> MPTCP fork)
>
> https://github.com/multipath-tcp/packetdrill
>
> Also, I see Sashiko is pointing to a potential issue with MP_CAPABLE and
> the token, also only visible with net.ipv4.tcp_migrate_req=1:
>
> https://sashiko.dev/#/patchset/86e2514b533bf4d55d4aa2fdbf1404022e8c9430.1776149210.git.caoruide123%40gmail.com
>
> Are you also looking at that?
>
> > Fixes: c905dee62232 ("tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.")
> > Cc: stable@kernel.org
> > Reported-by: Yuan Tan <yuantan098@gmail.com>
> > Reported-by: Yifan Wu <yifanwucs@gmail.com>
> > Reported-by: Juefei Pu <tomapufckgml@gmail.com>
> > Reported-by: Xin Liu <bird@lzu.edu.cn>
> > Signed-off-by: Ruide Cao <caoruide123@gmail.com>
> > Tested-by: Ren Wei <enjou1224z@gmail.com>
> > Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
> > ---
> > include/net/request_sock.h | 2 ++
> > net/ipv4/inet_connection_sock.c | 3 +++
> > net/mptcp/subflow.c | 13 +++++++++++++
> > 3 files changed, 18 insertions(+)
> >
> > diff --git a/include/net/request_sock.h b/include/net/request_sock.h
> > index 5a9c826a7092..560e464c400f 100644
> > --- a/include/net/request_sock.h
> > +++ b/include/net/request_sock.h
> > @@ -36,6 +36,8 @@ struct request_sock_ops {
> > struct sk_buff *skb,
> > enum sk_rst_reason reason);
> > void (*destructor)(struct request_sock *req);
> > + void (*init_clone)(const struct request_sock *req,
> > + struct request_sock *new_req);
>
> @TCP/INET maintainers: are you OK with this new function pointer?
>
> Currently, MPTCP is the only user, and "req" is not used, see below.
>
> > };
> >
> > struct saved_syn {
> > diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
> > index e961936b6be7..140a9e96ad58 100644
> > --- a/net/ipv4/inet_connection_sock.c
> > +++ b/net/ipv4/inet_connection_sock.c
> > @@ -954,6 +954,9 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req,
> > if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener)
> > rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq);
>
> (Maybe TCP with fastopen could be this other user to call
> rcu_assign_pointer()? (net-next material))
>
> > + if (req->rsk_ops->init_clone)
> > + req->rsk_ops->init_clone(req, nreq);
I think a simple direct call is better.
#ifdef CONFIG_MPTCP
if (tcp_rsk(req)->is_mptcp)
mptcp_reqsk_clone(nreq);
#endif
> > +
> > return nreq;
> > }
> >
> > diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> > index 4ff5863aa9fd..5f4069647822 100644
> > --- a/net/mptcp/subflow.c
> > +++ b/net/mptcp/subflow.c
> > @@ -47,6 +47,17 @@ static void subflow_req_destructor(struct request_sock *req)
> > mptcp_token_destroy_request(req);
> > }
> >
> > +static void subflow_req_clone(const struct request_sock *req,
> > + struct request_sock *new_req)
> > +{
> > + struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(new_req);
> > +
> > + (void)req;
>
>
> Note: if 'req' is not used, and MPTCP is currently the only user of this
> new init_clone() callback, perhaps enough to pass only 'new_req' for the
> moment? 'req' can be added later if other users need it, no?
>
> With only init_close(nreq) in a v2, or if TCP maintainers prefer to keep
> it, feel free to add:
>
> Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
>
>
> Cheers,
> Matt
> --
> Sponsored by the NGI0 Core fund.
>
^ permalink raw reply
* [PATCH net v4 0/2] net/sched: taprio: fix NULL pointer dereference in class dump
From: Weiming Shi @ 2026-04-16 18:55 UTC (permalink / raw)
To: jhs, vinicius.gomes, jiri, davem, edumazet, kuba, pabeni, shuah
Cc: horms, vladimir.oltean, xmei5, netdev, linux-kselftest,
Weiming Shi
Fix a NULL pointer dereference in taprio_dump_class() reachable by an
unprivileged local user on kernels with unprivileged user namespaces
enabled and CONFIG_NET_SCH_TAPRIO=y. The bug allows a local DoS via a
crafted sequence of taprio child-qdisc graft, delete, and class dump.
Patch 1/2 is the fix: replace NULL entries in q->qdiscs[] with the
global &noop_qdisc singleton so that control-plane dump paths, as well
as the existing NULL guards in the data-plane enqueue/dequeue paths,
cannot deref a NULL child qdisc.
Patch 2/2 is a tdc regression test that drives the graft + delete +
class-dump sequence on a multi-queue netdevsim device. It panics the
vulnerable kernel and passes on the fixed one.
v4:
add selftests/tc-testing regression test (patch 2/2) (Jamal).
add Assisted-by tag.
v3: https://lore.kernel.org/netdev/20260414104311.74115-2-bestswngs@gmail.com/
fix broken patch
v2: https://lore.kernel.org/netdev/20260410153902.955227-2-bestswngs@gmail.com/
also update NULL guards in taprio_enqueue() and
taprio_dequeue_from_txq() to avoid qlen/backlog inflation (Paolo).
v1: https://lore.kernel.org/netdev/20260330102904.2677818-5-bestswngs@gmail.com/
Weiming Shi (2):
net/sched: taprio: fix NULL pointer dereference in class dump
selftests/tc-testing: add taprio test for class dump after child
delete
net/sched/sch_taprio.c | 11 +++++---
.../tc-testing/tc-tests/qdiscs/taprio.json | 26 +++++++++++++++++++
2 files changed, 33 insertions(+), 4 deletions(-)
--
2.43.0
^ permalink raw reply
* [PATCH net v4 1/2] net/sched: taprio: fix NULL pointer dereference in class dump
From: Weiming Shi @ 2026-04-16 18:55 UTC (permalink / raw)
To: jhs, vinicius.gomes, jiri, davem, edumazet, kuba, pabeni, shuah
Cc: horms, vladimir.oltean, xmei5, netdev, linux-kselftest,
Weiming Shi
In-Reply-To: <20260416185501.647884-2-bestswngs@gmail.com>
When a TAPRIO child qdisc is deleted via RTM_DELQDISC, taprio_graft()
is called with new == NULL and stores NULL into q->qdiscs[cl - 1].
Subsequent RTM_GETTCLASS dump operations walk all classes via
taprio_walk() and call taprio_dump_class(), which calls taprio_leaf()
returning the NULL pointer, then dereferences it to read child->handle,
causing a kernel NULL pointer dereference.
The bug is reachable with namespace-scoped CAP_NET_ADMIN on any kernel
with CONFIG_NET_SCH_TAPRIO enabled. On systems with unprivileged user
namespaces enabled, an unprivileged local user can trigger a kernel
panic by creating a taprio qdisc inside a new network namespace,
grafting an explicit child qdisc, deleting it, and requesting a class
dump. The RTM_GETTCLASS dump itself requires no capability.
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000007: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000038-0x000000000000003f]
RIP: 0010:taprio_dump_class (net/sched/sch_taprio.c:2478)
Call Trace:
<TASK>
tc_fill_tclass (net/sched/sch_api.c:1966)
qdisc_class_dump (net/sched/sch_api.c:2326)
taprio_walk (net/sched/sch_taprio.c:2514)
tc_dump_tclass_qdisc (net/sched/sch_api.c:2352)
tc_dump_tclass_root (net/sched/sch_api.c:2370)
tc_dump_tclass (net/sched/sch_api.c:2431)
rtnl_dumpit (net/core/rtnetlink.c:6864)
netlink_dump (net/netlink/af_netlink.c:2325)
rtnetlink_rcv_msg (net/core/rtnetlink.c:6959)
netlink_rcv_skb (net/netlink/af_netlink.c:2550)
</TASK>
Fix this by substituting &noop_qdisc when new is NULL in
taprio_graft(), a common pattern used by other qdiscs (e.g.,
multiq_graft()) to ensure the q->qdiscs[] slots are never NULL.
This makes control-plane dump paths safe without requiring individual
NULL checks.
Since the data-plane paths (taprio_enqueue and taprio_dequeue_from_txq)
previously had explicit NULL guards that would drop/skip the packet
cleanly, update those checks to test for &noop_qdisc instead. Without
this, packets would reach taprio_enqueue_one() which increments the root
qdisc's qlen and backlog before calling the child's enqueue; noop_qdisc
drops the packet but those counters are never rolled back, permanently
inflating the root qdisc's statistics.
Fixes: 665338b2a7a0 ("net/sched: taprio: dump class stats for the actual q->qdiscs[]")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Assisted-by: Claude:claude-opus-4-6
---
net/sched/sch_taprio.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/net/sched/sch_taprio.c b/net/sched/sch_taprio.c
index 8e37528119506..e866e5288c3e6 100644
--- a/net/sched/sch_taprio.c
+++ b/net/sched/sch_taprio.c
@@ -634,7 +634,7 @@ static int taprio_enqueue(struct sk_buff *skb, struct Qdisc *sch,
queue = skb_get_queue_mapping(skb);
child = q->qdiscs[queue];
- if (unlikely(!child))
+ if (unlikely(child == &noop_qdisc))
return qdisc_drop(skb, sch, to_free);
if (taprio_skb_exceeds_queue_max_sdu(sch, skb)) {
@@ -717,7 +717,7 @@ static struct sk_buff *taprio_dequeue_from_txq(struct Qdisc *sch, int txq,
int len;
u8 tc;
- if (unlikely(!child))
+ if (unlikely(child == &noop_qdisc))
return NULL;
if (TXTIME_ASSIST_IS_ENABLED(q->flags))
@@ -2183,6 +2183,9 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
if (!dev_queue)
return -EINVAL;
+ if (!new)
+ new = &noop_qdisc;
+
if (dev->flags & IFF_UP)
dev_deactivate(dev, false);
@@ -2196,14 +2199,14 @@ static int taprio_graft(struct Qdisc *sch, unsigned long cl,
*old = q->qdiscs[cl - 1];
if (FULL_OFFLOAD_IS_ENABLED(q->flags)) {
WARN_ON_ONCE(dev_graft_qdisc(dev_queue, new) != *old);
- if (new)
+ if (new != &noop_qdisc)
qdisc_refcount_inc(new);
if (*old)
qdisc_put(*old);
}
q->qdiscs[cl - 1] = new;
- if (new)
+ if (new != &noop_qdisc)
new->flags |= TCQ_F_ONETXQUEUE | TCQ_F_NOPARENT;
if (dev->flags & IFF_UP)
--
2.43.0
^ permalink raw reply related
* [PATCH net v4 2/2] selftests/tc-testing: add taprio test for class dump after child delete
From: Weiming Shi @ 2026-04-16 18:55 UTC (permalink / raw)
To: jhs, vinicius.gomes, jiri, davem, edumazet, kuba, pabeni, shuah
Cc: horms, vladimir.oltean, xmei5, netdev, linux-kselftest,
Weiming Shi
In-Reply-To: <20260416185501.647884-2-bestswngs@gmail.com>
Add a regression test for the NULL pointer dereference fixed in the
previous commit. Before the fix, taprio_graft() stored NULL into
q->qdiscs[cl - 1] when an explicitly grafted child qdisc was deleted
via RTM_DELQDISC; the next RTM_GETTCLASS dump then crashed the kernel
in taprio_dump_class() while reading child->handle.
The test installs a taprio root qdisc on a multi-queue netdevsim
device, grafts a pfifo child onto class 8001:1, deletes that child,
and then performs a class dump. On a fixed kernel the dump succeeds
and all eight taprio classes are listed; on an unpatched kernel the
class dump crashes, which surfaces as a test failure.
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
Assisted-by: Claude:claude-opus-4-6
---
.../tc-testing/tc-tests/qdiscs/taprio.json | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
index 557fb074acf0c..cd19d05925e40 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/taprio.json
@@ -302,5 +302,31 @@
"$TC qdisc del dev $ETH root",
"echo \"1\" > /sys/bus/netdevsim/del_device"
]
+ },
+ {
+ "id": "c7e1",
+ "name": "Class dump after graft and delete of explicit child qdisc",
+ "category": [
+ "qdisc",
+ "taprio"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "echo \"1 1 8\" > /sys/bus/netdevsim/new_device",
+ "$TC qdisc replace dev $ETH handle 8001: parent root taprio num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 base-time 0 sched-entry S ff 20000000 clockid CLOCK_TAI",
+ "$TC qdisc add dev $ETH parent 8001:1 handle 8002: pfifo",
+ "$TC qdisc del dev $ETH parent 8001:1 handle 8002:"
+ ],
+ "cmdUnderTest": "$TC class show dev $ETH",
+ "expExitCode": "0",
+ "verifyCmd": "$TC class show dev $ETH",
+ "matchPattern": "class taprio 8001:[0-9]+ root",
+ "matchCount": "8",
+ "teardown": [
+ "$TC qdisc del dev $ETH root",
+ "echo \"1\" > /sys/bus/netdevsim/del_device"
+ ]
}
]
--
2.43.0
^ permalink raw reply related
* [PATCH net v8 00/15] net: sleepable ndo_set_rx_mode
From: Stanislav Fomichev @ 2026-04-16 18:56 UTC (permalink / raw)
To: netdev; +Cc: davem, edumazet, kuba, pabeni
This series adds a new ndo_set_rx_mode_async callback that enables
drivers to handle address list updates in a sleepable context. The
current ndo_set_rx_mode is called under the netif_addr_lock spinlock
with BHs disabled, which prevents drivers from sleeping. This is
problematic for ops-locked drivers that need to sleep.
The approach:
1. Add snapshot/reconcile infrastructure for address lists
2. Introduce dev_rx_mode_work that takes snapshots under the lock,
drops the lock, calls the driver, then reconciles changes back
3. Move promiscuity handling into the scheduled work as well
4. Convert existing ops-locked drivers to ndo_set_rx_mode_async
5. Add a warning for ops-locked drivers still using ndo_set_rx_mode
6. Add a selftest exercising the team+bridge+macvlan topology that
triggers the addr_lock -> ops_lock ordering issue
v8:
- fix netdev tracker release, similar approach to linkwatch (Jakub)
v7:
- rebase and address netkit warning in a separate patch (Jakub)
- keep only CONFIG_NET_TEAM=y in selftest (Breno)
v6:
- relink ref_ha in __hw_addr_list_reconcile (AI Review)
- set real_ha->sync_cnt to delta, not 1 (AI Review)
- s/KUNIT_ASSERT_EQ/KUNIT_EXPECT_EQ/ (AI Review)
- drop netif_rx_mode_clean from free_netdev (AI Review)
- clarify deep hierarchy flush in commit message (AI Review)
- use dev trackers (AI Review)
- drop mc argument from bnxt_cfg_rx_mode (AI Review)
- keep uc_update, but as an argument in bnxt (AI Review)
- add BNXT_STATE_OPEN check after re-lock in bnxt (AI Review)
- add addr lock around iavf_set_rx_mode (AI Review)
v5:
- resolve 32 bit failure (Jakub)
v4:
- rebase on https://lore.kernel.org/netdev/20260319005456.82745-1-saeed@kernel.org/T/#u (Cosmin)
- reword ndo_set_rx_mode_async kdoc (Jakub)
- s/EXPORT_SYMBOL/EXPORT_SYMBOL_IF_KUNIT/ (Jakub)
- remove netif_up_and_present (Jakub)
- netif_addr_lists_snapshot + netif_addr_lists_reconcile to better
explain mix-and-match between
ndo_set_rx_mode/ndo_set_rx_mode_async/ndo_change_rx_flags (Jakub)
- s/cancel_work_sync/flush_work/ (Jakub)
- separate commit to cache snapshot entries (Jakub)
- add dev_addr_test_snapshot_benchmark (Jakub)
- dev_addr_test_snapshot_benchmark: 1024 addrs x 1000 snapshots: 89872802 ns total, 89872 ns/iter
- remove redundant bnxt_uc_list_updated (Michael)
- switch to linkwatch-like work stealing (Jakub)
v3:
- module_export(__rtnl_unlock) (nipa)
- s/netdev_uc_count/netdev_hw_addr_list_count/ in bnxt (Aleksandr)
v2:
- wifi: cfg80211: use __rtnl_unlock in nl80211_pre_doit (syzbot)
- simplify mlx5e_sync_netdev_addr for !uc (Cosmin)
- switch to snapshot in bnxt_cfg_rx_mode (Michael)
- add team to net/config (Jakub)
Stanislav Fomichev (15):
net: add address list snapshot and reconciliation infrastructure
net: introduce ndo_set_rx_mode_async and netdev_rx_mode_work
net: cache snapshot entries for ndo_set_rx_mode_async
net: move promiscuity handling into netdev_rx_mode_work
fbnic: convert to ndo_set_rx_mode_async
mlx5: convert to ndo_set_rx_mode_async
bnxt: convert to ndo_set_rx_mode_async
bnxt: use snapshot in bnxt_cfg_rx_mode
iavf: convert to ndo_set_rx_mode_async
netdevsim: convert to ndo_set_rx_mode_async
dummy: convert to ndo_set_rx_mode_async
netkit: convert to ndo_set_rx_mode_async
net: warn ops-locked drivers still using ndo_set_rx_mode
selftests: net: add team_bridge_macvlan rx_mode test
selftests: net: use ip commands instead of teamd in team rx_mode test
Documentation/networking/netdevices.rst | 13 +
drivers/net/dummy.c | 6 +-
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 58 +--
drivers/net/ethernet/intel/iavf/iavf_main.c | 16 +-
.../net/ethernet/mellanox/mlx5/core/en/fs.h | 5 +-
.../net/ethernet/mellanox/mlx5/core/en_fs.c | 32 +-
.../net/ethernet/mellanox/mlx5/core/en_main.c | 13 +-
.../net/ethernet/meta/fbnic/fbnic_netdev.c | 20 +-
.../net/ethernet/meta/fbnic/fbnic_netdev.h | 4 +-
drivers/net/ethernet/meta/fbnic/fbnic_pci.c | 4 +-
drivers/net/ethernet/meta/fbnic/fbnic_rpc.c | 2 +-
drivers/net/netdevsim/netdev.c | 8 +-
drivers/net/netkit.c | 6 +-
include/linux/netdevice.h | 28 ++
net/core/dev.c | 67 +--
net/core/dev.h | 4 +
net/core/dev_addr_lists.c | 385 ++++++++++++++++-
net/core/dev_addr_lists_test.c | 387 +++++++++++++++++-
net/core/dev_api.c | 3 +
net/core/dev_ioctl.c | 6 +-
net/core/rtnetlink.c | 1 +
.../selftests/drivers/net/bonding/lag_lib.sh | 17 +-
.../drivers/net/team/dev_addr_lists.sh | 2 -
tools/testing/selftests/net/config | 1 +
tools/testing/selftests/net/rtnetlink.sh | 44 ++
25 files changed, 992 insertions(+), 140 deletions(-)
--
2.52.0
^ permalink raw reply
* [PATCH net v8 01/15] net: add address list snapshot and reconciliation infrastructure
From: Stanislav Fomichev @ 2026-04-16 18:56 UTC (permalink / raw)
To: netdev; +Cc: davem, edumazet, kuba, pabeni, Aleksandr Loktionov
In-Reply-To: <20260416185712.2155425-1-sdf@fomichev.me>
Introduce __hw_addr_list_snapshot() and __hw_addr_list_reconcile()
for use by the upcoming ndo_set_rx_mode_async callback.
The async rx_mode path needs to snapshot the device's unicast and
multicast address lists under the addr_lock, hand those snapshots
to the driver (which may sleep), and then propagate any sync_cnt
changes back to the real lists. Two identical snapshots are taken:
a work copy for the driver to pass to __hw_addr_sync_dev() and a
reference copy to compute deltas against.
__hw_addr_list_reconcile() walks the reference snapshot comparing
each entry against the work snapshot to determine what the driver
synced or unsynced. It then applies those deltas to the real list,
handling concurrent modifications:
- If the real entry was concurrently removed but the driver synced
it to hardware (delta > 0), re-insert a stale entry so the next
work run properly unsyncs it from hardware.
- If the entry still exists, apply the delta normally. An entry
whose refcount drops to zero is removed.
# dev_addr_test_snapshot_benchmark: 1024 addrs x 1000 snapshots: 89872802 ns total, 89872 ns/iter
# dev_addr_test_snapshot_benchmark.speed: slow
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
---
include/linux/netdevice.h | 7 +
net/core/dev.h | 1 +
net/core/dev_addr_lists.c | 109 +++++++++-
net/core/dev_addr_lists_test.c | 363 ++++++++++++++++++++++++++++++++-
4 files changed, 477 insertions(+), 3 deletions(-)
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 7969fcdd5ac4..a84c55488b8c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -5004,6 +5004,13 @@ void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
int (*unsync)(struct net_device *,
const unsigned char *));
void __hw_addr_init(struct netdev_hw_addr_list *list);
+void __hw_addr_flush(struct netdev_hw_addr_list *list);
+int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap,
+ const struct netdev_hw_addr_list *list,
+ int addr_len);
+void __hw_addr_list_reconcile(struct netdev_hw_addr_list *real_list,
+ struct netdev_hw_addr_list *work,
+ struct netdev_hw_addr_list *ref, int addr_len);
/* Functions used for device addresses handling */
void dev_addr_mod(struct net_device *dev, unsigned int offset,
diff --git a/net/core/dev.h b/net/core/dev.h
index 628bdaebf0ca..585b6d7e88df 100644
--- a/net/core/dev.h
+++ b/net/core/dev.h
@@ -78,6 +78,7 @@ void linkwatch_run_queue(void);
void dev_addr_flush(struct net_device *dev);
int dev_addr_init(struct net_device *dev);
void dev_addr_check(struct net_device *dev);
+void __hw_addr_flush(struct netdev_hw_addr_list *list);
#if IS_ENABLED(CONFIG_NET_SHAPER)
void net_shaper_flush_netdev(struct net_device *dev);
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index 76c91f224886..bb4851bc55ce 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -11,6 +11,7 @@
#include <linux/rtnetlink.h>
#include <linux/export.h>
#include <linux/list.h>
+#include <kunit/visibility.h>
#include "dev.h"
@@ -481,7 +482,7 @@ void __hw_addr_unsync_dev(struct netdev_hw_addr_list *list,
}
EXPORT_SYMBOL(__hw_addr_unsync_dev);
-static void __hw_addr_flush(struct netdev_hw_addr_list *list)
+void __hw_addr_flush(struct netdev_hw_addr_list *list)
{
struct netdev_hw_addr *ha, *tmp;
@@ -492,6 +493,7 @@ static void __hw_addr_flush(struct netdev_hw_addr_list *list)
}
list->count = 0;
}
+EXPORT_SYMBOL_IF_KUNIT(__hw_addr_flush);
void __hw_addr_init(struct netdev_hw_addr_list *list)
{
@@ -501,6 +503,111 @@ void __hw_addr_init(struct netdev_hw_addr_list *list)
}
EXPORT_SYMBOL(__hw_addr_init);
+/**
+ * __hw_addr_list_snapshot - create a snapshot copy of an address list
+ * @snap: destination snapshot list (needs to be __hw_addr_init-initialized)
+ * @list: source address list to snapshot
+ * @addr_len: length of addresses
+ *
+ * Creates a copy of @list with individually allocated entries suitable
+ * for use with __hw_addr_sync_dev() and other list manipulation helpers.
+ * Each entry is allocated with GFP_ATOMIC; must be called under a spinlock.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int __hw_addr_list_snapshot(struct netdev_hw_addr_list *snap,
+ const struct netdev_hw_addr_list *list,
+ int addr_len)
+{
+ struct netdev_hw_addr *ha, *entry;
+
+ list_for_each_entry(ha, &list->list, list) {
+ entry = __hw_addr_create(ha->addr, addr_len, ha->type,
+ false, false);
+ if (!entry) {
+ __hw_addr_flush(snap);
+ return -ENOMEM;
+ }
+ entry->sync_cnt = ha->sync_cnt;
+ entry->refcount = ha->refcount;
+
+ list_add_tail(&entry->list, &snap->list);
+ __hw_addr_insert(snap, entry, addr_len);
+ snap->count++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(__hw_addr_list_snapshot);
+
+/**
+ * __hw_addr_list_reconcile - sync snapshot changes back and free snapshots
+ * @real_list: the real address list to update
+ * @work: the working snapshot (modified by driver via __hw_addr_sync_dev)
+ * @ref: the reference snapshot (untouched copy of original state)
+ * @addr_len: length of addresses
+ *
+ * Walks the reference snapshot and compares each entry against the work
+ * snapshot to compute sync_cnt deltas. Applies those deltas to @real_list.
+ * Frees both snapshots when done.
+ * Caller must hold netif_addr_lock_bh.
+ */
+void __hw_addr_list_reconcile(struct netdev_hw_addr_list *real_list,
+ struct netdev_hw_addr_list *work,
+ struct netdev_hw_addr_list *ref, int addr_len)
+{
+ struct netdev_hw_addr *ref_ha, *tmp, *work_ha, *real_ha;
+ int delta;
+
+ list_for_each_entry_safe(ref_ha, tmp, &ref->list, list) {
+ work_ha = __hw_addr_lookup(work, ref_ha->addr, addr_len,
+ ref_ha->type);
+ if (work_ha)
+ delta = work_ha->sync_cnt - ref_ha->sync_cnt;
+ else
+ delta = -1;
+
+ if (delta == 0)
+ continue;
+
+ real_ha = __hw_addr_lookup(real_list, ref_ha->addr, addr_len,
+ ref_ha->type);
+ if (!real_ha) {
+ /* The real entry was concurrently removed. If the
+ * driver synced this addr to hardware (delta > 0),
+ * re-insert it as a stale entry so the next work
+ * run unsyncs it from hardware.
+ */
+ if (delta > 0) {
+ rb_erase(&ref_ha->node, &ref->tree);
+ list_del(&ref_ha->list);
+ ref->count--;
+ ref_ha->sync_cnt = delta;
+ ref_ha->refcount = delta;
+ list_add_tail_rcu(&ref_ha->list,
+ &real_list->list);
+ __hw_addr_insert(real_list, ref_ha,
+ addr_len);
+ real_list->count++;
+ }
+ continue;
+ }
+
+ real_ha->sync_cnt += delta;
+ real_ha->refcount += delta;
+ if (!real_ha->refcount) {
+ rb_erase(&real_ha->node, &real_list->tree);
+ list_del_rcu(&real_ha->list);
+ kfree_rcu(real_ha, rcu_head);
+ real_list->count--;
+ }
+ }
+
+ __hw_addr_flush(work);
+ __hw_addr_flush(ref);
+}
+EXPORT_SYMBOL_IF_KUNIT(__hw_addr_list_reconcile);
+
/*
* Device addresses handling functions
*/
diff --git a/net/core/dev_addr_lists_test.c b/net/core/dev_addr_lists_test.c
index 8e1dba825e94..fba926d5ec0d 100644
--- a/net/core/dev_addr_lists_test.c
+++ b/net/core/dev_addr_lists_test.c
@@ -2,22 +2,31 @@
#include <kunit/test.h>
#include <linux/etherdevice.h>
+#include <linux/math64.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
static const struct net_device_ops dummy_netdev_ops = {
};
+#define ADDR_A 1
+#define ADDR_B 2
+#define ADDR_C 3
+
struct dev_addr_test_priv {
u32 addr_seen;
+ u32 addr_synced;
+ u32 addr_unsynced;
};
static int dev_addr_test_sync(struct net_device *netdev, const unsigned char *a)
{
struct dev_addr_test_priv *datp = netdev_priv(netdev);
- if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN))
+ if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN)) {
datp->addr_seen |= 1 << a[0];
+ datp->addr_synced |= 1 << a[0];
+ }
return 0;
}
@@ -26,11 +35,22 @@ static int dev_addr_test_unsync(struct net_device *netdev,
{
struct dev_addr_test_priv *datp = netdev_priv(netdev);
- if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN))
+ if (a[0] < 31 && !memchr_inv(a, a[0], ETH_ALEN)) {
datp->addr_seen &= ~(1 << a[0]);
+ datp->addr_unsynced |= 1 << a[0];
+ }
return 0;
}
+static void dev_addr_test_reset(struct net_device *netdev)
+{
+ struct dev_addr_test_priv *datp = netdev_priv(netdev);
+
+ datp->addr_seen = 0;
+ datp->addr_synced = 0;
+ datp->addr_unsynced = 0;
+}
+
static int dev_addr_test_init(struct kunit *test)
{
struct dev_addr_test_priv *datp;
@@ -225,6 +245,339 @@ static void dev_addr_test_add_excl(struct kunit *test)
rtnl_unlock();
}
+/* Snapshot test: basic sync with no concurrent modifications.
+ * Add one address, snapshot, driver syncs it, reconcile propagates
+ * sync_cnt delta back to real list.
+ */
+static void dev_addr_test_snapshot_sync(struct kunit *test)
+{
+ struct net_device *netdev = test->priv;
+ struct netdev_hw_addr_list snap, ref;
+ struct dev_addr_test_priv *datp;
+ struct netdev_hw_addr *ha;
+ u8 addr[ETH_ALEN];
+
+ datp = netdev_priv(netdev);
+
+ rtnl_lock();
+
+ memset(addr, ADDR_A, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+
+ /* Snapshot: ADDR_A has sync_cnt=0, refcount=1 (new) */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_init(&snap);
+ __hw_addr_init(&ref);
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN));
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN));
+ netif_addr_unlock_bh(netdev);
+
+ /* Driver syncs ADDR_A to hardware */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
+
+ /* Reconcile: delta=+1 applied to real entry */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN);
+ netif_addr_unlock_bh(netdev);
+
+ /* Real entry should now reflect the sync: sync_cnt=1, refcount=2 */
+ KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list);
+ KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN);
+ KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
+ KUNIT_EXPECT_EQ(test, 2, ha->refcount);
+
+ /* Second work run: already synced, nothing to do */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
+ KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+
+ rtnl_unlock();
+}
+
+/* Snapshot test: ADDR_A synced to hardware, then concurrently removed
+ * from the real list before reconcile runs. Reconcile re-inserts ADDR_A as
+ * a stale entry so the next work run unsyncs it from hardware.
+ */
+static void dev_addr_test_snapshot_remove_during_sync(struct kunit *test)
+{
+ struct net_device *netdev = test->priv;
+ struct netdev_hw_addr_list snap, ref;
+ struct dev_addr_test_priv *datp;
+ struct netdev_hw_addr *ha;
+ u8 addr[ETH_ALEN];
+
+ datp = netdev_priv(netdev);
+
+ rtnl_lock();
+
+ memset(addr, ADDR_A, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+
+ /* Snapshot: ADDR_A is new (sync_cnt=0, refcount=1) */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_init(&snap);
+ __hw_addr_init(&ref);
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN));
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN));
+ netif_addr_unlock_bh(netdev);
+
+ /* Driver syncs ADDR_A to hardware */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
+
+ /* Concurrent removal: user deletes ADDR_A while driver was working */
+ memset(addr, ADDR_A, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr));
+ KUNIT_EXPECT_EQ(test, 0, netdev->uc.count);
+
+ /* Reconcile: ADDR_A gone from real list but driver synced it,
+ * so it gets re-inserted as stale (sync_cnt=1, refcount=1).
+ */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN);
+ netif_addr_unlock_bh(netdev);
+
+ KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list);
+ KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN);
+ KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
+ KUNIT_EXPECT_EQ(test, 1, ha->refcount);
+
+ /* Second work run: stale entry gets unsynced from HW and removed */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_unsynced);
+ KUNIT_EXPECT_EQ(test, 0, netdev->uc.count);
+
+ rtnl_unlock();
+}
+
+/* Snapshot test: ADDR_A was stale (unsynced from hardware by driver),
+ * but concurrently re-added by the user. The re-add bumps refcount of
+ * the existing stale entry. Reconcile applies delta=-1, leaving ADDR_A
+ * as a fresh entry (sync_cnt=0, refcount=1) for the next work run.
+ */
+static void dev_addr_test_snapshot_readd_during_unsync(struct kunit *test)
+{
+ struct net_device *netdev = test->priv;
+ struct netdev_hw_addr_list snap, ref;
+ struct dev_addr_test_priv *datp;
+ struct netdev_hw_addr *ha;
+ u8 addr[ETH_ALEN];
+
+ datp = netdev_priv(netdev);
+
+ rtnl_lock();
+
+ memset(addr, ADDR_A, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+
+ /* Sync ADDR_A to hardware: sync_cnt=1, refcount=2 */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
+
+ /* User removes ADDR_A: refcount=1, sync_cnt=1 -> stale */
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr));
+
+ /* Snapshot: ADDR_A is stale (sync_cnt=1, refcount=1) */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_init(&snap);
+ __hw_addr_init(&ref);
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN));
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN));
+ netif_addr_unlock_bh(netdev);
+
+ /* Driver unsyncs stale ADDR_A from hardware */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_unsynced);
+
+ /* Concurrent: user re-adds ADDR_A. dev_uc_add finds the existing
+ * stale entry and bumps refcount from 1 -> 2. sync_cnt stays 1.
+ */
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+ KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+
+ /* Reconcile: ref sync_cnt=1 matches real sync_cnt=1, delta=-1
+ * applied. Result: sync_cnt=0, refcount=1 (fresh).
+ */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN);
+ netif_addr_unlock_bh(netdev);
+
+ /* Entry survives as fresh: needs re-sync to HW */
+ KUNIT_EXPECT_EQ(test, 1, netdev->uc.count);
+ ha = list_first_entry(&netdev->uc.list, struct netdev_hw_addr, list);
+ KUNIT_EXPECT_MEMEQ(test, ha->addr, addr, ETH_ALEN);
+ KUNIT_EXPECT_EQ(test, 0, ha->sync_cnt);
+ KUNIT_EXPECT_EQ(test, 1, ha->refcount);
+
+ /* Second work run: fresh entry gets synced to HW */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_A, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
+
+ rtnl_unlock();
+}
+
+/* Snapshot test: ADDR_A is new (synced by driver), and independent ADDR_B
+ * is concurrently removed from the real list. A's sync delta propagates
+ * normally; B's absence doesn't interfere.
+ */
+static void dev_addr_test_snapshot_add_and_remove(struct kunit *test)
+{
+ struct net_device *netdev = test->priv;
+ struct netdev_hw_addr_list snap, ref;
+ struct dev_addr_test_priv *datp;
+ struct netdev_hw_addr *ha;
+ u8 addr[ETH_ALEN];
+
+ datp = netdev_priv(netdev);
+
+ rtnl_lock();
+
+ /* Add ADDR_A and ADDR_B (will be synced then removed) */
+ memset(addr, ADDR_A, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+ memset(addr, ADDR_B, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+
+ /* Sync both to hardware: sync_cnt=1, refcount=2 */
+ __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+
+ /* Add ADDR_C (new, will be synced by snapshot) */
+ memset(addr, ADDR_C, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+
+ /* Snapshot: A,B synced (sync_cnt=1,refcount=2); C new (0,1) */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_init(&snap);
+ __hw_addr_init(&ref);
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&snap, &netdev->uc, ETH_ALEN));
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&ref, &netdev->uc, ETH_ALEN));
+ netif_addr_unlock_bh(netdev);
+
+ /* Driver syncs snapshot: ADDR_C is new -> synced; A,B already synced */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&snap, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_C, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_unsynced);
+
+ /* Concurrent: user removes addr B while driver was working */
+ memset(addr, ADDR_B, sizeof(addr));
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_del(netdev, addr));
+
+ /* Reconcile: ADDR_C's delta=+1 applied to real list.
+ * ADDR_B's delta=0 (unchanged in snapshot),
+ * so nothing to apply to ADDR_B.
+ */
+ netif_addr_lock_bh(netdev);
+ __hw_addr_list_reconcile(&netdev->uc, &snap, &ref, ETH_ALEN);
+ netif_addr_unlock_bh(netdev);
+
+ /* ADDR_A: unchanged (sync_cnt=1, refcount=2)
+ * ADDR_B: refcount went from 2->1 via dev_uc_del (still present, stale)
+ * ADDR_C: sync propagated (sync_cnt=1, refcount=2)
+ */
+ KUNIT_EXPECT_EQ(test, 3, netdev->uc.count);
+ netdev_hw_addr_list_for_each(ha, &netdev->uc) {
+ u8 id = ha->addr[0];
+
+ if (!memchr_inv(ha->addr, id, ETH_ALEN)) {
+ if (id == ADDR_A) {
+ KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
+ KUNIT_EXPECT_EQ(test, 2, ha->refcount);
+ } else if (id == ADDR_B) {
+ /* B: still present but now stale */
+ KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
+ KUNIT_EXPECT_EQ(test, 1, ha->refcount);
+ } else if (id == ADDR_C) {
+ KUNIT_EXPECT_EQ(test, 1, ha->sync_cnt);
+ KUNIT_EXPECT_EQ(test, 2, ha->refcount);
+ }
+ }
+ }
+
+ /* Second work run: ADDR_B is stale, gets unsynced and removed */
+ dev_addr_test_reset(netdev);
+ __hw_addr_sync_dev(&netdev->uc, netdev, dev_addr_test_sync,
+ dev_addr_test_unsync);
+ KUNIT_EXPECT_EQ(test, 0, datp->addr_synced);
+ KUNIT_EXPECT_EQ(test, 1 << ADDR_B, datp->addr_unsynced);
+ KUNIT_EXPECT_EQ(test, 2, netdev->uc.count);
+
+ rtnl_unlock();
+}
+
+static void dev_addr_test_snapshot_benchmark(struct kunit *test)
+{
+ struct net_device *netdev = test->priv;
+ struct netdev_hw_addr_list snap;
+ u8 addr[ETH_ALEN];
+ s64 duration = 0;
+ ktime_t start;
+ int i, iter;
+
+ rtnl_lock();
+
+ for (i = 0; i < 1024; i++) {
+ memset(addr, 0, sizeof(addr));
+ addr[0] = (i >> 8) & 0xff;
+ addr[1] = i & 0xff;
+ KUNIT_EXPECT_EQ(test, 0, dev_uc_add(netdev, addr));
+ }
+
+ for (iter = 0; iter < 1000; iter++) {
+ netif_addr_lock_bh(netdev);
+ __hw_addr_init(&snap);
+
+ start = ktime_get();
+ KUNIT_EXPECT_EQ(test, 0,
+ __hw_addr_list_snapshot(&snap, &netdev->uc,
+ ETH_ALEN));
+ duration += ktime_to_ns(ktime_sub(ktime_get(), start));
+
+ netif_addr_unlock_bh(netdev);
+ __hw_addr_flush(&snap);
+ }
+
+ kunit_info(test,
+ "1024 addrs x 1000 snapshots: %lld ns total, %lld ns/iter",
+ duration, div_s64(duration, 1000));
+
+ rtnl_unlock();
+}
+
static struct kunit_case dev_addr_test_cases[] = {
KUNIT_CASE(dev_addr_test_basic),
KUNIT_CASE(dev_addr_test_sync_one),
@@ -232,6 +585,11 @@ static struct kunit_case dev_addr_test_cases[] = {
KUNIT_CASE(dev_addr_test_del_main),
KUNIT_CASE(dev_addr_test_add_set),
KUNIT_CASE(dev_addr_test_add_excl),
+ KUNIT_CASE(dev_addr_test_snapshot_sync),
+ KUNIT_CASE(dev_addr_test_snapshot_remove_during_sync),
+ KUNIT_CASE(dev_addr_test_snapshot_readd_during_unsync),
+ KUNIT_CASE(dev_addr_test_snapshot_add_and_remove),
+ KUNIT_CASE_SLOW(dev_addr_test_snapshot_benchmark),
{}
};
@@ -243,5 +601,6 @@ static struct kunit_suite dev_addr_test_suite = {
};
kunit_test_suite(dev_addr_test_suite);
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
MODULE_DESCRIPTION("KUnit tests for struct netdev_hw_addr_list");
MODULE_LICENSE("GPL");
--
2.52.0
^ permalink raw reply related
* [PATCH net v8 02/15] net: introduce ndo_set_rx_mode_async and netdev_rx_mode_work
From: Stanislav Fomichev @ 2026-04-16 18:56 UTC (permalink / raw)
To: netdev; +Cc: davem, edumazet, kuba, pabeni
In-Reply-To: <20260416185712.2155425-1-sdf@fomichev.me>
Add ndo_set_rx_mode_async callback that drivers can implement instead
of the legacy ndo_set_rx_mode. The legacy callback runs under the
netif_addr_lock spinlock with BHs disabled, preventing drivers from
sleeping. The async variant runs from a work queue with rtnl_lock and
netdev_lock_ops held, in fully sleepable context.
When __dev_set_rx_mode() sees ndo_set_rx_mode_async, it schedules
netdev_rx_mode_work instead of calling the driver inline. The work
function takes two snapshots of each address list (uc/mc) under
the addr_lock, then drops the lock and calls the driver with the
work copies. After the driver returns, it reconciles the snapshots
back to the real lists under the lock.
Add netif_rx_mode_sync() to opportunistically execute the pending
workqueue update inline, so that rx mode changes are committed
before returning to userspace:
- dev_change_flags (SIOCSIFFLAGS / RTM_NEWLINK)
- dev_set_promiscuity
- dev_set_allmulti
- dev_ifsioc SIOCADDMULTI / SIOCDELMULTI
- do_setlink (RTM_SETLINK)
Note that some deep hierarchies still do skip the lower updates via:
- dev_uc_sync
- dev_mc_sync
If we do end up hitting user-visible issues, we can add more calls to
netif_rx_mode_sync in specific places. But hopefully we should not,
the actual user-visible lists are still synced, it's that just HW state
that might be lagging.
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
---
Documentation/networking/netdevices.rst | 9 +
include/linux/netdevice.h | 18 ++
net/core/dev.c | 43 +----
net/core/dev.h | 3 +
net/core/dev_addr_lists.c | 209 ++++++++++++++++++++++++
net/core/dev_api.c | 3 +
net/core/dev_ioctl.c | 6 +-
net/core/rtnetlink.c | 1 +
8 files changed, 249 insertions(+), 43 deletions(-)
diff --git a/Documentation/networking/netdevices.rst b/Documentation/networking/netdevices.rst
index 83e28b96884f..e89b12d4f3a7 100644
--- a/Documentation/networking/netdevices.rst
+++ b/Documentation/networking/netdevices.rst
@@ -289,6 +289,15 @@ struct net_device synchronization rules
ndo_set_rx_mode:
Synchronization: netif_addr_lock spinlock.
Context: BHs disabled
+ Notes: Deprecated in favor of ndo_set_rx_mode_async which runs
+ in process context.
+
+ndo_set_rx_mode_async:
+ Synchronization: rtnl_lock() semaphore. In addition, netdev instance
+ lock if the driver implements queue management or shaper API.
+ Context: process (from a work queue)
+ Notes: Async version of ndo_set_rx_mode which runs in process
+ context. Receives snapshots of the unicast and multicast address lists.
ndo_setup_tc:
``TC_SETUP_BLOCK`` and ``TC_SETUP_FT`` are running under NFT locks
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index a84c55488b8c..6ed97f4c3bc6 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1119,6 +1119,16 @@ struct netdev_net_notifier {
* This function is called device changes address list filtering.
* If driver handles unicast address filtering, it should set
* IFF_UNICAST_FLT in its priv_flags.
+ * Cannot sleep, called with netif_addr_lock_bh held.
+ * Deprecated in favor of ndo_set_rx_mode_async.
+ *
+ * void (*ndo_set_rx_mode_async)(struct net_device *dev,
+ * struct netdev_hw_addr_list *uc,
+ * struct netdev_hw_addr_list *mc);
+ * Async version of ndo_set_rx_mode which runs in process context
+ * with rtnl_lock and netdev_lock_ops(dev) held. The uc/mc parameters
+ * are snapshots of the address lists - iterate with
+ * netdev_hw_addr_list_for_each(ha, uc).
*
* int (*ndo_set_mac_address)(struct net_device *dev, void *addr);
* This function is called when the Media Access Control address
@@ -1439,6 +1449,10 @@ struct net_device_ops {
void (*ndo_change_rx_flags)(struct net_device *dev,
int flags);
void (*ndo_set_rx_mode)(struct net_device *dev);
+ void (*ndo_set_rx_mode_async)(
+ struct net_device *dev,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc);
int (*ndo_set_mac_address)(struct net_device *dev,
void *addr);
int (*ndo_validate_addr)(struct net_device *dev);
@@ -1903,6 +1917,8 @@ enum netdev_reg_state {
* has been enabled due to the need to listen to
* additional unicast addresses in a device that
* does not implement ndo_set_rx_mode()
+ * @rx_mode_node: List entry for rx_mode work processing
+ * @rx_mode_tracker: Refcount tracker for rx_mode work
* @uc: unicast mac addresses
* @mc: multicast mac addresses
* @dev_addrs: list of device hw addresses
@@ -2294,6 +2310,8 @@ struct net_device {
unsigned int promiscuity;
unsigned int allmulti;
bool uc_promisc;
+ struct list_head rx_mode_node;
+ netdevice_tracker rx_mode_tracker;
#ifdef CONFIG_LOCKDEP
unsigned char nested_level;
#endif
diff --git a/net/core/dev.c b/net/core/dev.c
index e59f6025067c..b37061238a25 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -9593,7 +9593,7 @@ static void dev_change_rx_flags(struct net_device *dev, int flags)
ops->ndo_change_rx_flags(dev, flags);
}
-static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
+int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify)
{
unsigned int old_flags = dev->flags;
unsigned int promiscuity, flags;
@@ -9697,46 +9697,6 @@ int netif_set_allmulti(struct net_device *dev, int inc, bool notify)
return 0;
}
-/*
- * Upload unicast and multicast address lists to device and
- * configure RX filtering. When the device doesn't support unicast
- * filtering it is put in promiscuous mode while unicast addresses
- * are present.
- */
-void __dev_set_rx_mode(struct net_device *dev)
-{
- const struct net_device_ops *ops = dev->netdev_ops;
-
- /* dev_open will call this function so the list will stay sane. */
- if (!(dev->flags&IFF_UP))
- return;
-
- if (!netif_device_present(dev))
- return;
-
- if (!(dev->priv_flags & IFF_UNICAST_FLT)) {
- /* Unicast addresses changes may only happen under the rtnl,
- * therefore calling __dev_set_promiscuity here is safe.
- */
- if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
- __dev_set_promiscuity(dev, 1, false);
- dev->uc_promisc = true;
- } else if (netdev_uc_empty(dev) && dev->uc_promisc) {
- __dev_set_promiscuity(dev, -1, false);
- dev->uc_promisc = false;
- }
- }
-
- if (ops->ndo_set_rx_mode)
- ops->ndo_set_rx_mode(dev);
-}
-
-void dev_set_rx_mode(struct net_device *dev)
-{
- netif_addr_lock_bh(dev);
- __dev_set_rx_mode(dev);
- netif_addr_unlock_bh(dev);
-}
/**
* netif_get_flags() - get flags reported to userspace
@@ -12127,6 +12087,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
#endif
mutex_init(&dev->lock);
+ INIT_LIST_HEAD(&dev->rx_mode_node);
dev->priv_flags = IFF_XMIT_DST_RELEASE | IFF_XMIT_DST_RELEASE_PERM;
setup(dev);
diff --git a/net/core/dev.h b/net/core/dev.h
index 585b6d7e88df..0cf24b8f5008 100644
--- a/net/core/dev.h
+++ b/net/core/dev.h
@@ -165,6 +165,9 @@ int netif_change_carrier(struct net_device *dev, bool new_carrier);
int dev_change_carrier(struct net_device *dev, bool new_carrier);
void __dev_set_rx_mode(struct net_device *dev);
+int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify);
+bool netif_rx_mode_clean(struct net_device *dev);
+void netif_rx_mode_sync(struct net_device *dev);
void __dev_notify_flags(struct net_device *dev, unsigned int old_flags,
unsigned int gchanges, u32 portid,
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index bb4851bc55ce..056bca6fce12 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -11,10 +11,18 @@
#include <linux/rtnetlink.h>
#include <linux/export.h>
#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
#include <kunit/visibility.h>
#include "dev.h"
+static void netdev_rx_mode_work(struct work_struct *work);
+
+static LIST_HEAD(rx_mode_list);
+static DEFINE_SPINLOCK(rx_mode_lock);
+static DECLARE_WORK(rx_mode_work, netdev_rx_mode_work);
+
/*
* General list handling functions
*/
@@ -1156,3 +1164,204 @@ void dev_mc_init(struct net_device *dev)
__hw_addr_init(&dev->mc);
}
EXPORT_SYMBOL(dev_mc_init);
+
+static int netif_addr_lists_snapshot(struct net_device *dev,
+ struct netdev_hw_addr_list *uc_snap,
+ struct netdev_hw_addr_list *mc_snap,
+ struct netdev_hw_addr_list *uc_ref,
+ struct netdev_hw_addr_list *mc_ref)
+{
+ int err;
+
+ err = __hw_addr_list_snapshot(uc_snap, &dev->uc, dev->addr_len);
+ if (!err)
+ err = __hw_addr_list_snapshot(uc_ref, &dev->uc, dev->addr_len);
+ if (!err)
+ err = __hw_addr_list_snapshot(mc_snap, &dev->mc,
+ dev->addr_len);
+ if (!err)
+ err = __hw_addr_list_snapshot(mc_ref, &dev->mc, dev->addr_len);
+
+ if (err) {
+ __hw_addr_flush(uc_snap);
+ __hw_addr_flush(uc_ref);
+ __hw_addr_flush(mc_snap);
+ }
+
+ return err;
+}
+
+static void netif_addr_lists_reconcile(struct net_device *dev,
+ struct netdev_hw_addr_list *uc_snap,
+ struct netdev_hw_addr_list *mc_snap,
+ struct netdev_hw_addr_list *uc_ref,
+ struct netdev_hw_addr_list *mc_ref)
+{
+ __hw_addr_list_reconcile(&dev->uc, uc_snap, uc_ref, dev->addr_len);
+ __hw_addr_list_reconcile(&dev->mc, mc_snap, mc_ref, dev->addr_len);
+}
+
+static void netif_rx_mode_run(struct net_device *dev)
+{
+ struct netdev_hw_addr_list uc_snap, mc_snap, uc_ref, mc_ref;
+ const struct net_device_ops *ops = dev->netdev_ops;
+ int err;
+
+ might_sleep();
+ netdev_ops_assert_locked(dev);
+
+ __hw_addr_init(&uc_snap);
+ __hw_addr_init(&mc_snap);
+ __hw_addr_init(&uc_ref);
+ __hw_addr_init(&mc_ref);
+
+ if (!(dev->flags & IFF_UP) || !netif_device_present(dev))
+ return;
+
+ netif_addr_lock_bh(dev);
+ err = netif_addr_lists_snapshot(dev, &uc_snap, &mc_snap,
+ &uc_ref, &mc_ref);
+ if (err) {
+ netdev_WARN(dev, "failed to sync uc/mc addresses\n");
+ netif_addr_unlock_bh(dev);
+ return;
+ }
+ netif_addr_unlock_bh(dev);
+
+ ops->ndo_set_rx_mode_async(dev, &uc_snap, &mc_snap);
+
+ netif_addr_lock_bh(dev);
+ netif_addr_lists_reconcile(dev, &uc_snap, &mc_snap,
+ &uc_ref, &mc_ref);
+ netif_addr_unlock_bh(dev);
+}
+
+static void netdev_rx_mode_work(struct work_struct *work)
+{
+ struct net_device *dev;
+
+ rtnl_lock();
+
+ while (true) {
+ spin_lock_bh(&rx_mode_lock);
+ if (list_empty(&rx_mode_list)) {
+ spin_unlock_bh(&rx_mode_lock);
+ break;
+ }
+ dev = list_first_entry(&rx_mode_list, struct net_device,
+ rx_mode_node);
+ list_del_init(&dev->rx_mode_node);
+ /* We must free netdev tracker under
+ * the spinlock protection.
+ */
+ netdev_tracker_free(dev, &dev->rx_mode_tracker);
+ spin_unlock_bh(&rx_mode_lock);
+
+ netdev_lock_ops(dev);
+ netif_rx_mode_run(dev);
+ netdev_unlock_ops(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called above. Must be after netdev_unlock_ops() to prevent
+ * netdev_run_todo() from freeing the device while still in use.
+ */
+ __dev_put(dev);
+ }
+
+ rtnl_unlock();
+}
+
+static void netif_rx_mode_queue(struct net_device *dev)
+{
+ spin_lock_bh(&rx_mode_lock);
+ if (list_empty(&dev->rx_mode_node)) {
+ list_add_tail(&dev->rx_mode_node, &rx_mode_list);
+ netdev_hold(dev, &dev->rx_mode_tracker, GFP_ATOMIC);
+ }
+ spin_unlock_bh(&rx_mode_lock);
+ schedule_work(&rx_mode_work);
+}
+
+/**
+ * __dev_set_rx_mode() - upload unicast and multicast address lists to device
+ * and configure RX filtering.
+ * @dev: device
+ *
+ * When the device doesn't support unicast filtering it is put in promiscuous
+ * mode while unicast addresses are present.
+ */
+void __dev_set_rx_mode(struct net_device *dev)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ /* dev_open will call this function so the list will stay sane. */
+ if (!(dev->flags & IFF_UP))
+ return;
+
+ if (!netif_device_present(dev))
+ return;
+
+ if (ops->ndo_set_rx_mode_async) {
+ netif_rx_mode_queue(dev);
+ return;
+ }
+
+ if (!(dev->priv_flags & IFF_UNICAST_FLT)) {
+ if (!netdev_uc_empty(dev) && !dev->uc_promisc) {
+ __dev_set_promiscuity(dev, 1, false);
+ dev->uc_promisc = true;
+ } else if (netdev_uc_empty(dev) && dev->uc_promisc) {
+ __dev_set_promiscuity(dev, -1, false);
+ dev->uc_promisc = false;
+ }
+ }
+
+ if (ops->ndo_set_rx_mode)
+ ops->ndo_set_rx_mode(dev);
+}
+
+void dev_set_rx_mode(struct net_device *dev)
+{
+ netif_addr_lock_bh(dev);
+ __dev_set_rx_mode(dev);
+ netif_addr_unlock_bh(dev);
+}
+
+bool netif_rx_mode_clean(struct net_device *dev)
+{
+ bool clean = false;
+
+ spin_lock_bh(&rx_mode_lock);
+ if (!list_empty(&dev->rx_mode_node)) {
+ list_del_init(&dev->rx_mode_node);
+ clean = true;
+ /* We must release netdev tracker under
+ * the spinlock protection.
+ */
+ netdev_tracker_free(dev, &dev->rx_mode_tracker);
+ }
+ spin_unlock_bh(&rx_mode_lock);
+
+ return clean;
+}
+
+/**
+ * netif_rx_mode_sync() - sync rx mode inline
+ * @dev: network device
+ *
+ * Drivers implementing ndo_set_rx_mode_async() have their rx mode callback
+ * executed from a workqueue. This allows the callback to sleep, but means
+ * the hardware update is deferred and may not be visible to userspace
+ * by the time the initiating syscall returns. netif_rx_mode_sync() steals
+ * workqueue update and executes it inline. This preserves the atomicity of
+ * operations to the userspace.
+ */
+void netif_rx_mode_sync(struct net_device *dev)
+{
+ if (netif_rx_mode_clean(dev)) {
+ netif_rx_mode_run(dev);
+ /* Use __dev_put() because netdev_tracker_free() was already
+ * called inside netif_rx_mode_clean().
+ */
+ __dev_put(dev);
+ }
+}
diff --git a/net/core/dev_api.c b/net/core/dev_api.c
index f28852078aa6..437947dd08ed 100644
--- a/net/core/dev_api.c
+++ b/net/core/dev_api.c
@@ -66,6 +66,7 @@ int dev_change_flags(struct net_device *dev, unsigned int flags,
netdev_lock_ops(dev);
ret = netif_change_flags(dev, flags, extack);
+ netif_rx_mode_sync(dev);
netdev_unlock_ops(dev);
return ret;
@@ -285,6 +286,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc)
netdev_lock_ops(dev);
ret = netif_set_promiscuity(dev, inc);
+ netif_rx_mode_sync(dev);
netdev_unlock_ops(dev);
return ret;
@@ -311,6 +313,7 @@ int dev_set_allmulti(struct net_device *dev, int inc)
netdev_lock_ops(dev);
ret = netif_set_allmulti(dev, inc, true);
+ netif_rx_mode_sync(dev);
netdev_unlock_ops(dev);
return ret;
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 7a8966544c9d..f3979b276090 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -586,24 +586,26 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, void __user *data,
return err;
case SIOCADDMULTI:
- if (!ops->ndo_set_rx_mode ||
+ if ((!ops->ndo_set_rx_mode && !ops->ndo_set_rx_mode_async) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
netdev_lock_ops(dev);
err = dev_mc_add_global(dev, ifr->ifr_hwaddr.sa_data);
+ netif_rx_mode_sync(dev);
netdev_unlock_ops(dev);
return err;
case SIOCDELMULTI:
- if (!ops->ndo_set_rx_mode ||
+ if ((!ops->ndo_set_rx_mode && !ops->ndo_set_rx_mode_async) ||
ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
return -EINVAL;
if (!netif_device_present(dev))
return -ENODEV;
netdev_lock_ops(dev);
err = dev_mc_del_global(dev, ifr->ifr_hwaddr.sa_data);
+ netif_rx_mode_sync(dev);
netdev_unlock_ops(dev);
return err;
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 69daba3ddaf0..b613bb6e07df 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -3431,6 +3431,7 @@ static int do_setlink(const struct sk_buff *skb, struct net_device *dev,
dev->name);
}
+ netif_rx_mode_sync(dev);
netdev_unlock_ops(dev);
return err;
--
2.52.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox