xen-vtx-unstable
annotate linux-2.6-xen-sparse/arch/xen/kernel/gnttab.c @ 6339:1d86fcb11b59
Grant table updates for block device changes.
- add gnttab_free_grant_reference
- make gnttab_free_grant_references interface consistent with other gnttab
functions
- add support for callback when references are freed
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
- add gnttab_free_grant_reference
- make gnttab_free_grant_references interface consistent with other gnttab
functions
- add support for callback when references are freed
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author | cl349@firebug.cl.cam.ac.uk |
---|---|
date | Mon Aug 22 20:52:38 2005 +0000 (2005-08-22) |
parents | f294acb25858 |
children | 2d3a7be68ba3 |
rev | line source |
---|---|
cl349@4693 | 1 /****************************************************************************** |
cl349@4693 | 2 * gnttab.c |
cl349@4693 | 3 * |
cl349@4693 | 4 * Two sets of functionality: |
cl349@4693 | 5 * 1. Granting foreign access to our memory reservation. |
cl349@4693 | 6 * 2. Accessing others' memory reservations via grant references. |
cl349@4693 | 7 * (i.e., mechanisms for both sender and recipient of grant references) |
cl349@4693 | 8 * |
cl349@4693 | 9 * Copyright (c) 2005, Christopher Clark |
cl349@4693 | 10 * Copyright (c) 2004, K A Fraser |
cl349@4693 | 11 */ |
cl349@4693 | 12 |
cl349@4693 | 13 #include <linux/config.h> |
cl349@4693 | 14 #include <linux/module.h> |
cl349@4693 | 15 #include <linux/sched.h> |
cl349@4693 | 16 #include <asm/pgtable.h> |
cl349@4693 | 17 #include <asm/fixmap.h> |
cl349@4693 | 18 #include <asm/uaccess.h> |
cl349@4693 | 19 #include <asm-xen/xen_proc.h> |
cl349@4693 | 20 #include <asm-xen/linux-public/privcmd.h> |
cl349@4693 | 21 #include <asm-xen/gnttab.h> |
kaf24@5402 | 22 #include <asm-xen/synch_bitops.h> |
cl349@4693 | 23 |
cl349@4693 | 24 #if 1 |
cl349@4693 | 25 #define ASSERT(_p) \ |
cl349@4693 | 26 if ( !(_p) ) { printk(KERN_ALERT"Assertion '%s': line %d, file %s\n", \ |
cl349@4693 | 27 #_p , __LINE__, __FILE__); *(int*)0=0; } |
cl349@4693 | 28 #else |
cl349@4693 | 29 #define ASSERT(_p) ((void)0) |
cl349@4693 | 30 #endif |
cl349@4693 | 31 |
cl349@4693 | 32 #define WPRINTK(fmt, args...) \ |
cl349@4693 | 33 printk(KERN_WARNING "xen_grant: " fmt, ##args) |
cl349@4693 | 34 |
cl349@4693 | 35 |
cl349@4693 | 36 EXPORT_SYMBOL(gnttab_grant_foreign_access); |
cl349@4693 | 37 EXPORT_SYMBOL(gnttab_end_foreign_access); |
cl349@4693 | 38 EXPORT_SYMBOL(gnttab_query_foreign_access); |
cl349@4693 | 39 EXPORT_SYMBOL(gnttab_grant_foreign_transfer); |
cl349@4693 | 40 EXPORT_SYMBOL(gnttab_end_foreign_transfer); |
cl349@4693 | 41 EXPORT_SYMBOL(gnttab_alloc_grant_references); |
cl349@4693 | 42 EXPORT_SYMBOL(gnttab_free_grant_references); |
cl349@4693 | 43 EXPORT_SYMBOL(gnttab_claim_grant_reference); |
cl349@4693 | 44 EXPORT_SYMBOL(gnttab_release_grant_reference); |
cl349@4693 | 45 EXPORT_SYMBOL(gnttab_grant_foreign_access_ref); |
cl349@4693 | 46 EXPORT_SYMBOL(gnttab_grant_foreign_transfer_ref); |
cl349@4693 | 47 |
cl349@4693 | 48 static grant_ref_t gnttab_free_list[NR_GRANT_ENTRIES]; |
cl349@4693 | 49 static grant_ref_t gnttab_free_head; |
cl349@4693 | 50 |
cl349@4693 | 51 static grant_entry_t *shared; |
cl349@4693 | 52 |
cl349@6339 | 53 static struct gnttab_free_callback *gnttab_free_callback_list = NULL; |
cl349@6339 | 54 |
cl349@4693 | 55 /* |
cl349@4693 | 56 * Lock-free grant-entry allocator |
cl349@4693 | 57 */ |
cl349@4693 | 58 |
cl349@4693 | 59 static inline int |
cl349@4693 | 60 get_free_entry( |
cl349@4693 | 61 void) |
cl349@4693 | 62 { |
cl349@4693 | 63 grant_ref_t fh, nfh = gnttab_free_head; |
cl349@4693 | 64 do { if ( unlikely((fh = nfh) == NR_GRANT_ENTRIES) ) return -1; } |
cl349@4693 | 65 while ( unlikely((nfh = cmpxchg(&gnttab_free_head, fh, |
cl349@4693 | 66 gnttab_free_list[fh])) != fh) ); |
cl349@4693 | 67 return fh; |
cl349@4693 | 68 } |
cl349@4693 | 69 |
cl349@6339 | 70 static void do_free_callbacks(void) |
cl349@6339 | 71 { |
cl349@6339 | 72 struct gnttab_free_callback *callback = gnttab_free_callback_list; |
cl349@6339 | 73 gnttab_free_callback_list = NULL; |
cl349@6339 | 74 while (callback) { |
cl349@6339 | 75 schedule_work(callback->work); |
cl349@6339 | 76 callback = callback->next; |
cl349@6339 | 77 } |
cl349@6339 | 78 } |
cl349@6339 | 79 |
cl349@4693 | 80 static inline void |
cl349@4693 | 81 put_free_entry( |
cl349@4693 | 82 grant_ref_t ref) |
cl349@4693 | 83 { |
cl349@4693 | 84 grant_ref_t fh, nfh = gnttab_free_head; |
cl349@4693 | 85 do { gnttab_free_list[ref] = fh = nfh; wmb(); } |
cl349@4693 | 86 while ( unlikely((nfh = cmpxchg(&gnttab_free_head, fh, ref)) != fh) ); |
cl349@6339 | 87 if ( unlikely(gnttab_free_callback_list) ) |
cl349@6339 | 88 do_free_callbacks(); |
cl349@4693 | 89 } |
cl349@4693 | 90 |
cl349@4693 | 91 /* |
cl349@4693 | 92 * Public grant-issuing interface functions |
cl349@4693 | 93 */ |
cl349@4693 | 94 |
cl349@4693 | 95 int |
cl349@4693 | 96 gnttab_grant_foreign_access( |
cl349@4693 | 97 domid_t domid, unsigned long frame, int readonly) |
cl349@4693 | 98 { |
cl349@4693 | 99 int ref; |
cl349@4693 | 100 |
cl349@4693 | 101 if ( unlikely((ref = get_free_entry()) == -1) ) |
cl349@4693 | 102 return -ENOSPC; |
cl349@4693 | 103 |
cl349@4693 | 104 shared[ref].frame = frame; |
cl349@4693 | 105 shared[ref].domid = domid; |
cl349@4693 | 106 wmb(); |
cl349@4693 | 107 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0); |
cl349@4693 | 108 |
cl349@4693 | 109 return ref; |
cl349@4693 | 110 } |
cl349@4693 | 111 |
cl349@4693 | 112 void |
cl349@4693 | 113 gnttab_grant_foreign_access_ref( |
cl349@4693 | 114 grant_ref_t ref, domid_t domid, unsigned long frame, int readonly) |
cl349@4693 | 115 { |
cl349@4693 | 116 shared[ref].frame = frame; |
cl349@4693 | 117 shared[ref].domid = domid; |
cl349@4693 | 118 wmb(); |
cl349@4693 | 119 shared[ref].flags = GTF_permit_access | (readonly ? GTF_readonly : 0); |
cl349@4693 | 120 } |
cl349@4693 | 121 |
cl349@4693 | 122 |
cl349@4693 | 123 int |
cl349@4693 | 124 gnttab_query_foreign_access( grant_ref_t ref ) |
cl349@4693 | 125 { |
cl349@4693 | 126 u16 nflags; |
cl349@4693 | 127 |
cl349@4693 | 128 nflags = shared[ref].flags; |
cl349@4693 | 129 |
cl349@4693 | 130 return ( nflags & (GTF_reading|GTF_writing) ); |
cl349@4693 | 131 } |
cl349@4693 | 132 |
cl349@4693 | 133 void |
cl349@4693 | 134 gnttab_end_foreign_access( grant_ref_t ref, int readonly ) |
cl349@4693 | 135 { |
cl349@4693 | 136 u16 flags, nflags; |
cl349@4693 | 137 |
cl349@4693 | 138 nflags = shared[ref].flags; |
cl349@4693 | 139 do { |
cl349@4693 | 140 if ( (flags = nflags) & (GTF_reading|GTF_writing) ) |
cl349@4693 | 141 printk(KERN_ALERT "WARNING: g.e. still in use!\n"); |
cl349@4693 | 142 } |
sos22@5391 | 143 while ( (nflags = synch_cmpxchg(&shared[ref].flags, flags, 0)) != flags ); |
cl349@4693 | 144 |
cl349@4693 | 145 put_free_entry(ref); |
cl349@4693 | 146 } |
cl349@4693 | 147 |
cl349@4693 | 148 int |
cl349@4693 | 149 gnttab_grant_foreign_transfer( |
cl349@4693 | 150 domid_t domid, unsigned long pfn ) |
cl349@4693 | 151 { |
cl349@4693 | 152 int ref; |
cl349@4693 | 153 |
cl349@4693 | 154 if ( unlikely((ref = get_free_entry()) == -1) ) |
cl349@4693 | 155 return -ENOSPC; |
cl349@4693 | 156 |
cl349@4693 | 157 shared[ref].frame = pfn; |
cl349@4693 | 158 shared[ref].domid = domid; |
cl349@4693 | 159 wmb(); |
cl349@4693 | 160 shared[ref].flags = GTF_accept_transfer; |
cl349@4693 | 161 |
cl349@4693 | 162 return ref; |
cl349@4693 | 163 } |
cl349@4693 | 164 |
cl349@4693 | 165 void |
cl349@4693 | 166 gnttab_grant_foreign_transfer_ref( |
cl349@4693 | 167 grant_ref_t ref, domid_t domid, unsigned long pfn ) |
cl349@4693 | 168 { |
cl349@4693 | 169 shared[ref].frame = pfn; |
cl349@4693 | 170 shared[ref].domid = domid; |
cl349@4693 | 171 wmb(); |
cl349@4693 | 172 shared[ref].flags = GTF_accept_transfer; |
cl349@4693 | 173 } |
cl349@4693 | 174 |
cl349@4693 | 175 unsigned long |
cl349@4693 | 176 gnttab_end_foreign_transfer( |
cl349@4693 | 177 grant_ref_t ref) |
cl349@4693 | 178 { |
cl349@4693 | 179 unsigned long frame = 0; |
cl349@4693 | 180 u16 flags; |
cl349@4693 | 181 |
cl349@4693 | 182 flags = shared[ref].flags; |
vh249@5828 | 183 #ifdef CONFIG_XEN_NETDEV_GRANT_RX |
vh249@5828 | 184 /* |
vh249@5828 | 185 * But can't flags == (GTF_accept_transfer | GTF_transfer_completed) |
vh249@5828 | 186 * if gnttab_donate executes without interruption??? |
vh249@5828 | 187 */ |
vh249@5828 | 188 #else |
cl349@4693 | 189 ASSERT(flags == (GTF_accept_transfer | GTF_transfer_committed)); |
vh249@5828 | 190 #endif |
cl349@4693 | 191 /* |
cl349@4693 | 192 * If a transfer is committed then wait for the frame address to appear. |
cl349@4693 | 193 * Otherwise invalidate the grant entry against future use. |
cl349@4693 | 194 */ |
cl349@4693 | 195 if ( likely(flags != GTF_accept_transfer) || |
sos22@5391 | 196 (synch_cmpxchg(&shared[ref].flags, flags, 0) != GTF_accept_transfer) ) |
cl349@4693 | 197 while ( unlikely((frame = shared[ref].frame) == 0) ) |
cl349@4693 | 198 cpu_relax(); |
cl349@4693 | 199 |
cl349@4693 | 200 put_free_entry(ref); |
cl349@4693 | 201 |
cl349@4693 | 202 return frame; |
cl349@4693 | 203 } |
cl349@4693 | 204 |
cl349@4693 | 205 void |
cl349@6339 | 206 gnttab_free_grant_reference( grant_ref_t ref ) |
cl349@6339 | 207 { |
cl349@6339 | 208 |
cl349@6339 | 209 put_free_entry(ref); |
cl349@6339 | 210 } |
cl349@6339 | 211 |
cl349@6339 | 212 void |
cl349@6339 | 213 gnttab_free_grant_references( grant_ref_t *head, |
cl349@6339 | 214 grant_ref_t terminal ) |
cl349@4693 | 215 { |
cl349@4693 | 216 /* TODO: O(N)...? */ |
cl349@6339 | 217 grant_ref_t ref; |
cl349@4693 | 218 |
cl349@6339 | 219 while (*head != terminal) { |
cl349@6339 | 220 ref = *head; |
cl349@6339 | 221 *head = gnttab_free_list[*head]; |
cl349@6339 | 222 put_free_entry(ref); |
vh249@5272 | 223 } |
cl349@4693 | 224 } |
cl349@4693 | 225 |
cl349@4693 | 226 int |
cl349@4693 | 227 gnttab_alloc_grant_references( u16 count, |
cl349@4693 | 228 grant_ref_t *head, |
cl349@4693 | 229 grant_ref_t *terminal ) |
cl349@4693 | 230 { |
cl349@4693 | 231 int i; |
cl349@4693 | 232 grant_ref_t h = gnttab_free_head; |
cl349@4693 | 233 |
cl349@4693 | 234 for ( i = 0; i < count; i++ ) |
cl349@4693 | 235 if ( unlikely(get_free_entry() == -1) ) |
cl349@4693 | 236 goto not_enough_refs; |
cl349@4693 | 237 |
cl349@4693 | 238 *head = h; |
cl349@4693 | 239 *terminal = gnttab_free_head; |
cl349@4693 | 240 |
cl349@4693 | 241 return 0; |
cl349@4693 | 242 |
cl349@4693 | 243 not_enough_refs: |
cl349@4693 | 244 gnttab_free_head = h; |
cl349@4693 | 245 return -ENOSPC; |
cl349@4693 | 246 } |
cl349@4693 | 247 |
cl349@4693 | 248 int |
cl349@4693 | 249 gnttab_claim_grant_reference( grant_ref_t *private_head, |
cl349@4693 | 250 grant_ref_t terminal ) |
cl349@4693 | 251 { |
cl349@4693 | 252 grant_ref_t g; |
cl349@4693 | 253 if ( unlikely((g = *private_head) == terminal) ) |
cl349@4693 | 254 return -ENOSPC; |
cl349@4693 | 255 *private_head = gnttab_free_list[g]; |
cl349@4693 | 256 return g; |
cl349@4693 | 257 } |
cl349@4693 | 258 |
cl349@4693 | 259 void |
cl349@4693 | 260 gnttab_release_grant_reference( grant_ref_t *private_head, |
cl349@4693 | 261 grant_ref_t release ) |
cl349@4693 | 262 { |
cl349@4693 | 263 gnttab_free_list[release] = *private_head; |
cl349@4693 | 264 *private_head = release; |
cl349@4693 | 265 } |
cl349@4693 | 266 |
cl349@6339 | 267 void |
cl349@6339 | 268 gnttab_request_free_callback(struct gnttab_free_callback *callback, |
cl349@6339 | 269 struct work_struct *work) |
cl349@6339 | 270 { |
cl349@6339 | 271 callback->work = work; |
cl349@6339 | 272 callback->next = gnttab_free_callback_list; |
cl349@6339 | 273 gnttab_free_callback_list = callback; |
cl349@6339 | 274 } |
cl349@6339 | 275 |
cl349@4693 | 276 /* |
cl349@4693 | 277 * ProcFS operations |
cl349@4693 | 278 */ |
cl349@4693 | 279 |
cl349@4693 | 280 #ifdef CONFIG_PROC_FS |
cl349@4693 | 281 |
cl349@4693 | 282 static struct proc_dir_entry *grant_pde; |
cl349@4693 | 283 |
cl349@4693 | 284 static int grant_ioctl(struct inode *inode, struct file *file, |
cl349@4693 | 285 unsigned int cmd, unsigned long data) |
cl349@4693 | 286 { |
cl349@4693 | 287 int ret; |
cl349@4693 | 288 privcmd_hypercall_t hypercall; |
cl349@4693 | 289 |
cl349@4693 | 290 /* XXX Need safety checks here if using for anything other |
cl349@4693 | 291 * than debugging */ |
cl349@4693 | 292 return -ENOSYS; |
cl349@4693 | 293 |
cl349@4693 | 294 if ( cmd != IOCTL_PRIVCMD_HYPERCALL ) |
cl349@4693 | 295 return -ENOSYS; |
cl349@4693 | 296 |
cl349@4693 | 297 if ( copy_from_user(&hypercall, (void *)data, sizeof(hypercall)) ) |
cl349@4693 | 298 return -EFAULT; |
cl349@4693 | 299 |
cl349@4693 | 300 if ( hypercall.op != __HYPERVISOR_grant_table_op ) |
cl349@4693 | 301 return -ENOSYS; |
cl349@4693 | 302 |
cl349@4693 | 303 /* hypercall-invoking asm taken from privcmd.c */ |
cl349@4693 | 304 __asm__ __volatile__ ( |
cl349@4693 | 305 "pushl %%ebx; pushl %%ecx; pushl %%edx; pushl %%esi; pushl %%edi; " |
cl349@4693 | 306 "movl 4(%%eax),%%ebx ;" |
cl349@4693 | 307 "movl 8(%%eax),%%ecx ;" |
cl349@4693 | 308 "movl 12(%%eax),%%edx ;" |
cl349@4693 | 309 "movl 16(%%eax),%%esi ;" |
cl349@4693 | 310 "movl 20(%%eax),%%edi ;" |
cl349@4693 | 311 "movl (%%eax),%%eax ;" |
cl349@4693 | 312 TRAP_INSTR "; " |
cl349@4693 | 313 "popl %%edi; popl %%esi; popl %%edx; popl %%ecx; popl %%ebx" |
cl349@4693 | 314 : "=a" (ret) : "0" (&hypercall) : "memory" ); |
cl349@4693 | 315 |
cl349@4693 | 316 return ret; |
cl349@4693 | 317 } |
cl349@4693 | 318 |
cl349@4693 | 319 static struct file_operations grant_file_ops = { |
cl349@4693 | 320 ioctl: grant_ioctl, |
cl349@4693 | 321 }; |
cl349@4693 | 322 |
cl349@4693 | 323 static int grant_read(char *page, char **start, off_t off, |
cl349@4693 | 324 int count, int *eof, void *data) |
cl349@4693 | 325 { |
cl349@4693 | 326 int len; |
cl349@4693 | 327 unsigned int i; |
cl349@4693 | 328 grant_entry_t *gt; |
cl349@4693 | 329 |
cl349@4693 | 330 gt = (grant_entry_t *)shared; |
cl349@4693 | 331 len = 0; |
cl349@4693 | 332 |
cl349@4693 | 333 for ( i = 0; i < NR_GRANT_ENTRIES; i++ ) |
cl349@4693 | 334 /* TODO: safety catch here until this can handle >PAGE_SIZE output */ |
cl349@4693 | 335 if (len > (PAGE_SIZE - 200)) |
cl349@4693 | 336 { |
cl349@4693 | 337 len += sprintf( page + len, "Truncated.\n"); |
cl349@4693 | 338 break; |
cl349@4693 | 339 } |
cl349@4693 | 340 |
cl349@4693 | 341 if ( gt[i].flags ) |
cl349@4693 | 342 len += sprintf( page + len, |
cl349@4693 | 343 "Grant: ref (0x%x) flags (0x%hx) dom (0x%hx) frame (0x%x)\n", |
cl349@4693 | 344 i, |
cl349@4693 | 345 gt[i].flags, |
cl349@4693 | 346 gt[i].domid, |
cl349@4693 | 347 gt[i].frame ); |
cl349@4693 | 348 |
cl349@4693 | 349 *eof = 1; |
cl349@4693 | 350 return len; |
cl349@4693 | 351 } |
cl349@4693 | 352 |
cl349@4693 | 353 static int grant_write(struct file *file, const char __user *buffer, |
cl349@4693 | 354 unsigned long count, void *data) |
cl349@4693 | 355 { |
cl349@4693 | 356 /* TODO: implement this */ |
cl349@4693 | 357 return -ENOSYS; |
cl349@4693 | 358 } |
cl349@4693 | 359 |
cl349@4693 | 360 #endif /* CONFIG_PROC_FS */ |
cl349@4693 | 361 |
cl349@4693 | 362 int gnttab_resume(void) |
cl349@4693 | 363 { |
cl349@4693 | 364 gnttab_setup_table_t setup; |
cl349@4693 | 365 unsigned long frames[NR_GRANT_FRAMES]; |
cl349@4693 | 366 int i; |
cl349@4693 | 367 |
cl349@4693 | 368 setup.dom = DOMID_SELF; |
cl349@4693 | 369 setup.nr_frames = NR_GRANT_FRAMES; |
cl349@4693 | 370 setup.frame_list = frames; |
cl349@4693 | 371 |
cl349@4693 | 372 BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1) != 0); |
cl349@4693 | 373 BUG_ON(setup.status != 0); |
cl349@4693 | 374 |
cl349@4693 | 375 for ( i = 0; i < NR_GRANT_FRAMES; i++ ) |
cl349@4777 | 376 set_fixmap(FIX_GNTTAB_END - i, frames[i] << PAGE_SHIFT); |
cl349@4693 | 377 |
cl349@4693 | 378 return 0; |
cl349@4693 | 379 } |
cl349@4693 | 380 |
cl349@4693 | 381 int gnttab_suspend(void) |
cl349@4693 | 382 { |
cl349@4693 | 383 int i; |
cl349@4693 | 384 |
cl349@4693 | 385 for ( i = 0; i < NR_GRANT_FRAMES; i++ ) |
cl349@4693 | 386 clear_fixmap(FIX_GNTTAB_END - i); |
cl349@4693 | 387 |
cl349@4693 | 388 return 0; |
cl349@4693 | 389 } |
cl349@4693 | 390 |
cl349@4693 | 391 static int __init gnttab_init(void) |
cl349@4693 | 392 { |
cl349@4693 | 393 int i; |
cl349@4693 | 394 |
cl349@4693 | 395 BUG_ON(gnttab_resume()); |
cl349@4693 | 396 |
cl349@4693 | 397 shared = (grant_entry_t *)fix_to_virt(FIX_GNTTAB_END); |
cl349@4693 | 398 |
cl349@4693 | 399 for ( i = 0; i < NR_GRANT_ENTRIES; i++ ) |
cl349@4693 | 400 gnttab_free_list[i] = i + 1; |
cl349@4693 | 401 |
cl349@4693 | 402 #ifdef CONFIG_PROC_FS |
cl349@4693 | 403 /* |
cl349@4693 | 404 * /proc/xen/grant : used by libxc to access grant tables |
cl349@4693 | 405 */ |
cl349@4693 | 406 if ( (grant_pde = create_xen_proc_entry("grant", 0600)) == NULL ) |
cl349@4693 | 407 { |
cl349@4693 | 408 WPRINTK("Unable to create grant xen proc entry\n"); |
cl349@4693 | 409 return -1; |
cl349@4693 | 410 } |
cl349@4693 | 411 |
cl349@4693 | 412 grant_file_ops.read = grant_pde->proc_fops->read; |
cl349@4693 | 413 grant_file_ops.write = grant_pde->proc_fops->write; |
cl349@4693 | 414 |
cl349@4693 | 415 grant_pde->proc_fops = &grant_file_ops; |
cl349@4693 | 416 |
cl349@4693 | 417 grant_pde->read_proc = &grant_read; |
cl349@4693 | 418 grant_pde->write_proc = &grant_write; |
cl349@4693 | 419 #endif |
cl349@4693 | 420 |
cl349@4693 | 421 printk("Grant table initialized\n"); |
cl349@4693 | 422 return 0; |
cl349@4693 | 423 } |
cl349@4693 | 424 |
cl349@4693 | 425 __initcall(gnttab_init); |