/*
* srp_sync - discover SRP targets over IB
* Copyright (c) 2005 Topspin Communications. All rights reserved.
* Copyright (c) 2006 Cisco Systems, Inc. All rights reserved.
* Copyright (c) 2006 Mellanox Technologies Ltd. All rights reserved.
*
* This software is available to you under a choice of one of two
* licenses. You may choose to be licensed under the terms of the GNU
* General Public License (GPL) Version 2, available from the file
* COPYING in the main directory of this source tree, or the
* OpenIB.org BSD license below:
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* $Author: ishai Rabinovitz [ishai@mellanox.co.il]$
*/
#include <pthread.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "srp_daemon.h"
/*
* Schedule a rescan at now + when if when >= 0 or disable rescanning if
* when < 0.
*/
void __schedule_rescan(struct sync_resources *res, int when)
{
struct timespec *ts = &res->next_recalc_time;
clock_gettime(CLOCK_MONOTONIC, ts);
ts->tv_sec = when >= 0 ? ts->tv_sec + when : LONG_MAX;
}
void schedule_rescan(struct sync_resources *res, int when)
{
pthread_mutex_lock(&res->mutex);
__schedule_rescan(res, when);
pthread_mutex_unlock(&res->mutex);
}
int __rescan_scheduled(struct sync_resources *res)
{
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
return ts_cmp(&res->next_recalc_time, &now, <=);
}
int rescan_scheduled(struct sync_resources *res)
{
int ret;
pthread_mutex_lock(&res->mutex);
ret = __rescan_scheduled(res);
pthread_mutex_unlock(&res->mutex);
return ret;
}
int sync_resources_init(struct sync_resources *res)
{
int ret;
res->stop_threads = 0;
__schedule_rescan(res, 0);
res->next_task = 0;
ret = pthread_mutex_init(&res->mutex, NULL);
if (ret < 0) {
pr_err("could not initialize mutex\n");
return ret;
}
res->retry_tasks_head = NULL;
ret = pthread_mutex_init(&res->retry_mutex, NULL);
if (ret < 0) {
pr_err("could not initialize mutex\n");
return ret;
}
ret = pthread_cond_init(&res->retry_cond, NULL);
if (ret < 0)
pr_err("could not initialize cond\n");
return ret;
}
void sync_resources_cleanup(struct sync_resources *res)
{
pthread_cond_destroy(&res->retry_cond);
pthread_mutex_destroy(&res->retry_mutex);
pthread_mutex_destroy(&res->mutex);
}
void push_gid_to_list(struct sync_resources *res, union umad_gid *gid,
uint16_t pkey)
{
int i;
/* If there is going to be a recalc soon - do nothing */
if (rescan_scheduled(res))
return;
pthread_mutex_lock(&res->mutex);
/* check if the gid is already in the list */
for (i=0; i < res->next_task; ++i)
if (!memcmp(&res->tasks[i].gid, gid, 16) &&
res->tasks[i].pkey == pkey) {
pr_debug("gid is already in task list\n");
pthread_mutex_unlock(&res->mutex);
return;
}
if (res->next_task == SIZE_OF_TASKS_LIST) {
/* if the list is full, lets do a full rescan */
__schedule_rescan(res, 0);
res->next_task = 0;
} else {
/* otherwise enter to the next entry */
res->tasks[res->next_task].gid = *gid;
res->tasks[res->next_task].lid = 0;
res->tasks[res->next_task].pkey = pkey;
++res->next_task;
}
wake_up_main_loop(0);
pthread_mutex_unlock(&res->mutex);
}
void push_lid_to_list(struct sync_resources *res, uint16_t lid, uint16_t pkey)
{
int i;
/* If there is going to be a recalc soon - do nothing */
if (rescan_scheduled(res))
return;
pthread_mutex_lock(&res->mutex);
/* check if the lid is already in the list */
for (i=0; i < res->next_task; ++i)
if (res->tasks[i].lid == lid && res->tasks[i].pkey == pkey) {
pr_debug("lid %#x is already in task list\n", lid);
pthread_mutex_unlock(&res->mutex);
return;
}
if (res->next_task == SIZE_OF_TASKS_LIST) {
/* if the list is full, lets do a full rescan */
__schedule_rescan(res, 0);
res->next_task = 0;
} else {
/* otherwise enter to the next entry */
res->tasks[res->next_task].lid = lid;
res->tasks[res->next_task].pkey = pkey;
memset(&res->tasks[res->next_task].gid, 0, 16);
++res->next_task;
}
wake_up_main_loop(0);
pthread_mutex_unlock(&res->mutex);
}
void clear_traps_list(struct sync_resources *res)
{
pthread_mutex_lock(&res->mutex);
res->next_task = 0;
pthread_mutex_unlock(&res->mutex);
}
/* assumes that res->mutex is locked !!! */
int pop_from_list(struct sync_resources *res, uint16_t *lid,
union umad_gid *gid, uint16_t *pkey)
{
int ret=0;
int i;
if (res->next_task) {
*lid = res->tasks[0].lid;
*pkey = res->tasks[0].pkey;
*gid = res->tasks[0].gid;
/* push the rest down */
for (i=1; i < res->next_task; ++i)
res->tasks[i-1] = res->tasks[i];
ret = 1;
--res->next_task;
}
return ret;
}
/* assumes that res->retry_mutex is locked !!! */
struct target_details *pop_from_retry_list(struct sync_resources *res)
{
struct target_details *ret = res->retry_tasks_head;
if (ret)
res->retry_tasks_head = ret->next;
else
res->retry_tasks_tail = NULL;
return ret;
}
void push_to_retry_list(struct sync_resources *res,
struct target_details *orig_target)
{
struct target_details *target;
/* If there is going to be a recalc soon - do nothing */
if (rescan_scheduled(res))
return;
target = malloc(sizeof(struct target_details));
memcpy(target, orig_target, sizeof(struct target_details));
pthread_mutex_lock(&res->retry_mutex);
if (!res->retry_tasks_head)
res->retry_tasks_head = target;
if (res->retry_tasks_tail)
res->retry_tasks_tail->next = target;
res->retry_tasks_tail = target;
target->next = NULL;
pthread_cond_signal(&res->retry_cond);
pthread_mutex_unlock(&res->retry_mutex);
}
/* assumes that res->retry_mutex is locked !!! */
int retry_list_is_empty(struct sync_resources *res)
{
return res->retry_tasks_head == NULL;
}