I've been using this function in my linux device driver. This is to pin user pages for user virtual address (required number of pages) and get kernel address of it. By the ways, it was used in linux-5.4.21. (I think this method may have some problem. but it worked so I was using it)
static unsigned long uvirt_to_kvirt_ppin(unsigned long uvirt, unsigned long length, struct page *pages)
{
int res;
int offs;
uint64_t *kvpaddr;
uint64_t kvaddr;
uint64_t paddr;
offs = uvirt % PAGE_SIZE;
down_read(¤t->mm->mmap_sem);
res = get_user_pages( uvirt, length>>PAGE_SHIFT, FOLL_WRITE, &pages, NULL);
if (res) {
kvpaddr = kmap(pages);
kvaddr = ((unsigned long long int)(kvpaddr) offs);
paddr = page_to_phys(pages) offs;
}
else {
printk("get_user_pages failed! res = %x\n", res);
return -1;
}
up_read(¤t->mm->mmap_sem);
return kvaddr;
}
But today, when I was trying to build the module now against kernel 5.10.0-rc5, I met with this error message. This means as kernel was upgraded, the structure mm_struct was chagned.
/home/ckim/prj/abdsn/ab21sim/ab21tsim/QEMU/qemu_test/test_ldd_vanila/axpu_ldd_kc.c: In function ??uvirt_to_kvirt_ppin??:
/home/ckim/prj/abdsn/ab21sim/ab21tsim/QEMU/qemu_test/test_ldd_vanila/axpu_ldd_kc.c:246:26: error: ??struct mm_struct?? has no member named ??mmap_sem??; did you mean ??mmap_base???
246 | down_read(¤t->mm->mmap_sem);
| ^~~~~~~~
| mmap_base
/home/ckim/prj/abdsn/ab21sim/ab21tsim/QEMU/qemu_test/test_ldd_vanila/axpu_ldd_kc.c:257:27: error: ??struct mm_struct?? has no member named ??mmap_sem??; did you mean ??mmap_base???
257 | up_read(¤t->mm->mmap_sem);
| ^~~~~~~~
| mmap_base
How should I change it for 5.10.0-rc5? I searched the use of get_user_pages in 5.10.0-rc5 but there seems to be no locking around it. So, can I just remove the down_read and up_read lines? Probably I'll just go with it.(removing the lines).
CodePudding user response:
In Linux kernel 5.8, the mmap_sem
member of struct mm_struct
was renamed to mmap_lock
and a new mmap locking API was added.
You could do something like this:
#include <linux/mm.h>
#ifndef MMAP_LOCK_INITIALIZER
/* Define mmap locking API for pre-5.8 kernels. */
/* This one should not be needed in a driver. */
static inline void mmap_init_lock(struct mm_struct *mm)
{
init_rwsem(&mm->mmap_sem);
}
static inline void mmap_write_lock(struct mm_struct *mm)
{
down_write(&mm->mmap_sem);
}
static inline int mmap_write_lock_killable(struct mm_struct *mm)
{
return down_write_killable(&mm->mmap_sem);
}
static inline bool mmap_write_trylock(struct mm_struct *mm)
{
return down_write_trylock(&mm->mmap_sem) != 0;
}
static inline void mmap_write_unlock(struct mm_struct *mm)
{
up_write(&mm->mmap_sem);
}
static inline void mmap_write_downgrade(struct mm_struct *mm)
{
downgrade_write(&mm->mmap_sem);
}
static inline void mmap_read_lock(struct mm_struct *mm)
{
down_read(&mm->mmap_sem);
}
static inline int mmap_read_lock_killable(struct mm_struct *mm)
{
return down_read_killable(&mm->mmap_sem);
}
static inline bool mmap_read_trylock(struct mm_struct *mm)
{
return down_read_trylock(&mm->mmap_sem) != 0;
}
static inline void mmap_read_unlock(struct mm_struct *mm)
{
up_read(&mm->mmap_sem);
}
#endif /* MMAP_LOCK_INITIALIZER */
Then replace down_read(¤t->mm->mmap_sem);
with mmap_read_lock(current->mm);
and replace up_read(¤t->mm->mmap_sem);
with mmap_read_unlock(current->mm);
.