Skip to content

Commit 7717cb9

Browse files
author
Al Viro
committed
regset: new method and helpers for it
->regset_get() takes task+regset+buffer, returns the amount of free space left in the buffer on success and -E... on error. buffer is represented as struct membuf - a pair of (kernel) pointer and amount of space left Primitives for writing to such: * membuf_write(buf, data, size) * membuf_zero(buf, size) * membuf_store(buf, value) These are implemented as inlines (in case of membuf_store - a macro). All writes are sequential; they become no-ops when there's no space left. Return value of all primitives is the amount of space left after the operation, so they can be used as return values of ->regset_get(). Example of use: // stores pt_regs of task + 64 bytes worth of zeroes + 32bit PID of task int foo_get(struct task_struct *task, const struct regset *regset, struct membuf to) { membuf_write(&to, task_pt_regs(task), sizeof(struct pt_regs)); membuf_zero(&to, 64); return membuf_store(&to, (u32)task_tgid_vnr(task)); } regset_get()/regset_get_alloc() taught to use that thing if present. By the end of the series all users of ->get() will be converted; then ->get() and ->get_size() can go. Note that unlike ->get() this thing always starts at offset 0 and, since it only writes to kernel buffer, can't fail on copyout. It can, of course, fail for other reasons, but those tend to be less numerous. The caller guarantees that the buffer size won't be bigger than regset->n * regset->size. That simplifies life for quite a few instances. Signed-off-by: Al Viro <[email protected]>
1 parent dc12d79 commit 7717cb9

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

include/linux/regset.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,52 @@
1717
struct task_struct;
1818
struct user_regset;
1919

20+
struct membuf {
21+
void *p;
22+
size_t left;
23+
};
24+
25+
static inline int membuf_zero(struct membuf *s, size_t size)
26+
{
27+
if (s->left) {
28+
if (size > s->left)
29+
size = s->left;
30+
memset(s->p, 0, size);
31+
s->p += size;
32+
s->left -= size;
33+
}
34+
return s->left;
35+
}
36+
37+
static inline int membuf_write(struct membuf *s, const void *v, size_t size)
38+
{
39+
if (s->left) {
40+
if (size > s->left)
41+
size = s->left;
42+
memcpy(s->p, v, size);
43+
s->p += size;
44+
s->left -= size;
45+
}
46+
return s->left;
47+
}
48+
49+
/* current s->p must be aligned for v; v must be a scalar */
50+
#define membuf_store(s, v) \
51+
({ \
52+
struct membuf *__s = (s); \
53+
if (__s->left) { \
54+
typeof(v) __v = (v); \
55+
size_t __size = sizeof(__v); \
56+
if (unlikely(__size > __s->left)) { \
57+
__size = __s->left; \
58+
memcpy(__s->p, &__v, __size); \
59+
} else { \
60+
*(typeof(__v + 0) *)__s->p = __v; \
61+
} \
62+
__s->p += __size; \
63+
__s->left -= __size; \
64+
} \
65+
__s->left;})
2066

2167
/**
2268
* user_regset_active_fn - type of @active function in &struct user_regset
@@ -57,6 +103,10 @@ typedef int user_regset_get_fn(struct task_struct *target,
57103
unsigned int pos, unsigned int count,
58104
void *kbuf, void __user *ubuf);
59105

106+
typedef int user_regset_get2_fn(struct task_struct *target,
107+
const struct user_regset *regset,
108+
struct membuf to);
109+
60110
/**
61111
* user_regset_set_fn - type of @set function in &struct user_regset
62112
* @target: thread being examined
@@ -186,6 +236,7 @@ typedef unsigned int user_regset_get_size_fn(struct task_struct *target,
186236
*/
187237
struct user_regset {
188238
user_regset_get_fn *get;
239+
user_regset_get2_fn *regset_get;
189240
user_regset_set_fn *set;
190241
user_regset_active_fn *active;
191242
user_regset_writeback_fn *writeback;

kernel/regset.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ static int __regset_get(struct task_struct *target,
1111
void *p = *data, *to_free = NULL;
1212
int res;
1313

14-
if (!regset->get)
14+
if (!regset->get && !regset->regset_get)
1515
return -EOPNOTSUPP;
1616
if (size > regset->n * regset->size)
1717
size = regset->n * regset->size;
@@ -20,6 +20,16 @@ static int __regset_get(struct task_struct *target,
2020
if (!p)
2121
return -ENOMEM;
2222
}
23+
if (regset->regset_get) {
24+
res = regset->regset_get(target, regset,
25+
(struct membuf){.p = p, .left = size});
26+
if (res < 0) {
27+
kfree(to_free);
28+
return res;
29+
}
30+
*data = p;
31+
return size - res;
32+
}
2333
res = regset->get(target, regset, 0, size, p, NULL);
2434
if (unlikely(res < 0)) {
2535
kfree(to_free);

0 commit comments

Comments
 (0)