Wednesday, May 9, 2007

Allocating Memory in the Kernel

Unfortunately for kernel developers, allocating memory in the kernel is not as simple as allocating memory in user space. A number of factors contribute to the complication, among them:

The kernel is limited to about 1GB of virtual and physical memory.
The kernel's memory is not pageable.
The kernel usually wants physically contiguous memory.
Often, the kernel must allocate the memory without sleeping.
Mistakes in the kernel have a much higher price than they do elsewhere.

Although easy access to an abundance of memory certainly is not a luxury to the kernel, a little understanding of the issues can go a long way toward making the process relatively painless.
A General-Purpose Allocator

The general interface for allocating memory inside of the kernel is kmalloc():

#include
void * kmalloc(size_t size, int flags);

It should look familiar-it is pretty much the same as user space's malloc(), after all-except that it takes a second argument, flags. Let's ignore flags for a second and see what we recognize. First off, size is the same here as in malloc()'s-it specifies the size in bytes of the allocation. Upon successful return, kmalloc() returns a pointer to size bytes of memory. The alignment of the allocated memory is suitable for storage of and access to any type of object. As with malloc(), kmalloc() can fail, and you must check its return value against NULL. Let's look at an example:

struct falcon *p;
p = kmalloc(sizeof (struct falcon), GFP_KERNEL);
if (!p)
/* the allocation failed - handle appropriately */

Flags

The flags field controls the behavior of memory allocation. We can divide flags into three groups: action modifiers, zone modifiers and types. Action modifiers tell the kernel how to allocate memory. They specify, for example, whether the kernel can sleep (that is, whether the call to kmalloc() can block) in order to satisfy the allocation. Zone modifiers, on the other hand, tell the kernel from where the request should be satisfied. For example, some requests may need to be satisfied from memory that hardware can access through direct memory access (DMA). Finally, type flags specify a type of allocation. They group together relevant action and zone modifiers into a single mnemonic. In general, instead of specifying multiple action and zone modifiers, you specify a single type flag.

Table 1 is a listing of the action modifiers, and Table 2 is a listing of the zone modifiers. Many different flags can be used; allocating memory in the kernel is nontrivial. It is possible to control many aspects of memory allocation in the kernel. Your code should use the type flags and not the individual action and zone modifiers. The two most common flags are GFP_ATOMIC and GFP_KERNEL. Nearly all of your kernel memory allocations should specify one of these two flags. Garrick, please kern the double underscores in Tables 1 and 2.

Table 1. Action Modifiers
Flag Description
__GFP_COLD The kernel should use cache cold pages.
__GFP_FS The kernel can start filesystem I/O.
__GFP_HIGH The kernel can access emergency pools.
__GFP_IO The kernel can start disk I/O.
__GFP_NOFAIL The kernel can repeat the allocation.
__GFP_NORETRY The kernel does not retry if the allocation fails.
__GFP_NOWARN The kernel does not print failure warnings.
__GFP_REPEAT The kernel repeats the allocation if it fails.
__GFP_WAIT The kernel can sleep.

Table 2. Zone Modifiers
Flag Description
__GFP_DMA Allocate only DMA-capable memory.
No flag Allocate from wherever available.

The GFP_ATOMIC flag instructs the memory allocator never to block. Use this flag in situations where it cannot sleep-where it must remain atomic-such as interrupt handlers, bottom halves and process context code that is holding a lock. Because the kernel cannot block the allocation and try to free up sufficient memory to satisfy the request, an allocation specifying GFP_ATOMIC has a lesser chance of succeeding than one that does not. Nonetheless, if your current context is incapable of sleeping, it is your only choice. Using GFP_ATOMIC is simple:

struct wolf *p;
p = kmalloc(sizeof (struct wolf), GFP_ATOMIC);
if (!p)
/* error */


Conversely, the GFP_KERNEL flag specifies a normal kernel allocation. Use this flag in code executing in process context without any locks. A call to kmalloc() with this flag can sleep; thus, you must use this flag only when it is safe to do so. The kernel utilizes the ability to sleep in order to free memory, if needed. Therefore, allocations that specify this flag have a greater chance of succeeding. If insufficient memory is available, for example, the kernel can block the requesting code and swap some inactive pages to disk, shrink the in-memory caches, write out buffers and so on.

