aboutsummaryrefslogtreecommitdiffstats
path: root/crypto/scatterwalk.c
blob: 47ac90e615f49f1ffd490662a9006d7171e38079 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 * Cryptographic API.
 *
 * Cipher operations.
 *
 * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
 *               2002 Adam J. Richter <adam@yggdrasil.com>
 *               2004 Jean-Luc Cooke <jlcooke@certainkey.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 */
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <asm/bug.h>
#include <asm/scatterlist.h>
#include "internal.h"
#include "scatterwalk.h"

enum km_type crypto_km_types[] = {
	KM_USER0,
	KM_USER1,
	KM_SOFTIRQ0,
	KM_SOFTIRQ1,
};

static void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out)
{
	if (out)
		memcpy(sgdata, buf, nbytes);
	else
		memcpy(buf, sgdata, nbytes);
}

void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg)
{
	unsigned int rest_of_page;

	walk->sg = sg;

	walk->page = sg->page;
	walk->len_this_segment = sg->length;

	BUG_ON(!sg->length);

	rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1));
	walk->len_this_page = min(sg->length, rest_of_page);
	walk->offset = sg->offset;
}

void scatterwalk_map(struct scatter_walk *walk, int out)
{
	walk->data = crypto_kmap(walk->page, out) + walk->offset;
}

static inline void scatterwalk_unmap(struct scatter_walk *walk, int out)
{
	/* walk->data may be pointing the first byte of the next page;
	   however, we know we transfered at least one byte.  So,
	   walk->data - 1 will be a virtual address in the mapped page. */
	crypto_kunmap(walk->data - 1, out);
}

static void scatterwalk_pagedone(struct scatter_walk *walk, int out,
				 unsigned int more)
{
	if (out)
		flush_dcache_page(walk->page);

	if (more) {
		walk->len_this_segment -= walk->len_this_page;

		if (walk->len_this_segment) {
			walk->page++;
			walk->len_this_page = min(walk->len_this_segment,
						  (unsigned)PAGE_CACHE_SIZE);
			walk->offset = 0;
		}
		else
			scatterwalk_start(walk, sg_next(walk->sg));
	}
}

void scatterwalk_done(struct scatter_walk *walk, int out, int more)
{
	scatterwalk_unmap(walk, out);
	if (walk->len_this_page == 0 || !more)
		scatterwalk_pagedone(walk, out, more);
}

/*
 * Do not call this unless the total length of all of the fragments
 * has been verified as multiple of the block size.
 */
int scatterwalk_copychunks(void *buf, struct scatter_walk *walk,
			   size_t nbytes, int out)
{
	while (nbytes > walk->len_this_page) {
		memcpy_dir(buf, walk->data, walk->len_this_page, out);
		buf += walk->len_this_page;
		nbytes -= walk->len_this_page;

		scatterwalk_unmap(walk, out);
		scatterwalk_pagedone(walk, out, 1);
		scatterwalk_map(walk, out);
	}

	memcpy_dir(buf, walk->data, nbytes, out);
	return nbytes;
}
="hl kwb">struct fsnotify_mark *mark; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) { if (mark->group == group) { fsnotify_get_mark(mark); return mark; } } return NULL; } /* * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, struct inode *inode) { struct fsnotify_mark *mark; spin_lock(&inode->i_lock); mark = fsnotify_find_inode_mark_locked(group, inode); spin_unlock(&inode->i_lock); return mark; } /* * If we are setting a mark mask on an inode mark we should pin the inode * in memory. */ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) { struct inode *inode; assert_spin_locked(&mark->lock); if (mask && mark->i.inode && !(mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) { mark->flags |= FSNOTIFY_MARK_FLAG_OBJECT_PINNED; inode = igrab(mark->i.inode); /* * we shouldn't be able to get here if the inode wasn't * already safely held in memory. But bug in case it * ever is wrong. */ BUG_ON(!inode); } } /* * Attach an initialized mark to a given inode. * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. These * marks are ordered according to priority, highest number first, and then by * the group's location in memory. */ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { struct fsnotify_mark *lmark; struct hlist_node *node, *last = NULL; int ret = 0; mark->flags |= FSNOTIFY_MARK_FLAG_INODE; assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); spin_lock(&inode->i_lock); mark->i.inode = inode; /* is mark the first mark? */ if (hlist_empty(&inode->i_fsnotify_marks)) { hlist_add_head_rcu(&mark->i.i_list, &inode->i_fsnotify_marks); goto out; } /* should mark be in the middle of the current list? */ hlist_for_each_entry(lmark, node, &inode->i_fsnotify_marks, i.i_list) { last = node; if ((lmark->group == group) && !allow_dups) { ret = -EEXIST; goto out; } if (mark->group->priority < lmark->group->priority) continue; if ((mark->group->priority == lmark->group->priority) && (mark->group < lmark->group)) continue; hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); goto out; } BUG_ON(last == NULL); /* mark should be the last entry. last is the current last entry */ hlist_add_after_rcu(last, &mark->i.i_list); out: fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); return ret; } /** * fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes. * @list: list of inodes being unmounted (sb->s_inodes) * * Called with inode_lock held, protecting the unmounting super block's list * of inodes, and with iprune_mutex held, keeping shrink_icache_memory() at bay. * We temporarily drop inode_lock, however, and CAN block. */ void fsnotify_unmount_inodes(struct list_head *list) { struct inode *inode, *next_i, *need_iput = NULL; spin_lock(&inode_lock); list_for_each_entry_safe(inode, next_i, list, i_sb_list) { struct inode *need_iput_tmp; /* * We cannot __iget() an inode in state I_FREEING, * I_WILL_FREE, or I_NEW which is fine because by that point * the inode cannot have any associated watches. */ if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) continue; /* * If i_count is zero, the inode cannot have any watches and * doing an __iget/iput with MS_ACTIVE clear would actually * evict all inodes with zero i_count from icache which is * unnecessarily violent and may in fact be illegal to do. */ if (!atomic_read(&inode->i_count)) continue; need_iput_tmp = need_iput; need_iput = NULL; /* In case fsnotify_inode_delete() drops a reference. */ if (inode != need_iput_tmp) __iget(inode); else need_iput_tmp = NULL; /* In case the dropping of a reference would nuke next_i. */ if ((&next_i->i_sb_list != list) && atomic_read(&next_i->i_count) && !(next_i->i_state & (I_FREEING | I_WILL_FREE))) { __iget(next_i); need_iput = next_i; } /* * We can safely drop inode_lock here because we hold * references on both inode and next_i. Also no new inodes * will be added since the umount has begun. Finally, * iprune_mutex keeps shrink_icache_memory() away. */ spin_unlock(&inode_lock); if (need_iput_tmp) iput(need_iput_tmp); /* for each watch, send FS_UNMOUNT and then remove it */ fsnotify(inode, FS_UNMOUNT, inode, FSNOTIFY_EVENT_INODE, NULL, 0); fsnotify_inode_delete(inode); iput(inode); spin_lock(&inode_lock); } spin_unlock(&inode_lock); }