162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* net/atm/atm_misc.c - Various functions for use by ATM drivers */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL ICA */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/atm.h>
862306a36Sopenharmony_ci#include <linux/atmdev.h>
962306a36Sopenharmony_ci#include <linux/skbuff.h>
1062306a36Sopenharmony_ci#include <linux/sonet.h>
1162306a36Sopenharmony_ci#include <linux/bitops.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/atomic.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciint atm_charge(struct atm_vcc *vcc, int truesize)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	atm_force_charge(vcc, truesize);
1862306a36Sopenharmony_ci	if (atomic_read(&sk_atm(vcc)->sk_rmem_alloc) <= sk_atm(vcc)->sk_rcvbuf)
1962306a36Sopenharmony_ci		return 1;
2062306a36Sopenharmony_ci	atm_return(vcc, truesize);
2162306a36Sopenharmony_ci	atomic_inc(&vcc->stats->rx_drop);
2262306a36Sopenharmony_ci	return 0;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ciEXPORT_SYMBOL(atm_charge);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct sk_buff *atm_alloc_charge(struct atm_vcc *vcc, int pdu_size,
2762306a36Sopenharmony_ci				 gfp_t gfp_flags)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct sock *sk = sk_atm(vcc);
3062306a36Sopenharmony_ci	int guess = SKB_TRUESIZE(pdu_size);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	atm_force_charge(vcc, guess);
3362306a36Sopenharmony_ci	if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
3462306a36Sopenharmony_ci		struct sk_buff *skb = alloc_skb(pdu_size, gfp_flags);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		if (skb) {
3762306a36Sopenharmony_ci			atomic_add(skb->truesize-guess,
3862306a36Sopenharmony_ci				   &sk->sk_rmem_alloc);
3962306a36Sopenharmony_ci			return skb;
4062306a36Sopenharmony_ci		}
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci	atm_return(vcc, guess);
4362306a36Sopenharmony_ci	atomic_inc(&vcc->stats->rx_drop);
4462306a36Sopenharmony_ci	return NULL;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciEXPORT_SYMBOL(atm_alloc_charge);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/*
5062306a36Sopenharmony_ci * atm_pcr_goal returns the positive PCR if it should be rounded up, the
5162306a36Sopenharmony_ci * negative PCR if it should be rounded down, and zero if the maximum available
5262306a36Sopenharmony_ci * bandwidth should be used.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * The rules are as follows (* = maximum, - = absent (0), x = value "x",
5562306a36Sopenharmony_ci * (x+ = x or next value above x, x- = x or next value below):
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci *	min max pcr	result		min max pcr	result
5862306a36Sopenharmony_ci *	-   -   -	* (UBR only)	x   -   -	x+
5962306a36Sopenharmony_ci *	-   -   *	*		x   -   *	*
6062306a36Sopenharmony_ci *	-   -   z	z-		x   -   z	z-
6162306a36Sopenharmony_ci *	-   *   -	*		x   *   -	x+
6262306a36Sopenharmony_ci *	-   *   *	*		x   *   *	*
6362306a36Sopenharmony_ci *	-   *   z	z-		x   *   z	z-
6462306a36Sopenharmony_ci *	-   y   -	y-		x   y   -	x+
6562306a36Sopenharmony_ci *	-   y   *	y-		x   y   *	y-
6662306a36Sopenharmony_ci *	-   y   z	z-		x   y   z	z-
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * All non-error cases can be converted with the following simple set of rules:
6962306a36Sopenharmony_ci *
7062306a36Sopenharmony_ci *   if pcr == z then z-
7162306a36Sopenharmony_ci *   else if min == x && pcr == - then x+
7262306a36Sopenharmony_ci *     else if max == y then y-
7362306a36Sopenharmony_ci *	 else *
7462306a36Sopenharmony_ci */
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ciint atm_pcr_goal(const struct atm_trafprm *tp)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	if (tp->pcr && tp->pcr != ATM_MAX_PCR)
7962306a36Sopenharmony_ci		return -tp->pcr;
8062306a36Sopenharmony_ci	if (tp->min_pcr && !tp->pcr)
8162306a36Sopenharmony_ci		return tp->min_pcr;
8262306a36Sopenharmony_ci	if (tp->max_pcr != ATM_MAX_PCR)
8362306a36Sopenharmony_ci		return -tp->max_pcr;
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ciEXPORT_SYMBOL(atm_pcr_goal);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_civoid sonet_copy_stats(struct k_sonet_stats *from, struct sonet_stats *to)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
9162306a36Sopenharmony_ci	__SONET_ITEMS
9262306a36Sopenharmony_ci#undef __HANDLE_ITEM
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ciEXPORT_SYMBOL(sonet_copy_stats);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_civoid sonet_subtract_stats(struct k_sonet_stats *from, struct sonet_stats *to)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
9962306a36Sopenharmony_ci	__SONET_ITEMS
10062306a36Sopenharmony_ci#undef __HANDLE_ITEM
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ciEXPORT_SYMBOL(sonet_subtract_stats);
103