Re: Equivalent of Linux kmap on Mac OS
Re: Equivalent of Linux kmap on Mac OS
- Subject: Re: Equivalent of Linux kmap on Mac OS
- From: Anton Altaparmakov <email@hidden>
- Date: Wed, 7 Mar 2007 11:45:52 +0000
On 7 Mar 2007, at 11:32, Michael Smith wrote:
On Mar 6, 2007, at 4:35 PM, Bhavesh Davda wrote:
What's the equivalent of Linux's kmap to map a user virtual address
into the kernel memory space so that it is accessible "magically"
(i.e. without any additional mapping/unmapping required) to all other
tasks and kernel threads?
What do you mean by 'accessible "magically"'?
Are you just trying to make a portion of a user map visible in the
kernel map, or are you trying to project that space into other maps
as well? From what I can tell, the Linux 'kmap' call just maps a
single user page somewhere into the kernel address space, so I'm
assuming that you mean "so that other processes can call code in
the kernel and see this page from there".
No that is not kmap does. kmap is used to map the data content of a
"struct page" (i.e. a page in the VM) into the kernel address space
for access. I will give a concrete example from the Linux NTFS file
system driver and show you the equivalent code from the OSX NTFS
driver. If Bhavesh is asking about a file system then this is the
answer he is looking for...
Ok, on Linux in NTFS we map the content of a page at offset OFS of a
file using (note index = OFS >> PAGE_CACHE_SHIFT):
static inline struct page *ntfs_map_page(struct address_space *mapping,
pgoff_t index)
{
struct page *page;
page = read_mapping_page(mapping, index, NULL);
if (!IS_ERR(page)) {
wait_on_page_locked(page);
kmap(page);
if (PageUptodate(page) && !PageError(page))
return page;
ntfs_unmap_page(page);
return ERR_PTR(-EIO);
}
return page;
}
The important point of note is that once we have the struct page we
run "kmap(page);" after which we can call "page_address(page)" to
obtain the virtual memory pointer at which we can read/write the data
content of the page.
And now on OSX we do the same but it is a bit more involved (I have
oversimplified the code for simplicity - we only really care about
mapping the page anyway):
errno_t ntfs_page_map_ext(ntfs_inode *ni, s64 ofs, upl_t *upl,
upl_page_info_array_t *pl, u8 **kaddr, const BOOL rw)
{
s64 size;
kern_return_t kerr;
int abort_flags;
errno_t err;
if (ofs & PAGE_MASK)
panic("%s() called with non page aligned offset (0x%
llx).",
__FUNCTION__, (unsigned long long)ofs);
size = ubc_getsize(ni->vn);
if (ofs > size) {
ntfs_error(ni->vol->mp, "Offset 0x%llx is outside
the end of "
"the attribute (0x%llx).",
(unsigned long long)ofs,
(unsigned long long)size);
return EINVAL;
}
/* Create a page list for the wanted page. */
kerr = ubc_create_upl(ni->vn, ofs, PAGE_SIZE, upl, pl,
UPL_SET_LITE |
(rw ? UPL_WILL_MODIFY : 0));
if (kerr != KERN_SUCCESS)
panic("%s(): Failed to get page (error %d).\n",
__FUNCTION__,
(int)kerr);
/*
* If the page is not valid, need to read it in from the
vnode now thus
* making it valid.
*/
if (!upl_valid_page(*pl, 0)) {
ntfs_debug("Reading page as it was not valid.");
err = ntfs_pagein(ni, ofs, PAGE_SIZE, *upl, 0,
UPL_IOSYNC | UPL_NOCOMMIT);
if (err) {
ntfs_error(ni->vol->mp, "Failed to read page
(error "
"%d).", err);
goto pagein_err;
}
}
/* Map the page into the kernel's address space. */
kerr = ubc_upl_map(*upl, (vm_offset_t*)kaddr);
if (kerr == KERN_SUCCESS) {
ntfs_debug("Done.");
return 0;
}
ntfs_error(ni->vol->mp, "Failed to map page (error %d).",
(int)kerr);
err = EIO;
pagein_err:
abort_flags = UPL_ABORT_FREE_ON_EMPTY;
if (!upl_valid_page(*pl, 0) ||
(vnode_isnocache(ni->vn) && !upl_dirty_page
(*pl, 0)))
abort_flags |= UPL_ABORT_DUMP_PAGES;
ubc_upl_abort_range(*upl, 0, PAGE_SIZE, abort_flags);
return err;
}
The important bit is ubc_upl_map() which maps all the pages in the
UPL *upl into a linear virtual memory region which the start address
of which will be returned in *kaddr. (i.e. *kaddr == page_address
(page) on Linux).
If you are not working with a file system then I would suggest
checking out what ubc_upl_map() does and following it up that way to
figure out what to do analogously.
Best regards,
Anton
Typically, you don't want to map user data into the kernel in the
first place; it's expensive and kernel VM is a scarce resource -
typically you will find it's faster just to copy the page in/out.
If you are trying to establish a shared page for the purposes of
communication, I'd be inclined to encourage you to consider the
system's native communication primitives instead. There are lots
of ways of moving map entries around depending on what you're
trying to achieve.
I've already looked at vm_map and AFAICT, that ain't it.
If you're doing this in an I/O Kit kext, the canonical tool for
handling memory in another address space is the
IOMemoryDescriptor. You can use this to map said memory, if you
really need it.
= Mike
--
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer, http://www.linux-ntfs.org/
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Darwin-kernel mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden