1e46bd709SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2871a2c16STomasz Grobelny /*
3871a2c16STomasz Grobelny * net/dccp/qpolicy.c
4871a2c16STomasz Grobelny *
5871a2c16STomasz Grobelny * Policy-based packet dequeueing interface for DCCP.
6871a2c16STomasz Grobelny *
7871a2c16STomasz Grobelny * Copyright (c) 2008 Tomasz Grobelny <tomasz@grobelny.oswiecenia.net>
8871a2c16STomasz Grobelny */
9871a2c16STomasz Grobelny #include "dccp.h"
10871a2c16STomasz Grobelny
11871a2c16STomasz Grobelny /*
12871a2c16STomasz Grobelny * Simple Dequeueing Policy:
13871a2c16STomasz Grobelny * If tx_qlen is different from 0, enqueue up to tx_qlen elements.
14871a2c16STomasz Grobelny */
qpolicy_simple_push(struct sock * sk,struct sk_buff * skb)15871a2c16STomasz Grobelny static void qpolicy_simple_push(struct sock *sk, struct sk_buff *skb)
16871a2c16STomasz Grobelny {
17871a2c16STomasz Grobelny skb_queue_tail(&sk->sk_write_queue, skb);
18871a2c16STomasz Grobelny }
19871a2c16STomasz Grobelny
qpolicy_simple_full(struct sock * sk)20871a2c16STomasz Grobelny static bool qpolicy_simple_full(struct sock *sk)
21871a2c16STomasz Grobelny {
22871a2c16STomasz Grobelny return dccp_sk(sk)->dccps_tx_qlen &&
23871a2c16STomasz Grobelny sk->sk_write_queue.qlen >= dccp_sk(sk)->dccps_tx_qlen;
24871a2c16STomasz Grobelny }
25871a2c16STomasz Grobelny
qpolicy_simple_top(struct sock * sk)26871a2c16STomasz Grobelny static struct sk_buff *qpolicy_simple_top(struct sock *sk)
27871a2c16STomasz Grobelny {
28871a2c16STomasz Grobelny return skb_peek(&sk->sk_write_queue);
29871a2c16STomasz Grobelny }
30871a2c16STomasz Grobelny
31871a2c16STomasz Grobelny /*
32871a2c16STomasz Grobelny * Priority-based Dequeueing Policy:
33871a2c16STomasz Grobelny * If tx_qlen is different from 0 and the queue has reached its upper bound
34871a2c16STomasz Grobelny * of tx_qlen elements, replace older packets lowest-priority-first.
35871a2c16STomasz Grobelny */
qpolicy_prio_best_skb(struct sock * sk)36871a2c16STomasz Grobelny static struct sk_buff *qpolicy_prio_best_skb(struct sock *sk)
37871a2c16STomasz Grobelny {
38871a2c16STomasz Grobelny struct sk_buff *skb, *best = NULL;
39871a2c16STomasz Grobelny
40871a2c16STomasz Grobelny skb_queue_walk(&sk->sk_write_queue, skb)
41871a2c16STomasz Grobelny if (best == NULL || skb->priority > best->priority)
42871a2c16STomasz Grobelny best = skb;
43871a2c16STomasz Grobelny return best;
44871a2c16STomasz Grobelny }
45871a2c16STomasz Grobelny
qpolicy_prio_worst_skb(struct sock * sk)46871a2c16STomasz Grobelny static struct sk_buff *qpolicy_prio_worst_skb(struct sock *sk)
47871a2c16STomasz Grobelny {
48871a2c16STomasz Grobelny struct sk_buff *skb, *worst = NULL;
49871a2c16STomasz Grobelny
50871a2c16STomasz Grobelny skb_queue_walk(&sk->sk_write_queue, skb)
51871a2c16STomasz Grobelny if (worst == NULL || skb->priority < worst->priority)
52871a2c16STomasz Grobelny worst = skb;
53871a2c16STomasz Grobelny return worst;
54871a2c16STomasz Grobelny }
55871a2c16STomasz Grobelny
qpolicy_prio_full(struct sock * sk)56871a2c16STomasz Grobelny static bool qpolicy_prio_full(struct sock *sk)
57871a2c16STomasz Grobelny {
58871a2c16STomasz Grobelny if (qpolicy_simple_full(sk))
59871a2c16STomasz Grobelny dccp_qpolicy_drop(sk, qpolicy_prio_worst_skb(sk));
60871a2c16STomasz Grobelny return false;
61871a2c16STomasz Grobelny }
62871a2c16STomasz Grobelny
63871a2c16STomasz Grobelny /**
64871a2c16STomasz Grobelny * struct dccp_qpolicy_operations - TX Packet Dequeueing Interface
65871a2c16STomasz Grobelny * @push: add a new @skb to the write queue
66871a2c16STomasz Grobelny * @full: indicates that no more packets will be admitted
67871a2c16STomasz Grobelny * @top: peeks at whatever the queueing policy defines as its `top'
68*0b609b55SAndrew Lunn * @params: parameter passed to policy operation
69871a2c16STomasz Grobelny */
70*0b609b55SAndrew Lunn struct dccp_qpolicy_operations {
71871a2c16STomasz Grobelny void (*push) (struct sock *sk, struct sk_buff *skb);
72871a2c16STomasz Grobelny bool (*full) (struct sock *sk);
73871a2c16STomasz Grobelny struct sk_buff* (*top) (struct sock *sk);
7404910265STomasz Grobelny __be32 params;
75*0b609b55SAndrew Lunn };
76871a2c16STomasz Grobelny
77*0b609b55SAndrew Lunn static struct dccp_qpolicy_operations qpol_table[DCCPQ_POLICY_MAX] = {
78871a2c16STomasz Grobelny [DCCPQ_POLICY_SIMPLE] = {
79871a2c16STomasz Grobelny .push = qpolicy_simple_push,
80871a2c16STomasz Grobelny .full = qpolicy_simple_full,
81871a2c16STomasz Grobelny .top = qpolicy_simple_top,
8204910265STomasz Grobelny .params = 0,
83871a2c16STomasz Grobelny },
84871a2c16STomasz Grobelny [DCCPQ_POLICY_PRIO] = {
85871a2c16STomasz Grobelny .push = qpolicy_simple_push,
86871a2c16STomasz Grobelny .full = qpolicy_prio_full,
87871a2c16STomasz Grobelny .top = qpolicy_prio_best_skb,
8804910265STomasz Grobelny .params = DCCP_SCM_PRIORITY,
89871a2c16STomasz Grobelny },
90871a2c16STomasz Grobelny };
91871a2c16STomasz Grobelny
92871a2c16STomasz Grobelny /*
93871a2c16STomasz Grobelny * Externally visible interface
94871a2c16STomasz Grobelny */
dccp_qpolicy_push(struct sock * sk,struct sk_buff * skb)95871a2c16STomasz Grobelny void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb)
96871a2c16STomasz Grobelny {
97871a2c16STomasz Grobelny qpol_table[dccp_sk(sk)->dccps_qpolicy].push(sk, skb);
98871a2c16STomasz Grobelny }
99871a2c16STomasz Grobelny
dccp_qpolicy_full(struct sock * sk)100871a2c16STomasz Grobelny bool dccp_qpolicy_full(struct sock *sk)
101871a2c16STomasz Grobelny {
102871a2c16STomasz Grobelny return qpol_table[dccp_sk(sk)->dccps_qpolicy].full(sk);
103871a2c16STomasz Grobelny }
104871a2c16STomasz Grobelny
dccp_qpolicy_drop(struct sock * sk,struct sk_buff * skb)105871a2c16STomasz Grobelny void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb)
106871a2c16STomasz Grobelny {
107871a2c16STomasz Grobelny if (skb != NULL) {
108871a2c16STomasz Grobelny skb_unlink(skb, &sk->sk_write_queue);
109871a2c16STomasz Grobelny kfree_skb(skb);
110871a2c16STomasz Grobelny }
111871a2c16STomasz Grobelny }
112871a2c16STomasz Grobelny
dccp_qpolicy_top(struct sock * sk)113871a2c16STomasz Grobelny struct sk_buff *dccp_qpolicy_top(struct sock *sk)
114871a2c16STomasz Grobelny {
115871a2c16STomasz Grobelny return qpol_table[dccp_sk(sk)->dccps_qpolicy].top(sk);
116871a2c16STomasz Grobelny }
117871a2c16STomasz Grobelny
dccp_qpolicy_pop(struct sock * sk)118871a2c16STomasz Grobelny struct sk_buff *dccp_qpolicy_pop(struct sock *sk)
119871a2c16STomasz Grobelny {
120871a2c16STomasz Grobelny struct sk_buff *skb = dccp_qpolicy_top(sk);
121871a2c16STomasz Grobelny
122871a2c16STomasz Grobelny if (skb != NULL) {
123871a2c16STomasz Grobelny /* Clear any skb fields that we used internally */
124871a2c16STomasz Grobelny skb->priority = 0;
125871a2c16STomasz Grobelny skb_unlink(skb, &sk->sk_write_queue);
126871a2c16STomasz Grobelny }
127871a2c16STomasz Grobelny return skb;
128871a2c16STomasz Grobelny }
12904910265STomasz Grobelny
dccp_qpolicy_param_ok(struct sock * sk,__be32 param)13004910265STomasz Grobelny bool dccp_qpolicy_param_ok(struct sock *sk, __be32 param)
13104910265STomasz Grobelny {
13204910265STomasz Grobelny /* check if exactly one bit is set */
13304910265STomasz Grobelny if (!param || (param & (param - 1)))
13404910265STomasz Grobelny return false;
13504910265STomasz Grobelny return (qpol_table[dccp_sk(sk)->dccps_qpolicy].params & param) == param;
13604910265STomasz Grobelny }
137