platform/broadcom/saibcm-modules/systems/linux/kernel/modules/shared/ksal.c (183 lines of code) (raw):
/*
* Copyright 2007-2020 Broadcom Inc. All rights reserved.
*
* Permission is granted to use, copy, modify and/or distribute this
* software under either one of the licenses below.
*
* License Option 1: GPL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation (the "GPL").
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 (GPLv2) for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 (GPLv2) along with this source code.
*
*
* License Option 2: Broadcom Open Network Switch APIs (OpenNSA) license
*
* This software is governed by the Broadcom Open Network Switch APIs license:
* https://www.broadcom.com/products/ethernet-connectivity/software/opennsa
*/
/*
* $Id: ksal.c,v 1.1 Broadcom SDK $
* $Copyright: (c) 2005 Broadcom Corp.
* All Rights Reserved.$
*/
#include <sal/core/sync.h>
#include <sal/core/thread.h>
#include "lkm.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
#include <linux/semaphore.h>
#else
#include <asm/semaphore.h>
#endif
#include <linux/interrupt.h>
#include <linux/sched.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
#include <linux/sched/rt.h>
#endif
#include <linux/time.h>
#ifdef MAX_USER_RT_PRIO
/* Assume 2.6 scheduler */
#define SAL_YIELD(task) \
yield()
#else
/* Assume 2.4 scheduler */
#define SAL_YIELD(task) \
do { \
task->policy |= SCHED_YIELD; \
schedule(); \
} while (0)
#endif
#define SECOND_USEC (1000000)
#define USECS_PER_JIFFY (SECOND_USEC / HZ)
#define USEC_TO_JIFFIES(usec) ((usec + (USECS_PER_JIFFY - 1)) / USECS_PER_JIFFY)
#define sal_alloc(size, desc) kmalloc(size, GFP_KERNEL)
#define sal_free(ptr) kfree(ptr)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,12)
#define WQ_SLEEP(a, b) wait_event_interruptible_timeout(a, NULL, b)
#else
#define WQ_SLEEP(a, b) interruptible_sleep_on_timeout(&(a), b)
#endif
/*
* sem_ctrl_t
*
* The semaphore control type uses the binary property to implement
* timed semaphores with improved performance using wait queues.
*/
typedef struct sem_ctrl_s {
struct semaphore sem;
int binary;
int cnt;
wait_queue_head_t wq;
} sem_ctrl_t;
sal_sem_t
sal_sem_create(char *desc, int binary, int initial_count)
{
sem_ctrl_t *s;
if ((s = sal_alloc(sizeof(*s), desc)) != 0) {
sema_init(&s->sem, initial_count);
s->binary = binary;
if (s->binary) {
init_waitqueue_head(&s->wq);
}
}
return (sal_sem_t) s;
}
void
sal_sem_destroy(sal_sem_t b)
{
sem_ctrl_t *s = (sem_ctrl_t *) b;
if (s == NULL) {
return;
}
/*
* the linux kernel does not have a sema_destroy(s)
*/
sal_free(s);
}
int
sal_sem_take(sal_sem_t b, int usec)
{
sem_ctrl_t *s = (sem_ctrl_t *) b;
int err;
if (usec == sal_sem_FOREVER && !in_interrupt()) {
err = down_interruptible(&s->sem);
} else {
int time_wait = 1;
int cnt = s->cnt;
for (;;) {
if (down_trylock(&s->sem) == 0) {
err = 0;
break;
}
if (s->binary) {
/* Wait for event or timeout */
if (time_wait > 1) {
err = 1;
break;
}
err = wait_event_interruptible_timeout(s->wq, cnt != s->cnt,
USEC_TO_JIFFIES(usec));
if (err < 0) {
break;
}
time_wait++;
} else {
/* Retry algorithm with exponential backoff */
if (time_wait > usec) {
time_wait = usec;
}
sal_usleep(time_wait);
usec -= time_wait;
if (usec == 0) {
err = ETIMEDOUT;
break;
}
if ((time_wait *= 2) > 100000) {
time_wait = 100000;
}
}
}
}
return err ? -1 : 0;
}
int
sal_sem_give(sal_sem_t b)
{
sem_ctrl_t *s = (sem_ctrl_t *) b;
up(&s->sem);
if (s->binary) {
s->cnt++;
wake_up_interruptible(&s->wq);
}
return 0;
}
uint32
sal_time_usecs(void)
{
#if !defined(SAI_FIXUP)
struct timeval ltv;
do_gettimeofday(<v);
return (ltv.tv_sec * SECOND_USEC + ltv.tv_usec);
#else
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0)
/* ktime_to_us and ktime_get_real_ns return 64-bit integets, but this */
/* function is returning a 32-bit integer. This should be fine until 2038. */
return ktime_to_us(ktime_get_real_ns());
#else
struct timeval ltv;
do_gettimeofday(<v);
return (ltv.tv_sec * SECOND_USEC + ltv.tv_usec);
#endif
#endif
}
void
sal_usleep(uint32 usec)
{
uint32 start_usec;
wait_queue_head_t queue;
if (usec <= SECOND_USEC / HZ) {
start_usec = sal_time_usecs();
do {
SAL_YIELD(current);
} while ((sal_time_usecs() - start_usec) < usec);
} else {
init_waitqueue_head(&queue);
WQ_SLEEP(queue, USEC_TO_JIFFIES(usec));
}
}
void
sal_udelay(uint32 usec)
{
static volatile int _sal_udelay_counter;
static int loops = 0;
int ix, iy;
if (loops == 0 || usec == 0) { /* Need calibration? */
int max_loops;
int start = 0, stop = 0;
int mpt = USECS_PER_JIFFY; /* usec/tick */
for (loops = 1; loops < 0x1000 && stop == start; loops <<= 1) {
/* Wait for clock turn over */
for (stop = start = jiffies; start == stop; start = jiffies) {
/* Empty */
}
sal_udelay(mpt); /* Single recursion */
stop = jiffies;
}
max_loops = loops / 2; /* Loop above overshoots */
start = stop = 0;
if (loops < 4) {
loops = 4;
}
for (loops /= 4; loops < max_loops && stop == start; loops++) {
/* Wait for clock turn over */
for (stop = start = jiffies; start == stop; start = jiffies) {
/* Empty */
}
sal_udelay(mpt); /* Single recursion */
stop = jiffies;
}
}
for (iy = 0; iy < usec; iy++) {
for (ix = 0; ix < loops; ix++) {
_sal_udelay_counter++; /* Prevent optimizations */
}
}
}