{
struct bzcopy_state *bz;
unsigned long addr;
- int done_pages;
+ int done_pages = 0;
int thresh;
+ mm_segment_t cur_fs;
- thresh = ssk->zcopy_thresh ? : sdp_zcopy_thresh;
- if (thresh == 0 || len < thresh)
- return NULL;
+ cur_fs = get_fs();
- if (!can_do_mlock())
+ thresh = ssk->zcopy_thresh ? : sdp_zcopy_thresh;
+ if (thresh == 0 || len < thresh || !capable(CAP_IPC_LOCK))
return NULL;
/*
bz = kzalloc(sizeof(*bz), GFP_KERNEL);
if (!bz)
- return NULL;
+ return ERR_PTR(-ENOMEM);
addr = (unsigned long)base;
bz->page_cnt = PAGE_ALIGN(len + bz->cur_offset) >> PAGE_SHIFT;
bz->pages = kcalloc(bz->page_cnt, sizeof(struct page *), GFP_KERNEL);
- if (!bz->pages)
- goto out_1;
-
- down_write(¤t->mm->mmap_sem);
+ if (!bz->pages) {
+ kfree(bz);
+ return ERR_PTR(-ENOMEM);
+ }
- if (!capable(CAP_IPC_LOCK))
- goto out_2;
addr &= PAGE_MASK;
-
- done_pages = get_user_pages(current, current->mm, addr, bz->page_cnt,
- 0, 0, bz->pages, NULL);
+ if (segment_eq(cur_fs, KERNEL_DS)) {
+ for (done_pages = 0; done_pages < bz->page_cnt; done_pages++) {
+ bz->pages[done_pages] = virt_to_page(addr);
+ if (!bz->pages[done_pages])
+ break;
+ get_page(bz->pages[done_pages]);
+ addr += PAGE_SIZE;
+ }
+ } else {
+ if (current->mm) {
+ down_write(¤t->mm->mmap_sem);
+ done_pages = get_user_pages(current, current->mm, addr,
+ bz->page_cnt, 0, 0, bz->pages, NULL);
+ up_write(¤t->mm->mmap_sem);
+ }
+ }
if (unlikely(done_pages != bz->page_cnt)){
- bz->page_cnt = done_pages;
- goto out_2;
+ int i;
+ if (done_pages > 0) {
+ for (i = 0; i < done_pages; i++)
+ put_page(bz->pages[i]);
+ }
+ kfree(bz->pages);
+ kfree(bz);
+ bz = ERR_PTR(-EFAULT);
}
- up_write(¤t->mm->mmap_sem);
-
return bz;
-
-out_2:
- up_write(¤t->mm->mmap_sem);
- kfree(bz->pages);
-out_1:
- kfree(bz);
-
- return NULL;
}
if (!sk_wmem_schedule(sk, copy))
return SDP_DO_WAIT_MEM;
+ get_page(bz->pages[bz->cur_page]);
skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
bz->pages[bz->cur_page], bz->cur_offset,
this_page);
BUG_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS);
+ BUG_ON(bz->cur_offset > PAGE_SIZE);
bz->cur_offset += this_page;
if (bz->cur_offset == PAGE_SIZE) {
bz->cur_page++;
BUG_ON(bz->cur_page > bz->page_cnt);
- } else {
- BUG_ON(bz->cur_offset > PAGE_SIZE);
-
- if (bz->cur_page != bz->page_cnt || left != this_page)
- get_page(bz->pages[bz->cur_page]);
}
left -= this_page;
if (size_goal > SDP_MAX_PAYLOAD)
size_goal = SDP_MAX_PAYLOAD;
+ if (bz)
+ sdp_bz_cleanup(bz);
bz = sdp_bz_setup(ssk, from, seglen, size_goal);
+ if (IS_ERR(bz)) {
+ bz = NULL;
+ err = PTR_ERR(bz);
+ goto do_error;
+ }
while (seglen > 0) {
int copy;