Skip to content

Instantly share code, notes, and snippets.

@17twenty
Created June 14, 2012 13:58
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save 17twenty/2930467 to your computer and use it in GitHub Desktop.
Save 17twenty/2930467 to your computer and use it in GitHub Desktop.
Really simple example of using get_user_pages and memory aligned allocation using posix_memalign. Hope this helps!
diff -PurN dummy/gu_page.c get_user_pages/gu_page.c
--- dummy/gu_page.c 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/gu_page.c 2012-06-14 14:41:31.797310260 +0100
@@ -0,0 +1,124 @@
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+
+#include <linux/pagemap.h>
+
+#define LED_MAJOR 42
+#define DEVICE_NAME "simpleuser"
+
+/*
+ * Prototypes
+ */
+static int device_open(struct inode *, struct file *);
+static int device_release(struct inode *, struct file *);
+static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos);
+
+static struct file_operations led_ops = {
+ .owner = THIS_MODULE,
+ .open = device_open,
+ .release = device_release,
+ .write = device_write
+};
+
+static struct class *mmap_class;
+
+static int device_open(struct inode *inode, struct file *file)
+{
+ printk ("device_open: %d.%d\n", MAJOR (inode->i_rdev),
+ MINOR (inode->i_rdev));
+ return 0;
+}
+
+static int device_release(struct inode *inode, struct file *file)
+{
+ pr_info("device_release: %d.%d\n", MAJOR (inode->i_rdev),
+ MINOR (inode->i_rdev));
+ return 0;
+}
+
+static ssize_t device_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos)
+{
+ int res;
+ unsigned long uaddr;
+ char addrstr[80];
+ struct page *page;
+ char *my_page_address;
+
+ unsigned long copied = copy_from_user(addrstr, buf, sizeof(addrstr));
+ if (copied != 0)
+ {
+ pr_err("Told to copy %d, but only copied %lu\n", count, copied);
+ }
+ uaddr = simple_strtoul(addrstr, NULL, 0);
+
+ down_read(&current->mm->mmap_sem);
+ res = get_user_pages(current, current->mm,
+ uaddr,
+ 1, /* Only want one page */
+ 1, /* Do want to write into it */
+ 1, /* do force */
+ &page,
+ NULL);
+ if (res == 1) {
+ pr_err("Got page\n");
+ /* Do something with it */
+ my_page_address = kmap(page);
+ strcpy (my_page_address, "Hello, is it me you're looking for?\n");
+ pr_err("Got address %p and user told me it was %lx\n",my_page_address, uaddr);
+ pr_err("Wrote: %s", my_page_address);
+
+ kunmap(page);
+
+ /* Clean up */
+ if (!PageReserved(page))
+ SetPageDirty(page);
+ page_cache_release(page);
+ } else {
+ pr_err("Couldn't get page :(\n");
+ }
+ up_read(&current->mm->mmap_sem);
+
+
+ return count;
+}
+
+static int __init el504_init(void)
+{
+ int ret ;
+
+ /* Register the character device */
+ ret = register_chrdev (LED_MAJOR, DEVICE_NAME, &led_ops);
+ if (ret < 0) {
+ pr_alert("el504_init: failed with %d\n", ret);
+ return ret;
+ }
+
+ mmap_class = class_create (THIS_MODULE, "mmap");
+ device_create (mmap_class, NULL, MKDEV (LED_MAJOR, 0), NULL, "mmap");
+ return 0;
+}
+
+static void __exit el504_exit(void)
+{
+ pr_debug("Goodbye\n");
+ device_destroy (mmap_class, MKDEV (LED_MAJOR, 0));
+ class_destroy (mmap_class);
+ unregister_chrdev (LED_MAJOR, DEVICE_NAME);
+}
+
+module_init (el504_init);
+module_exit (el504_exit);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Nick Glynn");
+MODULE_DESCRIPTION ("Example for get_user_pages()");
+
diff -PurN dummy/Makefile get_user_pages/Makefile
--- dummy/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/Makefile 2012-06-14 14:21:11.271274011 +0100
@@ -0,0 +1,9 @@
+obj-m += gu_page.o
+KDIR := /lib/modules/$(shell uname -r)/build
+PWD := $(shell pwd)
+
+all:
+ $(MAKE) -C $(KDIR) M=$(PWD) modules
+clean:
+ $(MAKE) -C $(KDIR) M=$(PWD) clean
+
diff -PurN dummy/userspace/Makefile get_user_pages/userspace/Makefile
--- dummy/userspace/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/userspace/Makefile 2012-06-14 14:53:29.528816418 +0100
@@ -0,0 +1,8 @@
+BIN := userspace
+OBJECTS += $(BIN).c
+
+all:
+ gcc $(OBJECTS) -o $(BIN)
+
+clean:
+ rm $(BIN)
diff -PurN dummy/userspace/userspace.c get_user_pages/userspace/userspace.c
--- dummy/userspace/userspace.c 1970-01-01 01:00:00.000000000 +0100
+++ get_user_pages/userspace/userspace.c 2012-06-14 14:47:25.754137234 +0100
@@ -0,0 +1,51 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+int main (int argc, char *argv[])
+{
+ int mh, res;
+ char addrstr[80];
+ char *ptr, *bp;
+ const char *string = "Testing page";
+
+ printf ("%s\n", argv[0]);
+ if (argc < 2)
+ {
+ printf ("Usage: %s <file>\n", argv[0]);
+ return 1;
+ }
+ mh = open (argv [1], O_RDWR);
+ if (mh == -1)
+ {
+ printf ("Unable to open '%s'\n", argv [1]);
+ return 1;
+ }
+ /* Grab a page of memory (4096) and sprintf into it */
+
+ res = posix_memalign((void **)&ptr, 4096, 4096);
+
+ if (NULL != ptr)
+ {
+ sprintf(ptr, "%s", string);
+
+ bp = ptr;
+
+ /* Do a write to the kernel at which point it should mess with it */
+ sprintf(addrstr, "%p", ptr);
+ printf("Telling kernel it is %p\n", ptr);
+ res = write(mh, addrstr, strlen(addrstr));
+
+ /* See what lives there still */
+ printf ("Marker text 1: %s\n", ptr);
+ printf ("ptr = %p, bp = %p\n", ptr, bp);
+
+ }
+ close (mh);
+ return 0;
+}
@17twenty
Copy link
Author

The point of this code was to setup some page-aligned memory in user space, pass the address to the kernel and then have the kernel mess with it to highlight how you can use get_user_pages.

There's a lot of debuggy stuff in there and it's clearly sample code but I struggled to find decent examples on how to do what I wanted to do (allocate my own memory) so hopefully this should help some people out.

@milabs
Copy link

milabs commented Sep 4, 2013

@17twnty

...
unsigned long copied = copy_from_user(addrstr, buf, sizeof(addrstr));
if (copied != 0)
{
    pr_err("Told to copy %d, but only copied %lu\n", count, copied);
}
...

You'll need to check copy_from_user result for -EFAULT at least.

@anupgithub
Copy link

@17twenty. Thanks!....You are indeed helping out other!

@johnutz-PNSR
Copy link

And 8 years later somebody still wants to look at this! Thankyou so much for sharing this. I am grateful for it.

@paranlee
Copy link

@17twenty Thank you! I traced from LWM Zero Copy.
Latest drivers/scsi/st.c also helful :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment