Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/s390/include/asm/idals.h
10818 views
1
/*
2
* File...........: linux/include/asm-s390x/idals.h
3
* Author(s)......: Holger Smolinski <[email protected]>
4
* Martin Schwidefsky <[email protected]>
5
* Bugreports.to..: <[email protected]>
6
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000a
7
8
* History of changes
9
* 07/24/00 new file
10
* 05/04/02 code restructuring.
11
*/
12
13
#ifndef _S390_IDALS_H
14
#define _S390_IDALS_H
15
16
#include <linux/errno.h>
17
#include <linux/err.h>
18
#include <linux/types.h>
19
#include <linux/slab.h>
20
#include <asm/cio.h>
21
#include <asm/uaccess.h>
22
23
#ifdef __s390x__
24
#define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */
25
#else
26
#define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */
27
#endif
28
#define IDA_BLOCK_SIZE (1L<<IDA_SIZE_LOG)
29
30
/*
31
* Test if an address/length pair needs an idal list.
32
*/
33
static inline int
34
idal_is_needed(void *vaddr, unsigned int length)
35
{
36
#ifdef __s390x__
37
return ((__pa(vaddr) + length - 1) >> 31) != 0;
38
#else
39
return 0;
40
#endif
41
}
42
43
44
/*
45
* Return the number of idal words needed for an address/length pair.
46
*/
47
static inline unsigned int idal_nr_words(void *vaddr, unsigned int length)
48
{
49
return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length +
50
(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
51
}
52
53
/*
54
* Create the list of idal words for an address/length pair.
55
*/
56
static inline unsigned long *idal_create_words(unsigned long *idaws,
57
void *vaddr, unsigned int length)
58
{
59
unsigned long paddr;
60
unsigned int cidaw;
61
62
paddr = __pa(vaddr);
63
cidaw = ((paddr & (IDA_BLOCK_SIZE-1)) + length +
64
(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
65
*idaws++ = paddr;
66
paddr &= -IDA_BLOCK_SIZE;
67
while (--cidaw > 0) {
68
paddr += IDA_BLOCK_SIZE;
69
*idaws++ = paddr;
70
}
71
return idaws;
72
}
73
74
/*
75
* Sets the address of the data in CCW.
76
* If necessary it allocates an IDAL and sets the appropriate flags.
77
*/
78
static inline int
79
set_normalized_cda(struct ccw1 * ccw, void *vaddr)
80
{
81
#ifdef __s390x__
82
unsigned int nridaws;
83
unsigned long *idal;
84
85
if (ccw->flags & CCW_FLAG_IDA)
86
return -EINVAL;
87
nridaws = idal_nr_words(vaddr, ccw->count);
88
if (nridaws > 0) {
89
idal = kmalloc(nridaws * sizeof(unsigned long),
90
GFP_ATOMIC | GFP_DMA );
91
if (idal == NULL)
92
return -ENOMEM;
93
idal_create_words(idal, vaddr, ccw->count);
94
ccw->flags |= CCW_FLAG_IDA;
95
vaddr = idal;
96
}
97
#endif
98
ccw->cda = (__u32)(unsigned long) vaddr;
99
return 0;
100
}
101
102
/*
103
* Releases any allocated IDAL related to the CCW.
104
*/
105
static inline void
106
clear_normalized_cda(struct ccw1 * ccw)
107
{
108
#ifdef __s390x__
109
if (ccw->flags & CCW_FLAG_IDA) {
110
kfree((void *)(unsigned long) ccw->cda);
111
ccw->flags &= ~CCW_FLAG_IDA;
112
}
113
#endif
114
ccw->cda = 0;
115
}
116
117
/*
118
* Idal buffer extension
119
*/
120
struct idal_buffer {
121
size_t size;
122
size_t page_order;
123
void *data[0];
124
};
125
126
/*
127
* Allocate an idal buffer
128
*/
129
static inline struct idal_buffer *
130
idal_buffer_alloc(size_t size, int page_order)
131
{
132
struct idal_buffer *ib;
133
int nr_chunks, nr_ptrs, i;
134
135
nr_ptrs = (size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
136
nr_chunks = (4096 << page_order) >> IDA_SIZE_LOG;
137
ib = kmalloc(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *),
138
GFP_DMA | GFP_KERNEL);
139
if (ib == NULL)
140
return ERR_PTR(-ENOMEM);
141
ib->size = size;
142
ib->page_order = page_order;
143
for (i = 0; i < nr_ptrs; i++) {
144
if ((i & (nr_chunks - 1)) != 0) {
145
ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE;
146
continue;
147
}
148
ib->data[i] = (void *)
149
__get_free_pages(GFP_KERNEL, page_order);
150
if (ib->data[i] != NULL)
151
continue;
152
// Not enough memory
153
while (i >= nr_chunks) {
154
i -= nr_chunks;
155
free_pages((unsigned long) ib->data[i],
156
ib->page_order);
157
}
158
kfree(ib);
159
return ERR_PTR(-ENOMEM);
160
}
161
return ib;
162
}
163
164
/*
165
* Free an idal buffer.
166
*/
167
static inline void
168
idal_buffer_free(struct idal_buffer *ib)
169
{
170
int nr_chunks, nr_ptrs, i;
171
172
nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
173
nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG;
174
for (i = 0; i < nr_ptrs; i += nr_chunks)
175
free_pages((unsigned long) ib->data[i], ib->page_order);
176
kfree(ib);
177
}
178
179
/*
180
* Test if a idal list is really needed.
181
*/
182
static inline int
183
__idal_buffer_is_needed(struct idal_buffer *ib)
184
{
185
#ifdef __s390x__
186
return ib->size > (4096ul << ib->page_order) ||
187
idal_is_needed(ib->data[0], ib->size);
188
#else
189
return ib->size > (4096ul << ib->page_order);
190
#endif
191
}
192
193
/*
194
* Set channel data address to idal buffer.
195
*/
196
static inline void
197
idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw)
198
{
199
if (__idal_buffer_is_needed(ib)) {
200
// setup idals;
201
ccw->cda = (u32)(addr_t) ib->data;
202
ccw->flags |= CCW_FLAG_IDA;
203
} else
204
// we do not need idals - use direct addressing
205
ccw->cda = (u32)(addr_t) ib->data[0];
206
ccw->count = ib->size;
207
}
208
209
/*
210
* Copy count bytes from an idal buffer to user memory
211
*/
212
static inline size_t
213
idal_buffer_to_user(struct idal_buffer *ib, void __user *to, size_t count)
214
{
215
size_t left;
216
int i;
217
218
BUG_ON(count > ib->size);
219
for (i = 0; count > IDA_BLOCK_SIZE; i++) {
220
left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE);
221
if (left)
222
return left + count - IDA_BLOCK_SIZE;
223
to = (void __user *) to + IDA_BLOCK_SIZE;
224
count -= IDA_BLOCK_SIZE;
225
}
226
return copy_to_user(to, ib->data[i], count);
227
}
228
229
/*
230
* Copy count bytes from user memory to an idal buffer
231
*/
232
static inline size_t
233
idal_buffer_from_user(struct idal_buffer *ib, const void __user *from, size_t count)
234
{
235
size_t left;
236
int i;
237
238
BUG_ON(count > ib->size);
239
for (i = 0; count > IDA_BLOCK_SIZE; i++) {
240
left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE);
241
if (left)
242
return left + count - IDA_BLOCK_SIZE;
243
from = (void __user *) from + IDA_BLOCK_SIZE;
244
count -= IDA_BLOCK_SIZE;
245
}
246
return copy_from_user(ib->data[i], from, count);
247
}
248
249
#endif
250
251