diff options
-rw-r--r-- | net/packet/af_packet.c | 85 |
1 files changed, 69 insertions, 16 deletions
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 8298e676f5a0..20964560a0ed 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c | |||
@@ -61,6 +61,7 @@ | |||
61 | #include <linux/kernel.h> | 61 | #include <linux/kernel.h> |
62 | #include <linux/kmod.h> | 62 | #include <linux/kmod.h> |
63 | #include <linux/slab.h> | 63 | #include <linux/slab.h> |
64 | #include <linux/vmalloc.h> | ||
64 | #include <net/net_namespace.h> | 65 | #include <net/net_namespace.h> |
65 | #include <net/ip.h> | 66 | #include <net/ip.h> |
66 | #include <net/protocol.h> | 67 | #include <net/protocol.h> |
@@ -163,8 +164,14 @@ struct packet_mreq_max { | |||
163 | static int packet_set_ring(struct sock *sk, struct tpacket_req *req, | 164 | static int packet_set_ring(struct sock *sk, struct tpacket_req *req, |
164 | int closing, int tx_ring); | 165 | int closing, int tx_ring); |
165 | 166 | ||
167 | #define PGV_FROM_VMALLOC 1 | ||
168 | struct pgv { | ||
169 | char *buffer; | ||
170 | unsigned char flags; | ||
171 | }; | ||
172 | |||
166 | struct packet_ring_buffer { | 173 | struct packet_ring_buffer { |
167 | char **pg_vec; | 174 | struct pgv *pg_vec; |
168 | unsigned int head; | 175 | unsigned int head; |
169 | unsigned int frames_per_block; | 176 | unsigned int frames_per_block; |
170 | unsigned int frame_size; | 177 | unsigned int frame_size; |
@@ -283,7 +290,8 @@ static void *packet_lookup_frame(struct packet_sock *po, | |||
283 | pg_vec_pos = position / rb->frames_per_block; | 290 | pg_vec_pos = position / rb->frames_per_block; |
284 | frame_offset = position % rb->frames_per_block; | 291 | frame_offset = position % rb->frames_per_block; |
285 | 292 | ||
286 | h.raw = rb->pg_vec[pg_vec_pos] + (frame_offset * rb->frame_size); | 293 | h.raw = rb->pg_vec[pg_vec_pos].buffer + |
294 | (frame_offset * rb->frame_size); | ||
287 | 295 | ||
288 | if (status != __packet_get_status(po, h.raw)) | 296 | if (status != __packet_get_status(po, h.raw)) |
289 | return NULL; | 297 | return NULL; |
@@ -2325,37 +2333,74 @@ static const struct vm_operations_struct packet_mmap_ops = { | |||
2325 | .close = packet_mm_close, | 2333 | .close = packet_mm_close, |
2326 | }; | 2334 | }; |
2327 | 2335 | ||
2328 | static void free_pg_vec(char **pg_vec, unsigned int order, unsigned int len) | 2336 | static void free_pg_vec(struct pgv *pg_vec, unsigned int order, |
2337 | unsigned int len) | ||
2329 | { | 2338 | { |
2330 | int i; | 2339 | int i; |
2331 | 2340 | ||
2332 | for (i = 0; i < len; i++) { | 2341 | for (i = 0; i < len; i++) { |
2333 | if (likely(pg_vec[i])) | 2342 | if (likely(pg_vec[i].buffer)) { |
2334 | free_pages((unsigned long) pg_vec[i], order); | 2343 | if (pg_vec[i].flags & PGV_FROM_VMALLOC) |
2344 | vfree(pg_vec[i].buffer); | ||
2345 | else | ||
2346 | free_pages((unsigned long)pg_vec[i].buffer, | ||
2347 | order); | ||
2348 | pg_vec[i].buffer = NULL; | ||
2349 | } | ||
2335 | } | 2350 | } |
2336 | kfree(pg_vec); | 2351 | kfree(pg_vec); |
2337 | } | 2352 | } |
2338 | 2353 | ||
2339 | static inline char *alloc_one_pg_vec_page(unsigned long order) | 2354 | static inline char *alloc_one_pg_vec_page(unsigned long order, |
2355 | unsigned char *flags) | ||
2340 | { | 2356 | { |
2341 | gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | __GFP_ZERO | __GFP_NOWARN; | 2357 | char *buffer = NULL; |
2358 | gfp_t gfp_flags = GFP_KERNEL | __GFP_COMP | | ||
2359 | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY; | ||
2360 | |||
2361 | buffer = (char *) __get_free_pages(gfp_flags, order); | ||
2362 | |||
2363 | if (buffer) | ||
2364 | return buffer; | ||
2365 | |||
2366 | /* | ||
2367 | * __get_free_pages failed, fall back to vmalloc | ||
2368 | */ | ||
2369 | *flags |= PGV_FROM_VMALLOC; | ||
2370 | buffer = vmalloc((1 << order) * PAGE_SIZE); | ||
2342 | 2371 | ||
2343 | return (char *) __get_free_pages(gfp_flags, order); | 2372 | if (buffer) |
2373 | return buffer; | ||
2374 | |||
2375 | /* | ||
2376 | * vmalloc failed, lets dig into swap here | ||
2377 | */ | ||
2378 | *flags = 0; | ||
2379 | gfp_flags &= ~__GFP_NORETRY; | ||
2380 | buffer = (char *)__get_free_pages(gfp_flags, order); | ||
2381 | if (buffer) | ||
2382 | return buffer; | ||
2383 | |||
2384 | /* | ||
2385 | * complete and utter failure | ||
2386 | */ | ||
2387 | return NULL; | ||
2344 | } | 2388 | } |
2345 | 2389 | ||
2346 | static char **alloc_pg_vec(struct tpacket_req *req, int order) | 2390 | static struct pgv *alloc_pg_vec(struct tpacket_req *req, int order) |
2347 | { | 2391 | { |
2348 | unsigned int block_nr = req->tp_block_nr; | 2392 | unsigned int block_nr = req->tp_block_nr; |
2349 | char **pg_vec; | 2393 | struct pgv *pg_vec; |
2350 | int i; | 2394 | int i; |
2351 | 2395 | ||
2352 | pg_vec = kzalloc(block_nr * sizeof(char *), GFP_KERNEL); | 2396 | pg_vec = kcalloc(block_nr, sizeof(struct pgv), GFP_KERNEL); |
2353 | if (unlikely(!pg_vec)) | 2397 | if (unlikely(!pg_vec)) |
2354 | goto out; | 2398 | goto out; |
2355 | 2399 | ||
2356 | for (i = 0; i < block_nr; i++) { | 2400 | for (i = 0; i < block_nr; i++) { |
2357 | pg_vec[i] = alloc_one_pg_vec_page(order); | 2401 | pg_vec[i].buffer = alloc_one_pg_vec_page(order, |
2358 | if (unlikely(!pg_vec[i])) | 2402 | &pg_vec[i].flags); |
2403 | if (unlikely(!pg_vec[i].buffer)) | ||
2359 | goto out_free_pgvec; | 2404 | goto out_free_pgvec; |
2360 | } | 2405 | } |
2361 | 2406 | ||
@@ -2364,6 +2409,7 @@ out: | |||
2364 | 2409 | ||
2365 | out_free_pgvec: | 2410 | out_free_pgvec: |
2366 | free_pg_vec(pg_vec, order, block_nr); | 2411 | free_pg_vec(pg_vec, order, block_nr); |
2412 | kfree(pg_vec); | ||
2367 | pg_vec = NULL; | 2413 | pg_vec = NULL; |
2368 | goto out; | 2414 | goto out; |
2369 | } | 2415 | } |
@@ -2371,7 +2417,7 @@ out_free_pgvec: | |||
2371 | static int packet_set_ring(struct sock *sk, struct tpacket_req *req, | 2417 | static int packet_set_ring(struct sock *sk, struct tpacket_req *req, |
2372 | int closing, int tx_ring) | 2418 | int closing, int tx_ring) |
2373 | { | 2419 | { |
2374 | char **pg_vec = NULL; | 2420 | struct pgv *pg_vec = NULL; |
2375 | struct packet_sock *po = pkt_sk(sk); | 2421 | struct packet_sock *po = pkt_sk(sk); |
2376 | int was_running, order = 0; | 2422 | int was_running, order = 0; |
2377 | struct packet_ring_buffer *rb; | 2423 | struct packet_ring_buffer *rb; |
@@ -2533,15 +2579,22 @@ static int packet_mmap(struct file *file, struct socket *sock, | |||
2533 | continue; | 2579 | continue; |
2534 | 2580 | ||
2535 | for (i = 0; i < rb->pg_vec_len; i++) { | 2581 | for (i = 0; i < rb->pg_vec_len; i++) { |
2536 | struct page *page = virt_to_page(rb->pg_vec[i]); | 2582 | struct page *page; |
2583 | void *kaddr = rb->pg_vec[i].buffer; | ||
2537 | int pg_num; | 2584 | int pg_num; |
2538 | 2585 | ||
2539 | for (pg_num = 0; pg_num < rb->pg_vec_pages; | 2586 | for (pg_num = 0; pg_num < rb->pg_vec_pages; |
2540 | pg_num++, page++) { | 2587 | pg_num++) { |
2588 | if (rb->pg_vec[i].flags & PGV_FROM_VMALLOC) | ||
2589 | page = vmalloc_to_page(kaddr); | ||
2590 | else | ||
2591 | page = virt_to_page(kaddr); | ||
2592 | |||
2541 | err = vm_insert_page(vma, start, page); | 2593 | err = vm_insert_page(vma, start, page); |
2542 | if (unlikely(err)) | 2594 | if (unlikely(err)) |
2543 | goto out; | 2595 | goto out; |
2544 | start += PAGE_SIZE; | 2596 | start += PAGE_SIZE; |
2597 | kaddr += PAGE_SIZE; | ||
2545 | } | 2598 | } |
2546 | } | 2599 | } |
2547 | } | 2600 | } |