Sometimes, as when writing an ISA device driver, you need to ensure that the memory allocated is capable of undergoing DMA. For ISA devices, this is memory in the first 16MB of physical memory. To ensure that the kernel allocates from this specific memory, use the GFP_DMA flag. Generally, you would use this flag in conjunction with either GFP_ATOMIC or GFP_KERNEL; you can combine flags with a binary OR operation. For example, to instruct the kernel to allocate DMA-capable memory and to sleep if needed, do:

char *buf;
/* we want DMA-capable memory,
* and we can sleep if needed */
buf = kmalloc(BUF_LEN, GFP_DMA | GFP_KERNEL);
if (!buf)
/* error */

Table 3 is a listing of the type flags, and Table 4 shows to which type flag each action and zone modifier equates. The header defines all of the flags.

Table 3. Types
Flag Description
GFP_ATOMIC The allocation is high-priority and does not sleep. This is the flag to use in interrupt handlers, bottom halves and other situations where you cannot sleep.

GFP_DMA This is an allocation of DMA-capable memory. Device drivers that need DMA-capable memory use this flag.

GFP_KERNEL This is a normal allocation and might block. This is the flag to use in process context code when it is safe to sleep.

GFP_NOFS This allocation might block and might initiate disk I/O, but it does not initiate a filesystem operation. This is the flag to use in filesystem code when you cannot start another filesystem operation.

GFP_NOIO This allocation might block, but it does not initiate block I/O. This is the flag to use in block layer code when you cannot start more block I/O.

GFP_USER This is a normal allocation and might block. This flag is used to allocate memory for user-space processes.

Table 4. Composition of the Type Flags
Flag Value
GFP_ATOMIC __GFP_HIGH
GFP_NOIO __GFP_WAIT
GFP_NOFS (__GFP_WAIT | __GFP_IO)
GFP_KERNEL (__GFP_WAIT | __GFP_IO | __GFP_FS)
GFP_USER (__GFP_WAIT | __GFP_IO | __GFP_FS)
GFP_DMA __GFP_DMA

Returning Memory

When you are finished accessing the memory allocated via kmalloc(), you must return it to the kernel. This job is done using kfree(), which is the counterpart to user space's free() library call. The prototype for kfree() is:

#include
void kfree(const void *objp);

kfree()'s usage is identical to the user-space variant. Assume p is a pointer to a block of memory obtained via kmalloc(). The following command, then, would free that block and return the memory to the kernel:

kfree(p);

As with free() in user space, calling kfree() on a block of memory that already has been freed or on a pointer that is not an address returned from kmalloc() is a bug, and it can result in memory corruption. Always balance allocations and frees to ensure that kfree() is called exactly once on the correct pointer. Calling kfree() on NULL is checked for explicitly and is safe, although it is not necessarily a sensible idea.

Let's look at the full allocation and freeing cycle:

struct sausage *s;
s = kmalloc(sizeof (struct sausage), GFP_KERNEL);
if (!s)
return -ENOMEM;
/* ... */
kfree(s);

Allocating from Virtual Memory

The kmalloc() function returns physically and therefore virtually contiguous memory. This is a contrast to user space's malloc() function, which returns virtually but not necessarily physically contiguous memory. Physically contiguous memory has two primary benefits. First, many hardware devices cannot address virtual memory. Therefore, in order for them to be able to access a block of memory, the block must exist as a physically contiguous chunk of memory. Second, a physically contiguous block of memory can use a single large page mapping. This minimizes the translation lookaside buffer (TLB) overhead of addressing the memory, as only a single TLB entry is required.

Allocating physically contiguous memory has one downside: it is often hard to find physically contiguous blocks of memory, especially for large allocations. Allocating memory that is only virtually contiguous has a much larger chance of success. If you do not need physically contiguous memory, use vmalloc():


#include
void * vmalloc(unsigned long size);

You then return memory obtained with vmalloc() to the system by using vfree():

#include
void vfree(void *addr);

Here again, vfree()'s usage is identical to user space's malloc() and free() functions:

struct black_bear *p;
p = vmalloc(sizeof (struct black_bear));
if (!p)
/* error */
/* ... */
vfree(p);

In this particular case, vmalloc() might sleep.

