18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* net/atm/atm_misc.c - Various functions for use by ATM drivers */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci/* Written 1995-2000 by Werner Almesberger, EPFL ICA */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/atm.h>
88c2ecf20Sopenharmony_ci#include <linux/atmdev.h>
98c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
108c2ecf20Sopenharmony_ci#include <linux/sonet.h>
118c2ecf20Sopenharmony_ci#include <linux/bitops.h>
128c2ecf20Sopenharmony_ci#include <linux/errno.h>
138c2ecf20Sopenharmony_ci#include <linux/atomic.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciint atm_charge(struct atm_vcc *vcc, int truesize)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	atm_force_charge(vcc, truesize);
188c2ecf20Sopenharmony_ci	if (atomic_read(&sk_atm(vcc)->sk_rmem_alloc) <= sk_atm(vcc)->sk_rcvbuf)
198c2ecf20Sopenharmony_ci		return 1;
208c2ecf20Sopenharmony_ci	atm_return(vcc, truesize);
218c2ecf20Sopenharmony_ci	atomic_inc(&vcc->stats->rx_drop);
228c2ecf20Sopenharmony_ci	return 0;
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_charge);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistruct sk_buff *atm_alloc_charge(struct atm_vcc *vcc, int pdu_size,
278c2ecf20Sopenharmony_ci				 gfp_t gfp_flags)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct sock *sk = sk_atm(vcc);
308c2ecf20Sopenharmony_ci	int guess = SKB_TRUESIZE(pdu_size);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	atm_force_charge(vcc, guess);
338c2ecf20Sopenharmony_ci	if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf) {
348c2ecf20Sopenharmony_ci		struct sk_buff *skb = alloc_skb(pdu_size, gfp_flags);
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci		if (skb) {
378c2ecf20Sopenharmony_ci			atomic_add(skb->truesize-guess,
388c2ecf20Sopenharmony_ci				   &sk->sk_rmem_alloc);
398c2ecf20Sopenharmony_ci			return skb;
408c2ecf20Sopenharmony_ci		}
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci	atm_return(vcc, guess);
438c2ecf20Sopenharmony_ci	atomic_inc(&vcc->stats->rx_drop);
448c2ecf20Sopenharmony_ci	return NULL;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_alloc_charge);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * atm_pcr_goal returns the positive PCR if it should be rounded up, the
518c2ecf20Sopenharmony_ci * negative PCR if it should be rounded down, and zero if the maximum available
528c2ecf20Sopenharmony_ci * bandwidth should be used.
538c2ecf20Sopenharmony_ci *
548c2ecf20Sopenharmony_ci * The rules are as follows (* = maximum, - = absent (0), x = value "x",
558c2ecf20Sopenharmony_ci * (x+ = x or next value above x, x- = x or next value below):
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci *	min max pcr	result		min max pcr	result
588c2ecf20Sopenharmony_ci *	-   -   -	* (UBR only)	x   -   -	x+
598c2ecf20Sopenharmony_ci *	-   -   *	*		x   -   *	*
608c2ecf20Sopenharmony_ci *	-   -   z	z-		x   -   z	z-
618c2ecf20Sopenharmony_ci *	-   *   -	*		x   *   -	x+
628c2ecf20Sopenharmony_ci *	-   *   *	*		x   *   *	*
638c2ecf20Sopenharmony_ci *	-   *   z	z-		x   *   z	z-
648c2ecf20Sopenharmony_ci *	-   y   -	y-		x   y   -	x+
658c2ecf20Sopenharmony_ci *	-   y   *	y-		x   y   *	y-
668c2ecf20Sopenharmony_ci *	-   y   z	z-		x   y   z	z-
678c2ecf20Sopenharmony_ci *
688c2ecf20Sopenharmony_ci * All non-error cases can be converted with the following simple set of rules:
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci *   if pcr == z then z-
718c2ecf20Sopenharmony_ci *   else if min == x && pcr == - then x+
728c2ecf20Sopenharmony_ci *     else if max == y then y-
738c2ecf20Sopenharmony_ci *	 else *
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ciint atm_pcr_goal(const struct atm_trafprm *tp)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	if (tp->pcr && tp->pcr != ATM_MAX_PCR)
798c2ecf20Sopenharmony_ci		return -tp->pcr;
808c2ecf20Sopenharmony_ci	if (tp->min_pcr && !tp->pcr)
818c2ecf20Sopenharmony_ci		return tp->min_pcr;
828c2ecf20Sopenharmony_ci	if (tp->max_pcr != ATM_MAX_PCR)
838c2ecf20Sopenharmony_ci		return -tp->max_pcr;
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(atm_pcr_goal);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid sonet_copy_stats(struct k_sonet_stats *from, struct sonet_stats *to)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci#define __HANDLE_ITEM(i) to->i = atomic_read(&from->i)
918c2ecf20Sopenharmony_ci	__SONET_ITEMS
928c2ecf20Sopenharmony_ci#undef __HANDLE_ITEM
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sonet_copy_stats);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_civoid sonet_subtract_stats(struct k_sonet_stats *from, struct sonet_stats *to)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci#define __HANDLE_ITEM(i) atomic_sub(to->i, &from->i)
998c2ecf20Sopenharmony_ci	__SONET_ITEMS
1008c2ecf20Sopenharmony_ci#undef __HANDLE_ITEM
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sonet_subtract_stats);
103