blob: 455805198e26f89edee316355048d4b6c4fdd40c [file] [log] [blame]
/*
* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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 for more details.
*/
#define pr_fmt(fmt) "seemp: %s: " fmt, __func__
#include "seemp_logk.h"
#include "seemp_ringbuf.h"
#include "seemp_event_encoder.h"
/*initial function no need to hold ring_lock*/
int ringbuf_init(struct seemp_logk_dev *sdev)
{
char *buf;
unsigned long virt_addr;
if (kmalloc_flag) {
sdev->ring = kmalloc(sdev->ring_sz, GFP_KERNEL);
if (sdev->ring == NULL) {
pr_err("kmalloc failed, ring_sz= %d\n", sdev->ring_sz);
return -ENOMEM;
}
buf = (char *)sdev->ring;
/*reserve kmalloc memory as pages to make them remapable*/
for (virt_addr = (unsigned long)buf;
virt_addr < (unsigned long)buf + sdev->ring_sz;
virt_addr += PAGE_SIZE) {
SetPageReserved(virt_to_page((virt_addr)));
}
} else {
sdev->ring = vmalloc(sdev->ring_sz);
if (sdev->ring == NULL) {
pr_err("vmalloc failed, ring_sz = %d\n", sdev->ring_sz);
return -ENOMEM;
}
buf = (char *)sdev->ring;
/*reserve vmalloc memory as pages to make them remapable*/
for (virt_addr = (unsigned long)buf;
virt_addr < (unsigned long)buf + sdev->ring_sz;
virt_addr += PAGE_SIZE) {
SetPageReserved(vmalloc_to_page(
(unsigned long *) virt_addr));
}
}
memset(sdev->ring, 0, sdev->ring_sz);
sdev->num_tot_blks = (sdev->ring_sz / BLK_SIZE);
sdev->num_writers = 0;
sdev->write_idx = 0;
sdev->read_idx = 0;
sdev->num_write_avail_blks = sdev->num_tot_blks;
/*no. of blocks available for write*/
sdev->num_write_in_prog_blks = 0;
/*no. of blocks held by writers to perform writes*/
sdev->num_read_avail_blks = 0;
/*no. of blocks ready for read*/
sdev->num_read_in_prog_blks = 0;
/*no. of blocks held by the reader to perform read*/
return 0;
}
void ringbuf_cleanup(struct seemp_logk_dev *sdev)
{
unsigned long virt_addr;
if (kmalloc_flag) {
for (virt_addr = (unsigned long)sdev->ring;
virt_addr < (unsigned long)sdev->ring + sdev->ring_sz;
virt_addr += PAGE_SIZE) {
/*clear all pages*/
ClearPageReserved(virt_to_page((unsigned long *)
virt_addr));
}
kfree(sdev->ring);
} else {
for (virt_addr = (unsigned long)sdev->ring;
virt_addr < (unsigned long)sdev->ring + sdev->ring_sz;
virt_addr += PAGE_SIZE) {
/*clear all pages*/
ClearPageReserved(vmalloc_to_page((unsigned long *)
virt_addr));
}
vfree(sdev->ring);
}
}
struct seemp_logk_blk *ringbuf_fetch_wr_block
(struct seemp_logk_dev *sdev)
{
struct seemp_logk_blk *blk = NULL;
int idx;
mutex_lock(&sdev->lock);
if (sdev->num_write_avail_blks == 0) {
idx = -1;
mutex_unlock(&sdev->lock);
return blk;
}
idx = sdev->write_idx;
sdev->write_idx = (sdev->write_idx + 1) % sdev->num_tot_blks;
sdev->num_write_avail_blks--;
sdev->num_write_in_prog_blks++;
sdev->num_writers++;
blk = &sdev->ring[idx];
blk->status = 0x0;
mutex_unlock(&sdev->lock);
return blk;
}
void ringbuf_finish_writer(struct seemp_logk_dev *sdev,
struct seemp_logk_blk *blk)
{
/* Encode seemp parameters in multi-threaded mode (before mutex lock) */
encode_seemp_params(blk);
/*
* finish writing...
* the calling process will no longer access this block.
*/
mutex_lock(&sdev->lock);
sdev->num_writers--;
sdev->num_write_in_prog_blks--;
sdev->num_read_avail_blks++;
/*wake up any readers*/
if (sdev->num_writers == 0)
wake_up_interruptible(&sdev->readers_wq);
mutex_unlock(&sdev->lock);
}
int ringbuf_count_marked(struct seemp_logk_dev *sdev)
{
int i;
unsigned int marked;
mutex_lock(&sdev->lock);
for (marked = 0, i = 0; i < sdev->num_tot_blks; i++)
if (sdev->ring[i].status & 0x1)
marked++;
mutex_unlock(&sdev->lock);
return marked;
}