Many allocations in the kernel can use vmalloc(), because few allocations need to appear contiguous to hardware devices. If you are allocating memory that only software accesses, such as data associated with a user process, there is no need for the memory to be physically contiguous. Nonetheless, few allocations in the kernel use vmalloc(). Most choose to use kmalloc(), even if it's not needed, partly for historical and partly for performance reasons. Because the TLB overhead for physically contiguous pages is reduced greatly, the performance gains often are well appreciated. Despite this, if you need to allocate tens of megabytes of memory in the kernel, vmalloc() is your best option.
A Small Fixed-Size Stack

Unlike user-space processes, code executing in the kernel has neither a large nor a dynamically growing stack. Instead, each process in the kernel has a small fixed-size stack. The exact size of the stack is architecture-dependent. Most architectures allocate two pages for the stack, so the stack is 8KB on 32-bit machines.

Because of the small stack, allocations that are large, automatic and on-the-stack are discouraged. Indeed, you never should see anything such as this in kernel code:

#define BUF_LEN 2048
void rabbit_function(void)
{
char buf[BUF_LEN];
/* ... */
}

Instead, the following is preferred:

#define BUF_LEN 2048
void rabbit_function(void)
{
char *buf;
buf = kmalloc(BUF_LEN, GFP_KERNEL);
if (!buf)
/* error! */
/* ... */
}

You also seldom see the equivalent of this stack in user space, because there is rarely a reason to perform a dynamic memory allocation when you know the allocation size at the time you write the code. In the kernel, however, you should use dynamic memory any time the allocation size is larger than a handful of bytes or so. This helps prevent stack overflow, which ruins everyone's day.
Conclusion

With a little understanding, getting a hold of memory in the kernel is demystified and not too much more difficult to do than it is in user space. A few simple rules of thumb can go a long way:

Decide whether you can sleep (that is, whether the call to kmalloc() can block). If you are in an interrupt handler, in a bottom half, or if you hold a lock, you cannot. If you are in process context and do not hold a lock, you probably can.

If you can sleep, specify GFP_KERNEL.

If you cannot sleep, specify GFP_ATOMIC.

If you need DMA-capable memory (for example, for an ISA or broken PCI device), specify GFP_DMA.

Always check for and handle a NULL return value from kmalloc().

Do not leak memory; make sure you call kfree() somewhere.

Ensure that you do not race and call kfree() multiple times and that you never access a block of memory after you free it.

69 comments:

supratik said...

awesome checklist for kernel programming guidelines. loved it.thanks

Unknown said...

mee tooo

Anonymous said...

Why kernel space is not swapable?

Anonymous said...

