aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/basic
diff options
context:
space:
mode:
authorRafał Miłecki <zajec5@gmail.com>2014-07-30 15:38:00 +0200
committerJohn W. Linville <linville@tuxdriver.com>2014-07-31 13:45:30 -0400
commited7f75b4f058495332e107efa1b533fcc8720420 (patch)
treee97d1bc02deba3ac1150f20bb90c1f75576991c3 /scripts/basic
parentbrcmfmac: Add TDLS support to msgbuf. (diff)
b43: update PHY descriptions in Kconfig
Add lists of chipsets, so people can enable support for their device easier (at least after checking lspci). Signed-off-by: Rafał Miłecki <zajec5@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'scripts/basic')
0 files changed, 0 insertions, 0 deletions
6 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/ceph/pagelist.h>

struct ceph_pagelist *ceph_pagelist_alloc(gfp_t gfp_flags)
{
	struct ceph_pagelist *pl;

	pl = kmalloc(sizeof(*pl), gfp_flags);
	if (!pl)
		return NULL;

	INIT_LIST_HEAD(&pl->head);
	pl->mapped_tail = NULL;
	pl->length = 0;
	pl->room = 0;
	INIT_LIST_HEAD(&pl->free_list);
	pl->num_pages_free = 0;
	refcount_set(&pl->refcnt, 1);

	return pl;
}
EXPORT_SYMBOL(ceph_pagelist_alloc);

static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
{
	if (pl->mapped_tail) {
		struct page *page = list_entry(pl->head.prev, struct page, lru);
		kunmap(page);
		pl->mapped_tail = NULL;
	}
}

void ceph_pagelist_release(struct ceph_pagelist *pl)
{
	if (!refcount_dec_and_test(&pl->refcnt))
		return;
	ceph_pagelist_unmap_tail(pl);
	while (!list_empty(&pl->head)) {
		struct page *page = list_first_entry(&pl->head, struct page,
						     lru);
		list_del(&page->lru);
		__free_page(page);
	}
	ceph_pagelist_free_reserve(pl);
	kfree(pl);
}
EXPORT_SYMBOL(ceph_pagelist_release);

static int ceph_pagelist_addpage(struct ceph_pagelist *pl)
{
	struct page *page;

	if (!pl->num_pages_free) {
		page = __page_cache_alloc(GFP_NOFS);
	} else {
		page = list_first_entry(&pl->free_list, struct page, lru);
		list_del(&page->lru);
		--pl->num_pages_free;
	}
	if (!page)
		return -ENOMEM;
	pl->room += PAGE_SIZE;
	ceph_pagelist_unmap_tail(pl);
	list_add_tail(&page->lru, &pl->head);
	pl->mapped_tail = kmap(page);
	return 0;
}

int ceph_pagelist_append(struct ceph_pagelist *pl, const void *buf, size_t len)
{
	while (pl->room < len) {
		size_t bit = pl->room;
		int ret;

		memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK),
		       buf, bit);
		pl->length += bit;
		pl->room -= bit;
		buf += bit;
		len -= bit;
		ret = ceph_pagelist_addpage(pl);
		if (ret)
			return ret;
	}

	memcpy(pl->mapped_tail + (pl->length & ~PAGE_MASK), buf, len);
	pl->length += len;
	pl->room -= len;
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_append);

/* Allocate enough pages for a pagelist to append the given amount
 * of data without without allocating.
 * Returns: 0 on success, -ENOMEM on error.
 */
int ceph_pagelist_reserve(struct ceph_pagelist *pl, size_t space)
{
	if (space <= pl->room)
		return 0;
	space -= pl->room;
	space = (space + PAGE_SIZE - 1) >> PAGE_SHIFT;   /* conv to num pages */

	while (space > pl->num_pages_free) {
		struct page *page = __page_cache_alloc(GFP_NOFS);
		if (!page)
			return -ENOMEM;
		list_add_tail(&page->lru, &pl->free_list);
		++pl->num_pages_free;
	}
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_reserve);

/* Free any pages that have been preallocated. */
int ceph_pagelist_free_reserve(struct ceph_pagelist *pl)
{
	while (!list_empty(&pl->free_list)) {
		struct page *page = list_first_entry(&pl->free_list,
						     struct page, lru);
		list_del(&page->lru);
		__free_page(page);
		--pl->num_pages_free;
	}
	BUG_ON(pl->num_pages_free);
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_free_reserve);

/* Create a truncation point. */
void ceph_pagelist_set_cursor(struct ceph_pagelist *pl,
			      struct ceph_pagelist_cursor *c)
{
	c->pl = pl;
	c->page_lru = pl->head.prev;
	c->room = pl->room;
}
EXPORT_SYMBOL(ceph_pagelist_set_cursor);

/* Truncate a pagelist to the given point. Move extra pages to reserve.
 * This won't sleep.
 * Returns: 0 on success,
 *          -EINVAL if the pagelist doesn't match the trunc point pagelist
 */
int ceph_pagelist_truncate(struct ceph_pagelist *pl,
			   struct ceph_pagelist_cursor *c)
{
	struct page *page;

	if (pl != c->pl)
		return -EINVAL;
	ceph_pagelist_unmap_tail(pl);
	while (pl->head.prev != c->page_lru) {
		page = list_entry(pl->head.prev, struct page, lru);
		/* move from pagelist to reserve */
		list_move_tail(&page->lru, &pl->free_list);
		++pl->num_pages_free;
	}
	pl->room = c->room;
	if (!list_empty(&pl->head)) {
		page = list_entry(pl->head.prev, struct page, lru);
		pl->mapped_tail = kmap(page);
	}
	return 0;
}
EXPORT_SYMBOL(ceph_pagelist_truncate);