summaryrefslogtreecommitdiffstats
path: root/net/lapb/lapb_subr.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /net/lapb/lapb_subr.c
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'net/lapb/lapb_subr.c')
-rw-r--r--net/lapb/lapb_subr.c299
1 files changed, 299 insertions, 0 deletions
diff --git a/net/lapb/lapb_subr.c b/net/lapb/lapb_subr.c
new file mode 100644
index 000000000..592a22d86
--- /dev/null
+++ b/net/lapb/lapb_subr.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LAPB release 002
+ *
+ * This code REQUIRES 2.1.15 or higher/ NET3.038
+ *
+ * History
+ * LAPB 001 Jonathan Naylor Started Coding
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/sockios.h>
+#include <linux/net.h>
+#include <linux/inet.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <net/sock.h>
+#include <linux/uaccess.h>
+#include <linux/fcntl.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <net/lapb.h>
+
+/*
+ * This routine purges all the queues of frames.
+ */
+void lapb_clear_queues(struct lapb_cb *lapb)
+{
+ skb_queue_purge(&lapb->write_queue);
+ skb_queue_purge(&lapb->ack_queue);
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+void lapb_frames_acked(struct lapb_cb *lapb, unsigned short nr)
+{
+ struct sk_buff *skb;
+ int modulus;
+
+ modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+ /*
+ * Remove all the ack-ed frames from the ack queue.
+ */
+ if (lapb->va != nr)
+ while (skb_peek(&lapb->ack_queue) && lapb->va != nr) {
+ skb = skb_dequeue(&lapb->ack_queue);
+ kfree_skb(skb);
+ lapb->va = (lapb->va + 1) % modulus;
+ }
+}
+
+void lapb_requeue_frames(struct lapb_cb *lapb)
+{
+ struct sk_buff *skb, *skb_prev = NULL;
+
+ /*
+ * Requeue all the un-ack-ed frames on the output queue to be picked
+ * up by lapb_kick called from the timer. This arrangement handles the
+ * possibility of an empty output queue.
+ */
+ while ((skb = skb_dequeue(&lapb->ack_queue)) != NULL) {
+ if (!skb_prev)
+ skb_queue_head(&lapb->write_queue, skb);
+ else
+ skb_append(skb_prev, skb, &lapb->write_queue);
+ skb_prev = skb;
+ }
+}
+
+/*
+ * Validate that the value of nr is between va and vs. Return true or
+ * false for testing.
+ */
+int lapb_validate_nr(struct lapb_cb *lapb, unsigned short nr)
+{
+ unsigned short vc = lapb->va;
+ int modulus;
+
+ modulus = (lapb->mode & LAPB_EXTENDED) ? LAPB_EMODULUS : LAPB_SMODULUS;
+
+ while (vc != lapb->vs) {
+ if (nr == vc)
+ return 1;
+ vc = (vc + 1) % modulus;
+ }
+
+ return nr == lapb->vs;
+}
+
+/*
+ * This routine is the centralised routine for parsing the control
+ * information for the different frame formats.
+ */
+int lapb_decode(struct lapb_cb *lapb, struct sk_buff *skb,
+ struct lapb_frame *frame)
+{
+ frame->type = LAPB_ILLEGAL;
+
+ lapb_dbg(2, "(%p) S%d RX %3ph\n", lapb->dev, lapb->state, skb->data);
+
+ /* We always need to look at 2 bytes, sometimes we need
+ * to look at 3 and those cases are handled below.
+ */
+ if (!pskb_may_pull(skb, 2))
+ return -1;
+
+ if (lapb->mode & LAPB_MLP) {
+ if (lapb->mode & LAPB_DCE) {
+ if (skb->data[0] == LAPB_ADDR_D)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_C)
+ frame->cr = LAPB_RESPONSE;
+ } else {
+ if (skb->data[0] == LAPB_ADDR_C)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_D)
+ frame->cr = LAPB_RESPONSE;
+ }
+ } else {
+ if (lapb->mode & LAPB_DCE) {
+ if (skb->data[0] == LAPB_ADDR_B)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_A)
+ frame->cr = LAPB_RESPONSE;
+ } else {
+ if (skb->data[0] == LAPB_ADDR_A)
+ frame->cr = LAPB_COMMAND;
+ if (skb->data[0] == LAPB_ADDR_B)
+ frame->cr = LAPB_RESPONSE;
+ }
+ }
+
+ skb_pull(skb, 1);
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ if (!(skb->data[0] & LAPB_S)) {
+ if (!pskb_may_pull(skb, 2))
+ return -1;
+ /*
+ * I frame - carries NR/NS/PF
+ */
+ frame->type = LAPB_I;
+ frame->ns = (skb->data[0] >> 1) & 0x7F;
+ frame->nr = (skb->data[1] >> 1) & 0x7F;
+ frame->pf = skb->data[1] & LAPB_EPF;
+ frame->control[0] = skb->data[0];
+ frame->control[1] = skb->data[1];
+ skb_pull(skb, 2);
+ } else if ((skb->data[0] & LAPB_U) == 1) {
+ if (!pskb_may_pull(skb, 2))
+ return -1;
+ /*
+ * S frame - take out PF/NR
+ */
+ frame->type = skb->data[0] & 0x0F;
+ frame->nr = (skb->data[1] >> 1) & 0x7F;
+ frame->pf = skb->data[1] & LAPB_EPF;
+ frame->control[0] = skb->data[0];
+ frame->control[1] = skb->data[1];
+ skb_pull(skb, 2);
+ } else if ((skb->data[0] & LAPB_U) == 3) {
+ /*
+ * U frame - take out PF
+ */
+ frame->type = skb->data[0] & ~LAPB_SPF;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ frame->control[0] = skb->data[0];
+ frame->control[1] = 0x00;
+ skb_pull(skb, 1);
+ }
+ } else {
+ if (!(skb->data[0] & LAPB_S)) {
+ /*
+ * I frame - carries NR/NS/PF
+ */
+ frame->type = LAPB_I;
+ frame->ns = (skb->data[0] >> 1) & 0x07;
+ frame->nr = (skb->data[0] >> 5) & 0x07;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ } else if ((skb->data[0] & LAPB_U) == 1) {
+ /*
+ * S frame - take out PF/NR
+ */
+ frame->type = skb->data[0] & 0x0F;
+ frame->nr = (skb->data[0] >> 5) & 0x07;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ } else if ((skb->data[0] & LAPB_U) == 3) {
+ /*
+ * U frame - take out PF
+ */
+ frame->type = skb->data[0] & ~LAPB_SPF;
+ frame->pf = skb->data[0] & LAPB_SPF;
+ }
+
+ frame->control[0] = skb->data[0];
+
+ skb_pull(skb, 1);
+ }
+
+ return 0;
+}
+
+/*
+ * This routine is called when the HDLC layer internally generates a
+ * command or response for the remote machine ( eg. RR, UA etc. ).
+ * Only supervisory or unnumbered frames are processed, FRMRs are handled
+ * by lapb_transmit_frmr below.
+ */
+void lapb_send_control(struct lapb_cb *lapb, int frametype,
+ int poll_bit, int type)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+
+ if ((skb = alloc_skb(LAPB_HEADER_LEN + 3, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, LAPB_HEADER_LEN + 1);
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ if ((frametype & LAPB_U) == LAPB_U) {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= poll_bit ? LAPB_SPF : 0;
+ } else {
+ dptr = skb_put(skb, 2);
+ dptr[0] = frametype;
+ dptr[1] = (lapb->vr << 1);
+ dptr[1] |= poll_bit ? LAPB_EPF : 0;
+ }
+ } else {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= poll_bit ? LAPB_SPF : 0;
+ if ((frametype & LAPB_U) == LAPB_S) /* S frames carry NR */
+ *dptr |= (lapb->vr << 5);
+ }
+
+ lapb_transmit_buffer(lapb, skb, type);
+}
+
+/*
+ * This routine generates FRMRs based on information previously stored in
+ * the LAPB control block.
+ */
+void lapb_transmit_frmr(struct lapb_cb *lapb)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+
+ if ((skb = alloc_skb(LAPB_HEADER_LEN + 7, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, LAPB_HEADER_LEN + 1);
+
+ if (lapb->mode & LAPB_EXTENDED) {
+ dptr = skb_put(skb, 6);
+ *dptr++ = LAPB_FRMR;
+ *dptr++ = lapb->frmr_data.control[0];
+ *dptr++ = lapb->frmr_data.control[1];
+ *dptr++ = (lapb->vs << 1) & 0xFE;
+ *dptr = (lapb->vr << 1) & 0xFE;
+ if (lapb->frmr_data.cr == LAPB_RESPONSE)
+ *dptr |= 0x01;
+ dptr++;
+ *dptr++ = lapb->frmr_type;
+
+ lapb_dbg(1, "(%p) S%d TX FRMR %5ph\n",
+ lapb->dev, lapb->state,
+ &skb->data[1]);
+ } else {
+ dptr = skb_put(skb, 4);
+ *dptr++ = LAPB_FRMR;
+ *dptr++ = lapb->frmr_data.control[0];
+ *dptr = (lapb->vs << 1) & 0x0E;
+ *dptr |= (lapb->vr << 5) & 0xE0;
+ if (lapb->frmr_data.cr == LAPB_RESPONSE)
+ *dptr |= 0x10;
+ dptr++;
+ *dptr++ = lapb->frmr_type;
+
+ lapb_dbg(1, "(%p) S%d TX FRMR %3ph\n",
+ lapb->dev, lapb->state, &skb->data[1]);
+ }
+
+ lapb_transmit_buffer(lapb, skb, LAPB_RESPONSE);
+}