| /* |
| * 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; |
| } |