top [url=http://www.001casino.com/]online casinos[/url] coincide the latest [url=http://www.casinolasvegass.com/]casino las vegas[/url] unshackled no set aside bonus at the leading [url=http://www.baywatchcasino.com/]laid-back largesse casino
[/url].

Anonymous said...

Hey I know this is off topic but I was wondering if you knew of any widgets I could add
to my blog that automatically tweet my newest twitter updates.
I've been looking for a plug-in like this for quite some time and was hoping maybe you would have some experience with something like this. Please let me know if you run into anything. I truly enjoy reading your blog and I look forward to your new updates.
My page :: online cash for you

Anonymous said...

Good day! Do you know if they make any plugins to assist with Search Engine Optimization?
I'm trying to get my blog to rank for some targeted keywords but I'm not seeing very good gains.
If you know of any please share. Many thanks!
Look into my web-site - free online slot machines no downloads

Anonymous said...

Hi there, I enjoy reading through your article.
I like to write a little comment to support you.
My webpage :: Money Slots Online

Anonymous said...

This is a really good tip particularly to those fresh to the
blogosphere. Simple but very accurate information… Thank you for sharing this one.
A must read post!
Here is my web-site - affiliate marketing definition

Anonymous said...

Howdy! Do you know if they make any plugins to help with Search Engine
Optimization? I'm trying to get my blog to rank for some targeted keywords but I'm not seeing very good gains.
If you know of any please share. Kudos!
Here is my blog ; real money slot machine

Anonymous said...

Simply desire to say your article is as astonishing. The clarity
in your post is simply nice and i can assume you're an expert on this subject. Well with your permission let me to grab your RSS feed to keep updated with forthcoming post. Thanks a million and please keep up the gratifying work.
my page - binary options trading strategy

Anonymous said...

Cool blog! Is your theme custom made or did you download it from somewhere?
A theme like yours with a few simple adjustements would
really make my blog shine. Please let me know where
you got your design. Bless you
Feel free to visit my web site ; retirement account - td ameritrade

Anonymous said...

I read this paragraph completely concerning the resemblance
of most up-to-date and previous technologies, it's awesome article.
Here is my website blackjack for real money

Anonymous said...

Heya i am for the first time here. I found this board and I to find It truly helpful & it
helped me out much. I'm hoping to provide something again and help others such as you aided me.

Feel free to surf to my blog; day trading for dummies

Anonymous said...

I was suggested this website by my cousin. I'm not sure whether this post is written by him as no one else know such detailed about my problem. You are incredible! Thanks!

my page; Money talks full
my website > earn money from online surveys

Anonymous said...

Hello my family member! I want to say that this post is awesome,
great written and come with almost all significant infos. I'd like to peer more posts like this .

Review my page - Best work From Home jobs For moms

Anonymous said...

Ahaa, its nice dialogue on the topic of this paragraph
here at this web site, I have read all that, so at this time me also commenting at
this place.

My webpage :: best Rated work at home Jobs

Anonymous said...

Wonderful goods from you, man. I have be aware your
stuff previous to and you are simply extremely great.

I really like what you have obtained here, really like what you're stating and the way in which you are saying it. You're making it enjoyable
and you continue to care for to keep it sensible. I can't wait to learn much more from you. That is actually a tremendous website.

My web-site - als texter geld verdienen

Anonymous said...

Hey There. I found your blog using msn. That is an extremely smartly written article.
I'll make sure to bookmark it and return to learn more of your helpful info. Thank you for the post. I will certainly comeback.

Also visit my weblog - im internet geld verdienen

Anonymous said...

May I simply just say what a comfort to discover someone who genuinely knows what they're talking about over the internet. You certainly understand how to bring an issue to light and make it important. More and more people really need to look at this and understand this side of your story. It's
surprising you're not more popular given that you definitely have the gift.

my web-site free money website

Anonymous said...

Hello friends, its great piece of writing regarding teachingand
fully explained, keep it up all the time.

Stop by my site - hedging binary Options
my site > forex trading signals

Anonymous said...

Very good blog! Do you have any hints for aspiring writers?
I'm planning to start my own site soon but I'm a little lost on everything.
Would you propose starting with a free platform like Wordpress or go for a
paid option? There are so many choices out there that I'm totally confused .. Any ideas? Many thanks!

my website nasdaq penny stocks

Anonymous said...

Can I simply say what a comfort to find someone that truly knows what
they are discussing online. You actually know how to bring a problem
to light and make it important. A lot more people should look at this and understand this side of your
story. I was surprised you aren't more popular given that you surely possess the gift.

Check out my blog post; the binary options
Also see my website - binary options platforms

Anonymous said...

Hi there! Would you mind if I share your blog with my twitter group?
There's a lot of people that I think would really enjoy your content. Please let me know. Many thanks

my site - how to make money illegally fast

Anonymous said...

These are really great ideas in concerning blogging.
You have touched some nice things here. Any way keep up wrinting.


Also visit my website; legal work at home jobs
my page > legal work at home jobs

Anonymous said...

I am really enjoying the theme/design of your website. Do you ever
run into any browser compatibility problems? A number
of my blog visitors have complained about my
blog not operating correctly in Explorer but looks great in Chrome.
Do you have any advice to help fix this problem?

my website; legit online jobs
Also see my web site > online jobs work from home

Anonymous said...

Oh my goodness! Awesome article dude! Many thanks, However
I am having difficulties with your RSS. I don't know the reason why I cannot join it. Is there anyone else getting identical RSS problems? Anyone that knows the answer will you kindly respond? Thanx!!

Also visit my weblog: online jobs work at home

Anonymous said...

Howdy just wanted to give you a quick heads up. The text in
your post seem to be running off the screen in Firefox.
I'm not sure if this is a format issue or something to do with web browser compatibility but I figured I'd
post to let you know. The style and design look great though!
Hope you get the issue resolved soon. Many thanks

Feel free to visit my page :: best forex trading

Anonymous said...

whoah this blog is magnificent i like reading your posts. Stay up the good work!
You understand, a lot of people are searching around for
this information, you could help them greatly.


Take a look at my blog :: forex trading account

Anonymous said...

Tremendous issues here. I'm very glad to peer your post. Thanks so much and I'm taking a look forward to contact you.
Will you kindly drop me a e-mail?

Look at my site: earn making money online

Anonymous said...

I have been browsing online more than 2 hours today, yet I never found any interesting article like yours.

It's pretty worth enough for me. In my view, if all webmasters and bloggers made good content as you did, the internet will be a lot more useful than ever before.

Check out my weblog - how to make extra money from home

Anonymous said...

Whats up this is somewhat of off topic but I
was wondering if blogs use WYSIWYG editors
or if you have to manually code with HTML. I'm starting a blog soon but have no coding know-how so I wanted to get advice from someone with experience. Any help would be greatly appreciated!

my web site; online casino real money

Anonymous said...

It's not my first time to pay a visit this web page, i am visiting this website dailly and obtain pleasant facts from here everyday.

Also visit my web blog: Top usa online Casinos

Anonymous said...

My brother recommended I might like this blog.
He was totally right. This post actually made my day.

You can not imagine simply how much time I had spent for
this information! Thanks!

my homepage :: how to make money

Anonymous said...

Hello there! I know this is kinda off topic nevertheless I'd figured I'd ask.
Would you be interested in trading links or maybe guest writing a blog post or
vice-versa? My blog addresses a lot of the same topics as yours and
I believe we could greatly benefit from each other. If you happen to be interested feel free to
shoot me an email. I look forward to hearing from you! Terrific blog by the
way!

my page ... easy ways to make money

Anonymous said...

Pretty portion of content. I simply stumbled upon your site and in accession capital to assert that I get in fact enjoyed account your
blog posts. Any way I will be subscribing for your feeds
and even I success you get right of entry to persistently quickly.



Feel free to surf to my page: part time work from home

Anonymous said...

I am in fact grateful to the owner of this web
site who has shared this fantastic article
at at this place.

My page :: How To Make Some Extra Money

Anonymous said...

Hey! Do you know if they make any plugins to protect against hackers?
I'm kinda paranoid about losing everything I've worked hard on.
Any tips?

Also visit my site day trading software

Anonymous said...

Hurrah! At last I got a web site from where I know how
to really get helpful information regarding my study and knowledge.


Feel free to surf to my web site ... best work from home jobs

Anonymous said...

What's up, always i used to check webpage posts here in the early hours in the break of day, as i love to gain knowledge of more and more.

Also visit my blog post - make money fast online

Anonymous said...

Woah! I'm really digging the template/theme of this blog. It's
simple, yet effective. A lot of times it's hard to get that "perfect balance" between usability and visual appearance. I must say that you've done a fantastic job with this.
Also, the blog loads extremely quick for me on Firefox.
Exceptional Blog!

my site good penny stocks

Anonymous said...

I loved as much as you will receive carried out right here.
The sketch is tasteful, your authored subject matter stylish.
nonetheless, you command get got an edginess over that you wish
be delivering the following. unwell unquestionably come further formerly again since exactly the same nearly very
often inside case you shield this hike.

Also visit my page free to join online jobs

Anonymous said...

What's up, after reading this amazing post i am as well happy to share my familiarity here with mates.

my web site: fast ways to make money online

Anonymous said...

Hello to every single one, it's genuinely a nice for me to pay a visit this web site, it includes priceless Information.

my page; online trading systems

Anonymous said...

Hey! Do you know if they make any plugins to safeguard against hackers?
I'm kinda paranoid about losing everything I've worked hard on.
Any suggestions?

Also visit my site ... easy money making

Anonymous said...

I blog frequently and I genuinely appreciate your content.
This article has truly peaked my interest. I will take a
note of your blog and keep checking for new details about once
per week. I opted in for your RSS feed as well.

Feel free to surf to my site ... way to earn money online

Anonymous said...

Spot on with this write-up, I truly believe this site needs a great deal more attention.

I'll probably be back again to read through more, thanks for the information!

Stop by my blog after hours trading nyse

Anonymous said...

You could certainly see your expertise within the work you write.
The world hopes for even more passionate writers such as you who
aren't afraid to mention how they believe. Always follow your heart.

my webpage :: day trading strategies

Anonymous said...

Hi there, i read your blog occasionally and
i own a similar one and i was just curious if you get a lot of spam remarks?
If so how do you reduce it, any plugin or anything you can advise?
I get so much lately it's driving me crazy so any assistance is very much appreciated.

Have a look at my web-site :: facebook free slots

Anonymous said...

Hi, I think your site might be having browser compatibility issues.
When I look at your website in Safari, it looks fine but when
opening in Internet Explorer, it has some overlapping.
I just wanted to give you a quick heads up!
Other then that, superb blog!

Here is my web-site :: no fee work at home jobs

Anonymous said...

Spot on with this write-up, I really feel this site needs much more attention.

I'll probably be returning to read through more, thanks for the information!

my webpage :: new way to make money online

Anonymous said...

Greetings! Very helpful advice in this particular article!

It's the little changes which will make the greatest changes. Thanks for sharing!

Also visit my blog how to make extra money for the holidays

Anonymous said...

Hello! I know this is kinda off topic but I'd figured I'd ask.
Would you be interested in exchanging links or maybe guest
authoring a blog article or vice-versa? My blog addresses a lot of the same subjects as yours and I think we could greatly
benefit from each other. If you happen to be interested
feel free to shoot me an e-mail. I look forward to hearing from
you! Wonderful blog by the way!

Here is my web page - the best work from home business

Anonymous said...

Admiring the persistence you put into your website and in depth information you offer.

It's great to come across a blog every once in a while that isn't the same unwanted rehashed information.
Wonderful read! I've saved your site and I'm including your RSS feeds to
my Google account.

Also visit my homepage: work from home jobs

Anonymous said...

Every weekend i used to visit this web site, because i wish for enjoyment, for the reason that this this website conations
in fact fastidious funny data too.

Feel free to visit my homepage: help me i need money

Anonymous said...

This post is really a nice one it helps new the web people, who are wishing for blogging.


Stop by my blog how to make money fast

Anonymous said...

What's up, all is going sound here and ofcourse every one is sharing data, that's really
good, keep up writing.

Also visit my homepage legitimate ways to make money online

Anonymous said...

Hello, Neat post. There's a problem together with your website in web explorer, may test this? IE still is the market chief and a good component of other folks will pass over your wonderful writing due to this problem.

Feel free to surf to my web site i want money lots and lots of money

Anonymous said...

Hello! I just wanted to ask if you ever have any issues with hackers?
My last blog (wordpress) was hacked and I ended up losing a few months of hard work due to
no backup. Do you have any solutions to prevent hackers?


Also visit my blog post - Cedar Financial binary

Anonymous said...

I read this paragraph completely on the topic of the
comparison of latest and preceding technologies, it's amazing article.

Also visit my webpage best penny stocks to buy

Anonymous said...

Hi there, after reading this remarkable article i am also
cheerful to share my experience here with friends.


Have a look at my blog; trading binary options

Anonymous said...

Hi! Do you know if they make any plugins to help with Search Engine Optimization?

I'm trying to get my blog to rank for some targeted keywords but I'm not seeing very good gains.
If you know of any please share. Cheers!

Stop by my web blog - clickbank affiliate marketing

Anonymous said...

What i don't understood is if truth be told how you are no longer actually a lot more neatly-appreciated than you might be now. You're very intelligent.
You realize thus considerably on the subject of this topic,
produced me in my view believe it from numerous varied angles.

Its like women and men are not involved except it is one thing to accomplish with
Woman gaga! Your individual stuffs excellent. At all times deal with it up!



Have a look at my blog post: forex binary options

Anonymous said...

This paragraph is actually a fastidious one it assists new web users, who are wishing in favor of blogging.


Feel free to visit my blog: http://www.youtube.com/watch?v=fCkK9-pzu_o

Anonymous said...

I'm curious to find out what blog platform you're utilizing?

I'm experiencing some minor security problems with my latest blog and I'd like to find
something more risk-free. Do you have any solutions?

Feel free to visit my webpage; make money from home online

Anonymous said...

Wonderful site you have here but I was curious if you
knew of any user discussion forums that cover the same topics
talked about here? I'd really love to be a part of group where I can get responses from other experienced individuals that share the same interest. If you have any suggestions, please let me know. Kudos!

My webpage: http://www.youtube.com/watch?v=gtCm82Itxoc

Anonymous said...

You need to take part in a contest for one of the highest quality sites
online. I'm going to highly recommend this site!

My homepage - ways to make money online

Anonymous said...

Hey very interesting blog!

Check out my site :: dragon City cheat engine

Anonymous said...

This site was... how do I say it? Relevant!! Finally I
have found something that helped me. Cheers!

Feel free to surf to my page http://www.youtube.com/watch?v=9SnPSlwYwpQ

thesharebrokers said...

Great Article.key replacement add-on in car insurance policy