<commit_before>//
// Copyright 2016 <NAME>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "libc.h"
#include "slab.h"
#include "spinlock.h"
#include "thread.h"
#include "trap.h"
#include "vm_cache.h"

#define NUM_HASH_BUCKETS 37

static spinlock_t cache_lock;
struct list_node hash_table[NUM_HASH_BUCKETS];
MAKE_SLAB(cache_slab, struct vm_cache);
static int debug_cache_lock_owner = -1;

void bootstrap_vm_cache(void)
{
    int i;

    for (i = 0; i < NUM_HASH_BUCKETS; i++)
        list_init(&hash_table[i]);
}

struct vm_cache *create_vm_cache(void)
{
    struct vm_cache *cache;

    cache = slab_alloc(&cache_slab);
    list_init(&cache->page_list);
    cache->ref_count = 1;

    return cache;
}

static unsigned int gen_hash(struct vm_cache *cache, unsigned int offset)
{
	return (unsigned int) cache + (unsigned int) offset / PAGE_SIZE;
}

void lock_vm_cache(void)
{
    acquire_spinlock(&cache_lock);
    debug_cache_lock_owner = current_hw_thread();
}

void unlock_vm_cache(void)
{
    debug_cache_lock_owner = -1;
    release_spinlock(&cache_lock);
}

void inc_cache_ref(struct vm_cache *cache)
{
    __sync_fetch_and_add(&cache->ref_count, 1);
}

void dec_cache_ref(struct vm_cache *cache)
{
    struct vm_page *page;

    assert(debug_cache_lock_owner != current_hw_thread());
    assert(cache->ref_count > 0);

    if (__sync_fetch_and_add(&cache->ref_count, -1) == 1)
    {
        lock_vm_cache();
        kprintf("destroying vm_cache %p\n", cache);

        // Free all pages owned by this cache
        while (!list_is_empty(&cache->page_list))
        {
            page = list_peek_head(&cache->page_list, struct vm_page);
            remove_cache_page(page);
            vm_free_page(address_for_page(page));
            kprintf("free page %08x\n", address_for_page(page));
        }

        unlock_vm_cache();
    }
}

void insert_cache_page(struct vm_cache *cache, unsigned int offset,
                       struct vm_page *page)
{
    unsigned int bucket;

    assert(debug_cache_lock_owner == current_hw_thread());

    offset = PAGE_ALIGN(offset);
    bucket = gen_hash(cache, offset) % NUM_HASH_BUCKETS;
    assert(page->cache == 0);
    page->cache = cache;
    page->cache_offset = offset;
    list_add_tail(&hash_table[bucket], &page->hash_entry);
    list_add_tail(&cache->page_list, &page->list_entry);
}

struct vm_page *lookup_cache_page(struct vm_cache *cache, unsigned int offset)
{
    unsigned int bucket;
    struct vm_page *page;
    int found = 0;

    assert(debug_cache_lock_owner == current_hw_thread());

    offset = PAGE_ALIGN(offset);
    bucket = gen_hash(cache, offset) % NUM_HASH_BUCKETS;
    list_for_each(&hash_table[bucket], page, struct vm_page)
    {
        if (page->cache_offset == offset && page->cache == cache)
            return page;
    }

    return 0;
}

void remove_cache_page(struct vm_page *page)
{
    assert(debug_cache_lock_owner == current_hw_thread());

    list_remove_node(&page->list_entry);
    list_remove_node(&page->hash_entry);
}<commit_msg>kernel: fix bug in cache lookup<commit_after>//
// Copyright 2016 <NAME>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include "libc.h"
#include "slab.h"
#include "spinlock.h"
#include "thread.h"
#include "trap.h"
#include "vm_cache.h"

#define NUM_HASH_BUCKETS 37

static spinlock_t cache_lock;
struct list_node hash_table[NUM_HASH_BUCKETS];
MAKE_SLAB(cache_slab, struct vm_cache);
static int debug_cache_lock_owner = -1;

void bootstrap_vm_cache(void)
{
    int i;

    for (i = 0; i < NUM_HASH_BUCKETS; i++)
        list_init(&hash_table[i]);
}

struct vm_cache *create_vm_cache(void)
{
    struct vm_cache *cache;

    cache = slab_alloc(&cache_slab);
    list_init(&cache->page_list);
    cache->ref_count = 1;

    return cache;
}

static unsigned int gen_hash(struct vm_cache *cache, unsigned int offset)
{
	return (unsigned int) cache + (unsigned int) offset / PAGE_SIZE;
}

void lock_vm_cache(void)
{
    acquire_spinlock(&cache_lock);
    debug_cache_lock_owner = current_hw_thread();
}

void unlock_vm_cache(void)
{
    debug_cache_lock_owner = -1;
    release_spinlock(&cache_lock);
}

void inc_cache_ref(struct vm_cache *cache)
{
    __sync_fetch_and_add(&cache->ref_count, 1);
}

void dec_cache_ref(struct vm_cache *cache)
{
    struct vm_page *page;

    assert(debug_cache_lock_owner != current_hw_thread());
    assert(cache->ref_count > 0);

    if (__sync_fetch_and_add(&cache->ref_count, -1) == 1)
    {
        lock_vm_cache();
        kprintf("destroying vm_cache %p\n", cache);

        // Free all pages owned by this cache
        while (!list_is_empty(&cache->page_list))
        {
            page = list_peek_head(&cache->page_list, struct vm_page);
            remove_cache_page(page);
            vm_free_page(address_for_page(page));
            kprintf("free page %08x\n", address_for_page(page));
        }

        unlock_vm_cache();
    }
}

void insert_cache_page(struct vm_cache *cache, unsigned int offset,
                       struct vm_page *page)
{
    unsigned int bucket;

    assert(debug_cache_lock_owner == current_hw_thread());

    offset = PAGE_ALIGN(offset);
    bucket = gen_hash(cache, offset) % NUM_HASH_BUCKETS;
    assert(page->cache == 0);
    page->cache = cache;
    page->cache_offset = offset;
    list_add_tail(&hash_table[bucket], &page->hash_entry);
    list_add_tail(&cache->page_list, &page->list_entry);
}

struct vm_page *lookup_cache_page(struct vm_cache *cache, unsigned int offset)
{
    unsigned int bucket;
    struct vm_page *page;
    int found = 0;

    assert(debug_cache_lock_owner == current_hw_thread());

    offset = PAGE_ALIGN(offset);
    bucket = gen_hash(cache, offset) % NUM_HASH_BUCKETS;
    multilist_for_each(&hash_table[bucket], page, hash_entry, struct vm_page)
    {
        if (page->cache_offset == offset && page->cache == cache)
            return page;
    }

    return 0;
}

void remove_cache_page(struct vm_page *page)
{
    assert(debug_cache_lock_owner == current_hw_thread());

    list_remove_node(&page->list_entry);
    list_remove_node(&page->hash_entry);
}
{'fraction_non_alphanumeric': 0.11442995372318047, 'fraction_numerical': 0.006450708175571448, 'mean_word_length': 3.5983236621534496, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3365608', 'n_tokens_mistral': 2415, 'n_tokens_neox': 2343, 'n_words': 574}