All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: 2.6.36-rc7: kernel panic with SECURITY_TOMOYO=y
From: KOSAKI Motohiro @ 2010-10-08  4:55 UTC (permalink / raw)
  To: Christian Kujau; +Cc: kosaki.motohiro, LKML, takedakn, penguin-kernel
In-Reply-To: <alpine.DEB.2.01.1010071637090.21082@trent.utfs.org>

> On Thu, 7 Oct 2010 at 15:43, Christian Kujau wrote:
> > Kernel panic - not syncing: Profile uersion 0 is not supported
> 
> OK, I just saw that security/tomoyo/common.c panics when:
> 
>         if (tomoyo_profile_version != 20090903)
>                 panic("Profile version %u is not supported.\n",
>                       tomoyo_profile_version);
> 
> If it's imperative that the profile version is somewhat current (mine was 
> not, I had old tomoyo-ccstools installed), I would have expected a 
> slightly more descriptive error messages (apart from that panic), but now 
> I know. Upgraded to tomoyo-tools-2.3.0-20100820 did the trick.

I agree this zero information panic is suck.


Handa-san, please see this panic message again.

> Kernel panic - not syncing: Profile uersion 0 is not supported

Profile?
This message doesn't have any information which should we look!
And, 'profile' is wrong word. TOMOYO have to recommend to upgrade
userland tools here at minimum.

The best way is, of cource, you don't break backward compatibility.



> Call Trace:
> [efB4fd60] [c0008c88] show_stack+0x48/0x168 (unreliable)
> [ef84fda0] [c0375fe01 panic+0xa4/0x1ec
> [ef84fdf0] [c01c5f30] tomoyo_close_control+0x0/0xa4
> [efB4fe00] [c01cc2f0] tomoyo_load_policy+0xeB/0x11c





^ permalink raw reply

* Re: [PATCH v2] memcg: reduce lock time at move charge (Was Re: [PATCH 04/10] memcg: disable local interrupts in lock_page_cgroup()
From: Andrew Morton @ 2010-10-08  4:55 UTC (permalink / raw)
  To: KAMEZAWA Hiroyuki
  Cc: Daisuke Nishimura, Minchan Kim, Greg Thelen, linux-kernel,
	linux-mm, containers, Andrea Righi, Balbir Singh
In-Reply-To: <20101008133712.2a836331.kamezawa.hiroyu@jp.fujitsu.com>

On Fri, 8 Oct 2010 13:37:12 +0900 KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> wrote:

> On Thu, 7 Oct 2010 16:14:54 -0700
> Andrew Morton <akpm@linux-foundation.org> wrote:
> 
> > On Thu, 7 Oct 2010 17:04:05 +0900
> > KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> wrote:
> > 
> > > Now, at task migration among cgroup, memory cgroup scans page table and moving
> > > account if flags are properly set.
> > > 
> > > The core code, mem_cgroup_move_charge_pte_range() does
> > > 
> > >  	pte_offset_map_lock();
> > > 	for all ptes in a page table:
> > > 		1. look into page table, find_and_get a page
> > > 		2. remove it from LRU.
> > > 		3. move charge.
> > > 		4. putback to LRU. put_page()
> > > 	pte_offset_map_unlock();
> > > 
> > > for pte entries on a 3rd level? page table.
> > > 
> > > This pte_offset_map_lock seems a bit long. This patch modifies a rountine as
> > > 
> > > 	for 32 pages: pte_offset_map_lock()
> > > 		      find_and_get a page
> > > 		      record it
> > > 		      pte_offset_map_unlock()
> > > 	for all recorded pages
> > > 		      isolate it from LRU.
> > > 		      move charge
> > > 		      putback to LRU
> > > 	for all recorded pages
> > > 		      put_page()
> > 
> > The patch makes the code larger, more complex and slower!
> > 
> 
> Slower ?

Sure.  It walks the same data three times, potentially causing
thrashing in the L1 cache.  It takes and releases locks at a higher
frequency.  It increases the text size.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* Re: [PATCH] serial: DCC(JTAG) serial and console emulation support
From: Daniel Walker @ 2010-10-08  4:59 UTC (permalink / raw)
  To: Mike Frysinger
  Cc: Greg KH, Alan Cox, linux-kernel, Hyok S. Choi, Tony Lindgren,
	Jeff Ohlstein, Ben Dooks, Alan Cox, Kukjin Kim, Feng Tang,
	Tobias Klauser, Jason Wessel, Philippe Langlais

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=utf-8, Size: 1771 bytes --]

I'm not disputing the serial core part. I ok with converting it to tty.

Daniel

Mike Frysinger <vapier@gentoo.org> wrote:

>On Thu, Oct 7, 2010 at 18:11, Daniel Walker wrote:
>> On Thu, 2010-10-07 at 14:52 -0700, Greg KH wrote:
>>> On Thu, Oct 07, 2010 at 02:47:18PM -0700, Daniel Walker wrote:
>>> > On Thu, 2010-10-07 at 14:15 -0700, Greg KH wrote:
>>> > > On Thu, Oct 07, 2010 at 01:51:18PM -0700, Daniel Walker wrote:
>>> > > > > We've said no over a period of about ten years to a lot of attempts to
>>> > > > > just borrow the ttyS0 range. If we'd said yes it would have been a
>>> > > > > complete mess by now.
>>> > > > >
>>> > > > > So the answer is no.
>>> > > >
>>> > > > Nothing can be unilateral, there's always room for exceptions. You
>>> > > > should say something more like "it's possible, but unlikely".
>>> > >
>>> > > Hm, how about this, as the TTY and serial driver[1] maintainer, I will
>>> > > not accept this kind of patch at all.
>>> > >
>>> > > Is that final enough for you?
>>> >
>>> > So you don't like it, that's fair enough .. <thinks>I wonder what other
>>> > maintainers I can send this too</thinks> ;)
>>> >
>>> > Can you be more specific about your objections
>>>
>>> See everything that Mike and Alan wrote, do I need to repeat it?
>>
>> I'm not sure .. I would say "Yes" if I didn't get the feeling your
>> already to tear my head off.
>>
>> I think the only disputed issue is the use of ttyS as options part of
>> the driver ..
>
>and using "serial_core" at all in favor of HVC or tty_core (the latter
>might be easier for you as the former is a bit of unknown atm)
>-mike
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply

* Re: Ok, dumb question time ...
From: Joe Landman @ 2010-10-08  5:00 UTC (permalink / raw)
  To: Neil Brown; +Cc: landman, linux-raid
In-Reply-To: <20101008155448.19d46db6@notabene>

On 10/08/2010 12:54 AM, Neil Brown wrote:

>> This is quite disconcerting ... I thought it would be simple.
>
> It is.
>
> You want the array to think that it only has two devices?
>
>    mdadm --grow /dev/md0 --raid-devices=2

Very nice ... thanks!


^ permalink raw reply

* Re: [PATCH v2 00/11] SDIO Runtime PM Support
From: Luciano Coelho @ 2010-10-08  5:00 UTC (permalink / raw)
  To: ext Chris Ball
  Cc: ext Ohad Ben-Cohen, linux-mmc@vger.kernel.org,
	linux-wireless@vger.kernel.org, Nicolas Pitre
In-Reply-To: <20101007180107.GA15745@void.printf.net>

On Thu, 2010-10-07 at 20:01 +0200, ext Chris Ball wrote:
> Hi Luciano, Ohad,
> 
> On Thu, Oct 07, 2010 at 03:32:16PM +0300, Luciano Coelho wrote:
> > I have tested this on my beagle board with the wl1271 patches that Ohad
> > provided and it seems to be working fine.  I've also created a small
> > script to stress test the implementation a little bit and didn't see any
> > problems.
> > 
> > Tested-by: Luciano Coelho <luciano.coelho@nokia.com>
> > 
> > This is quite an important fix in the wl1271 point-of-view.  Chris, do
> > you think there is any chance that this could make it to 2.6.37?
> 
> Yes, can do -- the patchset looks good, and I've just pushed patches
> 1-9 (the MMC patches) to mmc-next.  Please go ahead and submit the
> two wl1271 patches through wireless-testing whenever you're ready.

Thanks a lot, Chris! This will easy our development work quite much,
since we don't have to be applying separate patches to be able to enable
and disable the chip at runtime.

I'll apply the wl1271 patches via the wl12xx tree.

-- 
Cheers,
Luca.


^ permalink raw reply

* Re: [PATCH] SCSI tape: add MTWEOFI to write filemarks without flushing drive buffer
From: James Bottomley @ 2010-10-07 21:54 UTC (permalink / raw)
  To: Kai Makisara; +Cc: linux-scsi
In-Reply-To: <alpine.LNX.2.00.1008291828080.8430@kai.makisara.local>

On Sun, 2010-08-29 at 18:45 +0300, Kai Makisara wrote:
> This patch adds a new MTIOCTOP operation MTWEOFI that writes filemarks with
> immediate bit set. This means that the drive does not flush its buffer and the
> next file can be started immediately. This speeds up writing in applications
> that have to write multiple small files.

This patch is rejecting massively ... could you respin it against
scsi-misc, please?

jejb@mulgrave:~/git/scsi-misc-2.6> patch -p1 < ~/tmp.mail
patching file drivers/scsi/st.c
Hunk #1 FAILED at 9.
Hunk #2 succeeded at 17 with fuzz 2.
Hunk #3 FAILED at 2696.
Hunk #4 FAILED at 2883.
Hunk #5 FAILED at 2901.
4 out of 5 hunks FAILED -- saving rejects to file drivers/scsi/st.c.rej
patching file include/linux/mtio.h
Hunk #1 FAILED at 63.
1 out of 1 hunk FAILED -- saving rejects to file include/linux/mtio.h.rej
patching file Documentation/scsi/st.txt
Hunk #1 succeeded at 2 with fuzz 2.
Hunk #2 FAILED at 85.
Hunk #3 FAILED at 301.
2 out of 3 hunks FAILED -- saving rejects to file Documentation/scsi/st.txt.rej

Thanks,

James



^ permalink raw reply

* Re: [PATCH 2/5] scsi: megaraid_sas - support devices update flag
From: James Bottomley @ 2010-10-07 22:35 UTC (permalink / raw)
  To: bo yang; +Cc: bo.yang, linux-scsi, akpm, linux-kernel
In-Reply-To: <AANLkTim3OKvhov1uCj5g6YgmOzR1rDogLmtJMTkbYq5V@mail.gmail.com>

On Wed, 2010-09-22 at 22:50 -0400, bo yang wrote:
> Driver added the Device update flag to tell LSI application driver
> will do the device Update.  LSI MegaRAID SAS application will check
> this flag to decide if it is need to update the Device or not.

Well, the good news is I've got the Online Controller reset update. The
bad news is that this patch now fails to apply: looks like an attachment
problem again:

Applying: megaraid_sas: support devices update flag
fatal: corrupt patch at line 70
Patch failed at 0001 megaraid_sas: support devices update flag

James



^ permalink raw reply

* Re: [RFC PATCH] Audio standards on tm6000
From: Dmitri Belimov @ 2010-10-08 19:03 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Felipe Sanches, Stefan Ringel, Bee Hock Goh,
	Luis Henrique Fagundes, Linux Media Mailing List
In-Reply-To: <4CAD5A78.3070803@redhat.com>

Hi Mauro

Not so good. Audio with this patch has bad white noise sometimes and
bad quality. I try found better configuration for SECAM-DK.

With my best regards, Dmitry.

> Hi Dmitri,
> 
> IMO, the better is to remove the audio init from tm6000-core and add
> a separate per-standard set of tables.
> 
> I'm enclosing the patch for it. Please check if this won't break for
> your device.
> 
> On all tests I did here with a tm6010 device (HVR 900H), I was only
> able to listen to white noise.
> 
> I'm suspecting that this device uses XC3028 MTS mode (e. g. uses
> xc3028 to decode audio, and just inputs the audio stream from some
> line IN. As the driver is not able yet to handle an audio mux, this
> may explain why I'm not able to receive any audio at all.
> 
> Maybe tm5600 devices may also require (or use) line input entries,
> instead of I2S.
> 
> Could you please check those issues?
> 
> PS.: the PAL/M hunk will probably fail, as I likely applied some
> patches before this one, in order to try to fix it. It should be
> trivial to solve the conflicts.
> 
> ---
> 
> tm6000: Implement audio standard tables
> 
> Implement separate tables for audio standards, associating them with
> the video standards.
> 
> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
> 
> diff --git a/drivers/staging/tm6000/tm6000-core.c
> b/drivers/staging/tm6000/tm6000-core.c index 57cb69e..9cb2901 100644
> --- a/drivers/staging/tm6000/tm6000-core.c
> +++ b/drivers/staging/tm6000/tm6000-core.c
> @@ -200,6 +200,10 @@ int tm6000_init_analog_mode(struct tm6000_core
> *dev) val &= ~0x40;
>  		tm6000_set_reg(dev,
> TM6010_REQ07_RC0_ACTIVE_VIDEO_SOURCE, val); 
> +		tm6000_set_reg(dev,
> TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfc); +
> +#if 0		/* FIXME: VBI is standard-dependent */
> +
>  		/* Init teletext */
>  		tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
>  		tm6000_set_reg(dev,
> TM6010_REQ07_R41_TELETEXT_VBI_CODE1, 0x27); @@ -249,44 +253,7 @@ int
> tm6000_init_analog_mode(struct tm6000_core *dev) tm6000_set_reg(dev,
> TM6010_REQ07_R5B_VBI_TELETEXT_DTO0, 0x4c); tm6000_set_reg(dev,
> TM6010_REQ07_R40_TELETEXT_VBI_CODE0, 0x01); tm6000_set_reg(dev,
> TM6010_REQ07_R3F_RESET, 0x00); -
> -
> -		/* Init audio */
> -		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> -		tm6000_set_reg(dev, TM6010_REQ08_R04_A_SIF_AMP_CTRL,
> 0xa0);
> -		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> -		tm6000_set_reg(dev, TM6010_REQ08_R07_A_LEFT_VOL,
> 0x00);
> -		tm6000_set_reg(dev, TM6010_REQ08_R08_A_RIGHT_VOL,
> 0x00);
> -		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> -		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> -		tm6000_set_reg(dev, TM6010_REQ08_R0B_A_ASD_THRES1,
> 0x20);
> -		tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2,
> 0x12);
> -		tm6000_set_reg(dev, TM6010_REQ08_R0D_A_AMD_THRES,
> 0x20);
> -		tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1,
> 0xf0);
> -		tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2,
> 0x80);
> -		tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1,
> 0xc0);
> -		tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2,
> 0x80);
> -		tm6000_set_reg(dev, TM6010_REQ08_R12_A_AGC_U, 0x12);
> -		tm6000_set_reg(dev, TM6010_REQ08_R13_A_AGC_ERR_T,
> 0xfe);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R14_A_AGC_GAIN_INIT, 0x20);
> -		tm6000_set_reg(dev, TM6010_REQ08_R15_A_AGC_STEP_THR,
> 0x14);
> -		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> -		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> -		tm6000_set_reg(dev, TM6010_REQ08_R18_A_TR_CTRL,
> 0xa0);
> -		tm6000_set_reg(dev, TM6010_REQ08_R19_A_FH_2FH_GAIN,
> 0x32);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R1A_A_NICAM_SER_MAX, 0x64);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R1B_A_NICAM_SER_MIN, 0x20);
> -		tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1c,
> 0x00);
> -		tm6000_set_reg(dev, REQ_08_SET_GET_AVREG_BIT, 0x1d,
> 0x00);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R1E_A_GAIN_DEEMPH_OUT, 0x13);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_R1F_A_TEST_INTF_SEL, 0x00);
> -		tm6000_set_reg(dev, TM6010_REQ08_R20_A_TEST_PIN_SEL,
> 0x00);
> -		tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL,
> 0xf3);
> -		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x00);
> -		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> -
> +#endif
>  	} else {
>  		/* Enables soft reset */
>  		tm6000_set_reg(dev, TM6010_REQ07_R3F_RESET, 0x01);
> @@ -360,7 +327,6 @@ int tm6000_init_digital_mode(struct tm6000_core
> *dev) tm6000_set_reg(dev, TM6010_REQ07_RFE_POWER_DOWN, 0x28);
>  		tm6000_set_reg(dev,
> TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xfc); tm6000_set_reg(dev,
> TM6010_REQ08_RE6_POWER_DOWN_CTRL2, 0xff);
> -		tm6000_set_reg(dev,
> TM6010_REQ08_RF1_AADC_POWER_DOWN, 0xfe); tm6000_read_write_usb(dev,
> 0xc0, 0x0e, 0x00c2, 0x0008, buf, 2); printk(KERN_INFO"buf %#x %#x\n",
> buf[0], buf[1]); } else  {
> diff --git a/drivers/staging/tm6000/tm6000-stds.c
> b/drivers/staging/tm6000/tm6000-stds.c index 33adf6c..e79a72e 100644
> --- a/drivers/staging/tm6000/tm6000-stds.c
> +++ b/drivers/staging/tm6000/tm6000-stds.c
> @@ -28,8 +28,22 @@ struct tm6000_reg_settings {
>  	unsigned char value;
>  };
>  
> +enum tm6000_audio_std {
> +	BG_NICAM,
> +	BTSC,
> +	BG_A2,
> +	DK_NICAM,
> +	EIAJ,
> +	FM_RADIO,
> +	I_NICAM,
> +	KOREA_A2,
> +	L_NICAM,
> +};
> +
>  struct tm6000_std_tv_settings {
>  	v4l2_std_id id;
> +	enum tm6000_audio_std audio_default_std;
> +
>  	struct tm6000_reg_settings sif[12];
>  	struct tm6000_reg_settings nosif[12];
>  	struct tm6000_reg_settings common[26];
> @@ -37,12 +51,14 @@ struct tm6000_std_tv_settings {
>  
>  struct tm6000_std_settings {
>  	v4l2_std_id id;
> +	enum tm6000_audio_std audio_default_std;
>  	struct tm6000_reg_settings common[37];
>  };
>  
>  static struct tm6000_std_tv_settings tv_stds[] = {
>  	{
>  		.id = V4L2_STD_PAL_M,
> +		.audio_default_std = BTSC,
>  		.sif = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
> @@ -96,12 +112,14 @@ static struct tm6000_std_tv_settings tv_stds[] =
> { 
>  			{TM6010_REQ07_R04_LUMA_HAGC_CONTROL, 0xdc},
>  			{TM6010_REQ07_R0D_CHROMA_KILL_LEVEL, 0x07},
> -			{TM6010_REQ08_R05_A_STANDARD_MOD, 0x22},
> +
>  			{TM6010_REQ07_R3F_RESET, 0x00},
> +
>  			{0, 0, 0},
>  		},
>  	}, {
>  		.id = V4L2_STD_PAL_Nc,
> +		.audio_default_std = BTSC,
>  		.sif = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
> @@ -161,6 +179,7 @@ static struct tm6000_std_tv_settings tv_stds[] = {
>  		},
>  	}, {
>  		.id = V4L2_STD_PAL,
> +		.audio_default_std = BG_A2,
>  		.sif = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
> @@ -220,6 +239,7 @@ static struct tm6000_std_tv_settings tv_stds[] = {
>  		},
>  	}, {
>  		.id = V4L2_STD_SECAM,
> +		.audio_default_std = BG_NICAM,
>  		.sif = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
> @@ -278,6 +298,7 @@ static struct tm6000_std_tv_settings tv_stds[] = {
>  		},
>  	}, {
>  		.id = V4L2_STD_NTSC,
> +		.audio_default_std = BTSC,
>  		.sif = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf2},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf8},
> @@ -341,6 +362,7 @@ static struct tm6000_std_tv_settings tv_stds[] = {
>  static struct tm6000_std_settings composite_stds[] = {
>  	{
>  		.id = V4L2_STD_PAL_M,
> +		.audio_default_std = BTSC,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
> @@ -383,6 +405,7 @@ static struct tm6000_std_settings
> composite_stds[] = { },
>  	 }, {
>  		.id = V4L2_STD_PAL_Nc,
> +		.audio_default_std = BTSC,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
> @@ -425,6 +448,7 @@ static struct tm6000_std_settings
> composite_stds[] = { },
>  	}, {
>  		.id = V4L2_STD_PAL,
> +		.audio_default_std = BG_A2,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
> @@ -467,6 +491,7 @@ static struct tm6000_std_settings
> composite_stds[] = { },
>  	 }, {
>  		.id = V4L2_STD_SECAM,
> +		.audio_default_std = BG_NICAM,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
> @@ -508,6 +533,7 @@ static struct tm6000_std_settings
> composite_stds[] = { },
>  	}, {
>  		.id = V4L2_STD_NTSC,
> +		.audio_default_std = BTSC,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xf4},
> @@ -554,6 +580,7 @@ static struct tm6000_std_settings
> composite_stds[] = { static struct tm6000_std_settings svideo_stds[]
> = { {
>  		.id = V4L2_STD_PAL_M,
> +		.audio_default_std = BTSC,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
> @@ -596,6 +623,7 @@ static struct tm6000_std_settings svideo_stds[] =
> { },
>  	}, {
>  		.id = V4L2_STD_PAL_Nc,
> +		.audio_default_std = BTSC,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
> @@ -638,6 +666,7 @@ static struct tm6000_std_settings svideo_stds[] =
> { },
>  	}, {
>  		.id = V4L2_STD_PAL,
> +		.audio_default_std = BG_A2,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
> @@ -680,6 +709,7 @@ static struct tm6000_std_settings svideo_stds[] =
> { },
>  	 }, {
>  		.id = V4L2_STD_SECAM,
> +		.audio_default_std = BG_NICAM,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
> @@ -721,6 +751,7 @@ static struct tm6000_std_settings svideo_stds[] =
> { },
>  	}, {
>  		.id = V4L2_STD_NTSC,
> +		.audio_default_std = BTSC,
>  		.common = {
>  			{TM6010_REQ08_RE2_POWER_DOWN_CTRL1, 0xf0},
>  			{TM6010_REQ08_RE3_ADC_IN1_SEL, 0xfc},
> @@ -765,6 +796,136 @@ static struct tm6000_std_settings svideo_stds[]
> = { },
>  };
>  
> +
> +static int tm6000_set_audio_std(struct tm6000_core *dev,
> +				enum tm6000_audio_std std)
> +{
> +	switch (std) {
> +	case BG_NICAM:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x11);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case BTSC:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x02);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1,
> 0xf0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2,
> 0x80);
> +		tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1,
> 0xc0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2,
> 0x80);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case BG_A2:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x05);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1,
> 0xf0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2,
> 0x80);
> +		tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1,
> 0xc0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2,
> 0x80);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case DK_NICAM:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2,
> 0x0a);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case EIAJ:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x03);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case FM_RADIO:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x01);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x0c);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x10);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case I_NICAM:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0C_A_ASD_THRES2,
> 0x0a);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case KOREA_A2:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x04);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x04);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0E_A_MONO_THRES1,
> 0xf0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0F_A_MONO_THRES2,
> 0x80);
> +		tm6000_set_reg(dev, TM6010_REQ08_R10_A_MUTE_THRES1,
> 0xc0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R11_A_MUTE_THRES2,
> 0xf0);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	case L_NICAM:
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R02_A_FIX_GAIN_CTRL, 0x02);
> +		tm6000_set_reg(dev,
> TM6010_REQ08_R03_A_AUTO_GAIN_CTRL, 0x00);
> +		tm6000_set_reg(dev, TM6010_REQ08_R05_A_STANDARD_MOD,
> 0x0a);
> +		tm6000_set_reg(dev, TM6010_REQ08_R06_A_SOUND_MOD,
> 0x06);
> +		tm6000_set_reg(dev, TM6010_REQ08_R09_A_MAIN_VOL,
> 0x08);
> +		tm6000_set_reg(dev, TM6010_REQ08_R0A_A_I2S_MOD,
> 0x91);
> +		tm6000_set_reg(dev, TM6010_REQ08_R16_A_AGC_GAIN_MAX,
> 0xfe);
> +		tm6000_set_reg(dev, TM6010_REQ08_R17_A_AGC_GAIN_MIN,
> 0x01);
> +		tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x80);
> +		break;
> +	}
> +	return 0;
> +}
> +
>  void tm6000_get_std_res(struct tm6000_core *dev)
>  {
>  	/* Currently, those are the only supported resoltions */
> @@ -825,6 +986,8 @@ static int tm6000_set_tv(struct tm6000_core *dev,
> int pos) rc = tm6000_load_std(dev, tv_stds[pos].common,
>  			     sizeof(tv_stds[pos].common));
>  
> +	tm6000_set_audio_std(dev, tv_stds[pos].audio_default_std);
> +
>  	return rc;
>  }
>  
> @@ -850,6 +1013,8 @@ int tm6000_set_standard(struct tm6000_core *dev,
> v4l2_std_id * norm) rc = tm6000_load_std(dev, svideo_stds[i].common,
>  						     sizeof(svideo_stds[i].
>  							    common));
> +				tm6000_set_audio_std(dev,
> svideo_stds[i].audio_default_std); +
>  				goto ret;
>  			}
>  		}
> @@ -861,6 +1026,7 @@ int tm6000_set_standard(struct tm6000_core *dev,
> v4l2_std_id * norm) composite_stds[i].common,
>  						     sizeof(composite_stds[i].
>  							    common));
> +				tm6000_set_audio_std(dev,
> composite_stds[i].audio_default_std); goto ret;
>  			}
>  		}
> diff --git a/drivers/staging/tm6000/tm6000-video.c
> b/drivers/staging/tm6000/tm6000-video.c index a45b012..9304158 100644
> --- a/drivers/staging/tm6000/tm6000-video.c
> +++ b/drivers/staging/tm6000/tm6000-video.c
> @@ -1015,7 +1015,8 @@ static int vidioc_s_std (struct file *file,
> void *priv, v4l2_std_id *norm) struct tm6000_fh   *fh=priv;
>  	struct tm6000_core *dev = fh->dev;
>  
> -	rc=tm6000_set_standard (dev, norm);
> +	rc = tm6000_set_standard(dev, norm);
> +	rc = tm6000_init_analog_mode(dev);
>  
>  	fh->width  = dev->width;
>  	fh->height = dev->height;
> @@ -1292,9 +1293,10 @@ static int tm6000_open(struct file *file)
>  				"active=%d\n",list_empty(&dev->vidq.active));
>  
>  	/* initialize hardware on analog mode */
> -	if (dev->mode!=TM6000_MODE_ANALOG) {
> -		rc=tm6000_init_analog_mode (dev);
> -		if (rc<0)
> +//	if (dev->mode!=TM6000_MODE_ANALOG) {
> +//		rc = tm6000_set_standard(dev, dev->norm);
> +		rc += tm6000_init_analog_mode(dev);
> +		if (rc < 0)
>  			return rc;
>  
>  		/* Put all controls at a sane state */
> @@ -1302,7 +1304,7 @@ static int tm6000_open(struct file *file)
>  			qctl_regs[i] =tm6000_qctrl[i].default_value;
>  
>  		dev->mode=TM6000_MODE_ANALOG;
> -	}
> +//	}
>  
>  	videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops,
>  			NULL, &dev->slock,

^ permalink raw reply

* Re: empty filter on FORWARD chain with rp_filter means safe right?
From: Jan Engelhardt @ 2010-10-08  5:02 UTC (permalink / raw)
  To: Payam Chychi; +Cc: Scott Mcdermott, netfilter
In-Reply-To: <4CAEA0D3.1020207@gmail.com>


On Friday 2010-10-08 06:40, Payam Chychi wrote:

> Thats correct Scott,
> in order for any systems to abuse your setup they will need to be directly
> connected to a segment that has knowledge of valid route to the end system...
> meaning if a computer is 2 hops away and the router in between has no knowledge
> of how to get to your private rfc1918 then pkts get dropped.
>
> Keep in mind that as ipv4 exhaustion gets extreme, some isps will use rcf1918
> blocks and route them either in their IGP or even EGP (aka internet routes)...

Internally yes, but externally no. And it's not really RFC1918 routes being
"used in the Internet" - instead, it is "enlarging our NAT domain". (Mobile
UMTS/HSDPA providers do this in Germany already.)

^ permalink raw reply

* Re: [PATCH v2] memcg: reduce lock time at move charge (Was Re: [PATCH 04/10] memcg: disable local interrupts in lock_page_cgroup()
From: KAMEZAWA Hiroyuki @ 2010-10-08  5:12 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Daisuke Nishimura, Minchan Kim, Greg Thelen, linux-kernel,
	linux-mm, containers, Andrea Righi, Balbir Singh
In-Reply-To: <20101007215556.21412ae6.akpm@linux-foundation.org>

On Thu, 7 Oct 2010 21:55:56 -0700
Andrew Morton <akpm@linux-foundation.org> wrote:

> On Fri, 8 Oct 2010 13:37:12 +0900 KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> wrote:
> 
> > On Thu, 7 Oct 2010 16:14:54 -0700
> > Andrew Morton <akpm@linux-foundation.org> wrote:
> > 
> > > On Thu, 7 Oct 2010 17:04:05 +0900
> > > KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> wrote:
> > > 
> > > > Now, at task migration among cgroup, memory cgroup scans page table and moving
> > > > account if flags are properly set.
> > > > 
> > > > The core code, mem_cgroup_move_charge_pte_range() does
> > > > 
> > > >  	pte_offset_map_lock();
> > > > 	for all ptes in a page table:
> > > > 		1. look into page table, find_and_get a page
> > > > 		2. remove it from LRU.
> > > > 		3. move charge.
> > > > 		4. putback to LRU. put_page()
> > > > 	pte_offset_map_unlock();
> > > > 
> > > > for pte entries on a 3rd level? page table.
> > > > 
> > > > This pte_offset_map_lock seems a bit long. This patch modifies a rountine as
> > > > 
> > > > 	for 32 pages: pte_offset_map_lock()
> > > > 		      find_and_get a page
> > > > 		      record it
> > > > 		      pte_offset_map_unlock()
> > > > 	for all recorded pages
> > > > 		      isolate it from LRU.
> > > > 		      move charge
> > > > 		      putback to LRU
> > > > 	for all recorded pages
> > > > 		      put_page()
> > > 
> > > The patch makes the code larger, more complex and slower!
> > > 
> > 
> > Slower ?
> 
> Sure.  It walks the same data three times, potentially causing
> thrashing in the L1 cache.

Hmm, make this 2 times, at least.

> It takes and releases locks at a higher frequency.  It increases the text size.
> 

But I don't think page_table_lock is a lock which someone can hold so long
that
	1. find_get_page
	2. spin_lock(zone->lock)
		3. remove it from LRU
	4. lock_page_cgroup()
	5. move charge (This means page 
	5. putback to LRU
for 4096/8=1024 pages long.

will try to make the routine smarter.

But I want to get rid of page_table_lock -> lock_page_cgroup().

Thanks,
-Kame


--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply

* linux-next: Tree for October 8
From: Stephen Rothwell @ 2010-10-08  5:15 UTC (permalink / raw)
  To: linux-next; +Cc: LKML

[-- Attachment #1: Type: text/plain, Size: 10852 bytes --]

Hi all,

Changes since 20101007:

All the trees hosted on git.infradead.org are unfetchable (and presumably
unable to be updated) due to the machine crashing.

The galak tree lost its conflict.

The xfs tree gained a build failure from a mismerge for which I have
applied a patch.

The drm tree lost its conflicts.

The alacrity tree gained a conflict against various trees.

The hwpoison tree gained a conflict against Linus' tree.

The irqflags tree lost its conflict.

----------------------------------------------------------------------------

I have created today's linux-next tree at
git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
(patches at http://www.kernel.org/pub/linux/kernel/v2.6/next/ ).  If you
are tracking the linux-next tree using git, you should not use "git pull"
to do so as that will try to merge the new linux-next release with the
old one.  You should use "git fetch" as mentioned in the FAQ on the wiki
(see below).

You can see which trees have been included by looking in the Next/Trees
file in the source.  There are also quilt-import.log and merge.log files
in the Next directory.  Between each merge, the tree was built with
a ppc64_defconfig for powerpc and an allmodconfig for x86_64. After the
final fixups (if any), it is also built with powerpc allnoconfig (32 and
64 bit), ppc44x_defconfig and allyesconfig (minus
CONFIG_PROFILE_ALL_BRANCHES - this fails its final link) and i386, sparc
and sparc64 defconfig. These builds also have
CONFIG_ENABLE_WARN_DEPRECATED, CONFIG_ENABLE_MUST_CHECK and
CONFIG_DEBUG_INFO disabled when necessary.

Below is a summary of the state of the merge.

We are up to 176 trees (counting Linus' and 22 trees of patches pending
for Linus' tree), more are welcome (even if they are currently empty).
Thanks to those who have contributed, and to those who haven't, please do.

Status of my local build tests will be at
http://kisskb.ellerman.id.au/linux-next .  If maintainers want to give
advice about cross compilers/configs that work, we are always open to add
more builds.

Thanks to Randy Dunlap for doing many randconfig builds.

There is a wiki covering stuff to do with linux-next at
http://linux.f-seidel.de/linux-next/pmwiki/ .  Thanks to Frank Seidel.

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au

$ git checkout master
$ git reset --hard stable
Merging origin/master
Merging fixes/fixes
Merging arm-current/master
Merging m68k-current/for-linus
Merging powerpc-merge/merge
Merging sparc-current/master
Merging scsi-rc-fixes/master
Merging net-current/master
Merging sound-current/for-linus
Merging pci-current/for-linus
Merging wireless-current/master
Merging kbuild-current/rc-fixes
Merging quilt/driver-core.current
Merging quilt/tty.current
Merging quilt/usb.current
Merging quilt/staging.current
Merging cpufreq-current/fixes
Merging input-current/for-linus
Merging md-current/for-linus
Merging audit-current/for-linus
Merging crypto-current/master
Merging ide-curent/master
Merging dwmw2/master
Merging gcl-current/merge
Merging arm/devel
Merging davinci/davinci-next
Merging i.MX/for-next
Merging msm/for-next
Merging omap/for-next
Merging pxa/for-next
Merging samsung/next-samsung
Merging s5p/for-next
Merging tegra/for-next
Merging avr32/avr32-arch
Merging blackfin/for-linus
Merging cris/for-next
Merging ia64/test
Merging m68k/for-next
Merging m68knommu/for-next
Merging microblaze/next
Merging mips/mips-for-linux-next
Merging parisc/next
Merging powerpc/next
Merging 4xx/next
Merging 52xx-and-virtex/next
Merging galak/next
Merging s390/features
Merging sh/master
Merging genesis/master
Merging sparc/master
Merging tile/master
Merging xtensa/master
CONFLICT (content): Merge conflict in arch/xtensa/configs/iss_defconfig
Merging ceph/for-next
Merging cifs/master
Merging configfs/linux-next
Merging ecryptfs/next
Merging ext3/for_next
Merging ext4/next
Merging fatfs/master
Merging fuse/for-next
Merging gfs2/master
Merging hfsplus/for-next
Merging jfs/next
Merging logfs/master
CONFLICT (content): Merge conflict in fs/logfs/logfs.h
Merging nfs/linux-next
Merging nfsd/nfsd-next
Merging nilfs2/for-next
Merging ocfs2/linux-next
Merging omfs/for-next
Merging squashfs/master
Merging udf/for_next
Merging v9fs/for-next
Merging ubifs/linux-next
Merging xfs/master
Applying: xfs: fix up mismerge of __xfs_inode_clear_reclaim_tag
Merging vfs/for-next
Merging pci/linux-next
Merging hid/for-next
Merging quilt/i2c
Merging bjdooks-i2c/next-i2c
Merging quilt/jdelvare-hwmon
Merging hwmon-staging/hwmon-next
Merging quilt/kernel-doc
Merging v4l-dvb/master
Merging kbuild/for-next
Merging kconfig/for-next
Merging ide/master
Merging libata/NEXT
Merging infiniband/for-next
Merging acpi/test
Merging idle-test/idle-test
Merging ieee1394/for-next
Merging ubi/linux-next
Merging kvm/linux-next
Merging dlm/next
Merging swiotlb/master
Merging swiotlb-xen/master
Merging ibft/master
Merging scsi/master
Merging async_tx/next
Merging wireless/master
CONFLICT (content): Merge conflict in Documentation/feature-removal-schedule.txt
CONFLICT (content): Merge conflict in arch/arm/mach-omap2/board-omap3pandora.c
CONFLICT (content): Merge conflict in arch/arm/mach-omap2/board-zoom-peripherals.c
CONFLICT (content): Merge conflict in drivers/net/wireless/libertas/if_sdio.c
Merging net/master
CONFLICT (content): Merge conflict in drivers/net/pcmcia/pcnet_cs.c
CONFLICT (content): Merge conflict in drivers/net/wireless/ipw2x00/ipw2200.c
CONFLICT (content): Merge conflict in net/caif/caif_socket.c
Merging mtd/master
Merging crypto/master
CONFLICT (content): Merge conflict in arch/arm/mach-omap2/devices.c
Merging sound-asoc/for-next
CONFLICT (content): Merge conflict in drivers/video/sh_mobile_hdmi.c
Merging sound/for-next
Merging cpufreq/next
Merging quilt/rr
Merging input/next
CONFLICT (content): Merge conflict in drivers/input/keyboard/Kconfig
Merging lsm/for-next
Merging block/for-next
CONFLICT (content): Merge conflict in fs/ext4/mballoc.c
Merging quilt/device-mapper
Merging embedded/master
Merging firmware/master
Merging pcmcia/master
CONFLICT (content): Merge conflict in drivers/net/pcmcia/smc91c92_cs.c
Merging battery/master
Merging leds/for-mm
Merging backlight/for-mm
Merging mmc/mmc-next
Merging kgdb/kgdb-next
CONFLICT (content): Merge conflict in drivers/char/sysrq.c
Merging slab/for-next
Merging uclinux/for-next
Merging md/for-next
Merging mfd/for-next
CONFLICT (content): Merge conflict in drivers/mfd/sh_mobile_sdhi.c
Merging hdlc/hdlc-next
Merging drm/drm-next
Merging viafb/viafb-next
Merging voltage/for-next
Merging security-testing/next
Merging lblnet/master
Merging agp/agp-next
Merging uwb/for-upstream
Merging watchdog/master
Merging bdev/master
Merging dwmw2-iommu/master
Merging cputime/cputime
Merging osd/linux-next
Merging jc_docs/docs-next
Merging nommu/master
Merging trivial/for-next
Merging audit/for-next
Merging quilt/aoe
Merging suspend/linux-next
Merging bluetooth/master
Merging fsnotify/for-next
Merging irda/for-next
Merging catalin/for-next
CONFLICT (content): Merge conflict in arch/arm/include/asm/smp_plat.h
CONFLICT (content): Merge conflict in arch/arm/kernel/Makefile
CONFLICT (content): Merge conflict in arch/arm/mach-vexpress/ct-ca9x4.c
CONFLICT (content): Merge conflict in arch/arm/mm/flush.c
Merging alacrity/linux-next
CONFLICT (content): Merge conflict in include/linux/Kbuild
Merging i7core_edac/linux_next
CONFLICT (content): Merge conflict in MAINTAINERS
Merging i7300_edac/linux_next
Merging devicetree/next-devicetree
Merging spi/next-spi
Merging omap_dss2/for-next
Merging xen/upstream/xen
Merging rcu/rcu/next
CONFLICT (content): Merge conflict in include/linux/rcupdate.h
Merging tip/auto-latest
CONFLICT (content): Merge conflict in arch/x86/kernel/module.c
CONFLICT (content): Merge conflict in include/linux/percpu.h
CONFLICT (content): Merge conflict in net/core/dev.c
Merging edac-amd/for-next
Merging oprofile/for-next
Merging percpu/for-next
CONFLICT (content): Merge conflict in include/linux/percpu.h
CONFLICT (content): Merge conflict in mm/percpu.c
Merging workqueues/for-next
Merging sfi/sfi-test
Merging asm-generic/next
Merging drivers-x86/linux-next
Merging hwpoison/hwpoison
CONFLICT (content): Merge conflict in mm/memory-failure.c
Merging sysctl/master
Merging quilt/driver-core
CONFLICT (content): Merge conflict in drivers/misc/Makefile
Merging quilt/tty
Merging quilt/usb
CONFLICT (content): Merge conflict in drivers/usb/gadget/rndis.c
Merging staging-next/staging-next
CONFLICT (rename/modify): Merge conflict in drivers/misc/ti-st/st_kim.c
CONFLICT (content): Merge conflict in arch/arm/plat-omap/devices.c
CONFLICT (content): Merge conflict in drivers/misc/Makefile
CONFLICT (content): Merge conflict in drivers/staging/Makefile
CONFLICT (content): Merge conflict in drivers/staging/batman-adv/hard-interface.c
CONFLICT (delete/modify): drivers/staging/mrst-touchscreen/Makefile deleted in HEAD and modified in staging-next/staging-next. Version staging-next/staging-next of drivers/staging/mrst-touchscreen/Makefile left in tree.
CONFLICT (delete/modify): drivers/staging/mrst-touchscreen/intel-mid-touch.c deleted in HEAD and modified in staging-next/staging-next. Version staging-next/staging-next of drivers/staging/mrst-touchscreen/intel-mid-touch.c left in tree.
CONFLICT (delete/modify): drivers/staging/ti-st/st.h deleted in staging-next/staging-next and modified in HEAD. Version HEAD of drivers/staging/ti-st/st.h left in tree.
$ git rm -f drivers/staging/mrst-touchscreen/Makefile drivers/staging/mrst-touchscreen/intel-mid-touch.c
$ git rm -f drivers/staging/ti-st/st.h drivers/staging/ti-st/st_core.h
Applying: staging: ath6kl: Fixing the driver to use modified mmc_host structure
Merging slabh/slabh
Merging bkl-trivial/trivial
CONFLICT (content): Merge conflict in drivers/block/ataflop.c
CONFLICT (content): Merge conflict in drivers/char/pcmcia/cm4000_cs.c
CONFLICT (content): Merge conflict in drivers/char/pcmcia/cm4040_cs.c
CONFLICT (content): Merge conflict in drivers/mmc/card/block.c
Merging bkl-llseek/llseek
CONFLICT (content): Merge conflict in drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
CONFLICT (content): Merge conflict in drivers/infiniband/hw/cxgb4/device.c
Merging bkl-vfs/vfs
CONFLICT (content): Merge conflict in fs/nilfs2/super.c
Merging bkl-config/config
CONFLICT (content): Merge conflict in fs/compat_ioctl.c
Merging irqflags/master
Merging cleancache/linux-next
CONFLICT (content): Merge conflict in include/linux/fs.h
CONFLICT (content): Merge conflict in mm/Kconfig
Merging scsi-post-merge/merge-base:master

[-- Attachment #2: Type: application/pgp-signature, Size: 490 bytes --]

^ permalink raw reply

* Re: [PATCH v2] memcg: reduce lock time at move charge (Was Re: [PATCH 04/10] memcg: disable local interrupts in lock_page_cgroup()
From: KAMEZAWA Hiroyuki @ 2010-10-08  5:12 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Daisuke Nishimura, Minchan Kim, Greg Thelen, linux-kernel,
	linux-mm, containers, Andrea Righi, Balbir Singh
In-Reply-To: <20101007215556.21412ae6.akpm@linux-foundation.org>

On Thu, 7 Oct 2010 21:55:56 -0700
Andrew Morton <akpm@linux-foundation.org> wrote:

> On Fri, 8 Oct 2010 13:37:12 +0900 KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> wrote:
> 
> > On Thu, 7 Oct 2010 16:14:54 -0700
> > Andrew Morton <akpm@linux-foundation.org> wrote:
> > 
> > > On Thu, 7 Oct 2010 17:04:05 +0900
> > > KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> wrote:
> > > 
> > > > Now, at task migration among cgroup, memory cgroup scans page table and moving
> > > > account if flags are properly set.
> > > > 
> > > > The core code, mem_cgroup_move_charge_pte_range() does
> > > > 
> > > >  	pte_offset_map_lock();
> > > > 	for all ptes in a page table:
> > > > 		1. look into page table, find_and_get a page
> > > > 		2. remove it from LRU.
> > > > 		3. move charge.
> > > > 		4. putback to LRU. put_page()
> > > > 	pte_offset_map_unlock();
> > > > 
> > > > for pte entries on a 3rd level? page table.
> > > > 
> > > > This pte_offset_map_lock seems a bit long. This patch modifies a rountine as
> > > > 
> > > > 	for 32 pages: pte_offset_map_lock()
> > > > 		      find_and_get a page
> > > > 		      record it
> > > > 		      pte_offset_map_unlock()
> > > > 	for all recorded pages
> > > > 		      isolate it from LRU.
> > > > 		      move charge
> > > > 		      putback to LRU
> > > > 	for all recorded pages
> > > > 		      put_page()
> > > 
> > > The patch makes the code larger, more complex and slower!
> > > 
> > 
> > Slower ?
> 
> Sure.  It walks the same data three times, potentially causing
> thrashing in the L1 cache.

Hmm, make this 2 times, at least.

> It takes and releases locks at a higher frequency.  It increases the text size.
> 

But I don't think page_table_lock is a lock which someone can hold so long
that
	1. find_get_page
	2. spin_lock(zone->lock)
		3. remove it from LRU
	4. lock_page_cgroup()
	5. move charge (This means page 
	5. putback to LRU
for 4096/8=1024 pages long.

will try to make the routine smarter.

But I want to get rid of page_table_lock -> lock_page_cgroup().

Thanks,
-Kame



^ permalink raw reply

* Re: [PATCH 1/2] block: Ensure physical block size is unsigned int
From: Martin K. Petersen @ 2010-10-08  5:15 UTC (permalink / raw)
  To: jaxboe, James.Bottomley; +Cc: Mike Snitzer, linux-scsi
In-Reply-To: <20100927174052.GA14180@redhat.com>

>>>>> "Mike" == Mike Snitzer <snitzer@redhat.com> writes:

>> Physical block size was declared unsigned int to accomodate the
>> maximum size reported by READ CAPACITY(16).  Make sure we use the
>> right type in the related functions.
>> 
>> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

Mike> Acked-by: Mike Snitzer <snitzer@redhat.com>

Jens, ping on the block fix.

James, ping on the sd ditto.

-- 
Martin K. Petersen	Oracle Linux Engineering

^ permalink raw reply

* [PATCH] block: Make the integrity mapped property a bio flag
From: Martin K. Petersen @ 2010-10-08  5:19 UTC (permalink / raw)
  To: Jens Axboe; +Cc: linux-scsi


Previously we tracked whether the integrity metadata had been remapped
using a request flag. This was fine for low-level retries. However, if
an I/O was redriven by upper layers we would end up remapping again,
causing the retry to fail.

Deprecate the REQ_INTEGRITY flag and introduce BIO_MAPPED_INTEGRITY
which enables filesystems to notify lower layers that the bio in
question has already been remapped.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

---

diff --git a/drivers/scsi/sd_dif.c b/drivers/scsi/sd_dif.c
index 84be621..0cb39ff 100644
--- a/drivers/scsi/sd_dif.c
+++ b/drivers/scsi/sd_dif.c
@@ -375,21 +375,20 @@ int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_s
 	unsigned int i, j;
 	u32 phys, virt;
 
-	/* Already remapped? */
-	if (rq->cmd_flags & REQ_INTEGRITY)
-		return 0;
-
 	sdkp = rq->bio->bi_bdev->bd_disk->private_data;
 
 	if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION)
 		return 0;
 
-	rq->cmd_flags |= REQ_INTEGRITY;
 	phys = hw_sector & 0xffffffff;
 
 	__rq_for_each_bio(bio, rq) {
 		struct bio_vec *iv;
 
+		/* Already remapped? */
+		if (bio_flagged(bio, BIO_MAPPED_INTEGRITY))
+			break;
+
 		virt = bio->bi_integrity->bip_sector & 0xffffffff;
 
 		bip_for_each_vec(iv, bio->bi_integrity, i) {
@@ -408,6 +407,8 @@ int sd_dif_prepare(struct request *rq, sector_t hw_sector, unsigned int sector_s
 
 			kunmap_atomic(sdt, KM_USER0);
 		}
+
+		bio->bi_flags |= BIO_MAPPED_INTEGRITY;
 	}
 
 	return 0;
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index ca83a97..925b80d 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -97,6 +97,7 @@ struct bio {
 #define BIO_NULL_MAPPED 9	/* contains invalid user pages */
 #define BIO_FS_INTEGRITY 10	/* fs owns integrity data, not block layer */
 #define BIO_QUIET	11	/* Make BIO Quiet */
+#define BIO_MAPPED_INTEGRITY 12/* integrity metadata has been remapped */
 #define bio_flagged(bio, flag)	((bio)->bi_flags & (1 << (flag)))
 
 /*
@@ -146,7 +147,6 @@ enum rq_flag_bits {
 	__REQ_ORDERED_COLOR,	/* is before or after barrier */
 	__REQ_ALLOCED,		/* request came from our alloc pool */
 	__REQ_COPY_USER,	/* contains copies of user pages */
-	__REQ_INTEGRITY,	/* integrity metadata has been remapped */
 	__REQ_FLUSH,		/* request for cache flush */
 	__REQ_IO_STAT,		/* account I/O stat */
 	__REQ_MIXED_MERGE,	/* merge of different types, fail separately */
@@ -187,7 +187,6 @@ enum rq_flag_bits {
 #define REQ_ORDERED_COLOR	(1 << __REQ_ORDERED_COLOR)
 #define REQ_ALLOCED		(1 << __REQ_ALLOCED)
 #define REQ_COPY_USER		(1 << __REQ_COPY_USER)
-#define REQ_INTEGRITY		(1 << __REQ_INTEGRITY)
 #define REQ_FLUSH		(1 << __REQ_FLUSH)
 #define REQ_IO_STAT		(1 << __REQ_IO_STAT)
 #define REQ_MIXED_MERGE		(1 << __REQ_MIXED_MERGE)

^ permalink raw reply related

* [PATCH] block: Fix double free in blk_integrity_unregister
From: Martin K. Petersen @ 2010-10-08  5:18 UTC (permalink / raw)
  To: Jens Axboe; +Cc: Xiaotian Feng, linux-scsi


Commit 3839e4b introduced a kobject_put but failed to remove the
kmem_cache_free beneath it, leading to a double free.

Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

---

diff --git a/block/blk-integrity.c b/block/blk-integrity.c
index 885cbb5..54bcba6 100644
--- a/block/blk-integrity.c
+++ b/block/blk-integrity.c
@@ -432,7 +432,6 @@ void blk_integrity_unregister(struct gendisk *disk)
 	kobject_uevent(&bi->kobj, KOBJ_REMOVE);
 	kobject_del(&bi->kobj);
 	kobject_put(&bi->kobj);
-	kmem_cache_free(integrity_cachep, bi);
 	disk->integrity = NULL;
 }
 EXPORT_SYMBOL(blk_integrity_unregister);

^ permalink raw reply related

* [PATCH 02/18] fs: Convert nr_inodes and nr_unused to per-cpu counters
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Dave Chinner <dchinner@redhat.com>

The number of inodes allocated does not need to be tied to the
addition or removal of an inode to/from a list. If we are not tied
to a list lock, we could update the counters when inodes are
initialised or destroyed, but to do that we need to convert the
counters to be per-cpu (i.e. independent of a lock). This means that
we have the freedom to change the list/locking implementation
without needing to care about the counters.

Based on a patch originally from Eric Dumazet.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/fs-writeback.c  |    5 +--
 fs/inode.c         |   65 ++++++++++++++++++++++++++++++++++++---------------
 include/linux/fs.h |    4 ++-
 kernel/sysctl.c    |    4 +-
 4 files changed, 53 insertions(+), 25 deletions(-)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index ab38fef..58a95b7 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -723,7 +723,7 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb)
 	wb->last_old_flush = jiffies;
 	nr_pages = global_page_state(NR_FILE_DIRTY) +
 			global_page_state(NR_UNSTABLE_NFS) +
-			(inodes_stat.nr_inodes - inodes_stat.nr_unused);
+			get_nr_dirty_inodes();
 
 	if (nr_pages) {
 		struct wb_writeback_work work = {
@@ -1090,8 +1090,7 @@ void writeback_inodes_sb(struct super_block *sb)
 
 	WARN_ON(!rwsem_is_locked(&sb->s_umount));
 
-	work.nr_pages = nr_dirty + nr_unstable +
-			(inodes_stat.nr_inodes - inodes_stat.nr_unused);
+	work.nr_pages = nr_dirty + nr_unstable + get_nr_dirty_inodes();
 
 	bdi_queue_work(sb->s_bdi, &work);
 	wait_for_completion(&done);
diff --git a/fs/inode.c b/fs/inode.c
index 8646433..f04d501 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -103,8 +103,41 @@ static DECLARE_RWSEM(iprune_sem);
  */
 struct inodes_stat_t inodes_stat;
 
+static struct percpu_counter nr_inodes __cacheline_aligned_in_smp;
+static struct percpu_counter nr_inodes_unused __cacheline_aligned_in_smp;
+
 static struct kmem_cache *inode_cachep __read_mostly;
 
+static inline int get_nr_inodes(void)
+{
+	return percpu_counter_sum_positive(&nr_inodes);
+}
+
+static inline int get_nr_inodes_unused(void)
+{
+	return percpu_counter_sum_positive(&nr_inodes_unused);
+}
+
+int get_nr_dirty_inodes(void)
+{
+	int nr_dirty = get_nr_inodes() - get_nr_inodes_unused();
+	return nr_dirty > 0 ? nr_dirty : 0;
+
+}
+
+/*
+ * Handle nr_inode sysctl
+ */
+#if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS)
+int proc_nr_inodes(ctl_table *table, int write,
+		   void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	inodes_stat.nr_inodes = get_nr_inodes();
+	inodes_stat.nr_unused = get_nr_inodes_unused();
+	return proc_dointvec(table, write, buffer, lenp, ppos);
+}
+#endif
+
 static void wake_up_inode(struct inode *inode)
 {
 	/*
@@ -192,6 +225,8 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
 	inode->i_fsnotify_mask = 0;
 #endif
 
+	percpu_counter_inc(&nr_inodes);
+
 	return 0;
 out:
 	return -ENOMEM;
@@ -232,6 +267,7 @@ void __destroy_inode(struct inode *inode)
 	if (inode->i_default_acl && inode->i_default_acl != ACL_NOT_CACHED)
 		posix_acl_release(inode->i_default_acl);
 #endif
+	percpu_counter_dec(&nr_inodes);
 }
 EXPORT_SYMBOL(__destroy_inode);
 
@@ -286,7 +322,7 @@ void __iget(struct inode *inode)
 
 	if (!(inode->i_state & (I_DIRTY|I_SYNC)))
 		list_move(&inode->i_list, &inode_in_use);
-	inodes_stat.nr_unused--;
+	percpu_counter_dec(&nr_inodes_unused);
 }
 
 void end_writeback(struct inode *inode)
@@ -327,8 +363,6 @@ static void evict(struct inode *inode)
  */
 static void dispose_list(struct list_head *head)
 {
-	int nr_disposed = 0;
-
 	while (!list_empty(head)) {
 		struct inode *inode;
 
@@ -344,11 +378,7 @@ static void dispose_list(struct list_head *head)
 
 		wake_up_inode(inode);
 		destroy_inode(inode);
-		nr_disposed++;
 	}
-	spin_lock(&inode_lock);
-	inodes_stat.nr_inodes -= nr_disposed;
-	spin_unlock(&inode_lock);
 }
 
 /*
@@ -357,7 +387,7 @@ static void dispose_list(struct list_head *head)
 static int invalidate_list(struct list_head *head, struct list_head *dispose)
 {
 	struct list_head *next;
-	int busy = 0, count = 0;
+	int busy = 0;
 
 	next = head->next;
 	for (;;) {
@@ -383,13 +413,11 @@ static int invalidate_list(struct list_head *head, struct list_head *dispose)
 			list_move(&inode->i_list, dispose);
 			WARN_ON(inode->i_state & I_NEW);
 			inode->i_state |= I_FREEING;
-			count++;
+			percpu_counter_dec(&nr_inodes_unused);
 			continue;
 		}
 		busy = 1;
 	}
-	/* only unused inodes may be cached with i_count zero */
-	inodes_stat.nr_unused -= count;
 	return busy;
 }
 
@@ -448,7 +476,6 @@ static int can_unuse(struct inode *inode)
 static void prune_icache(int nr_to_scan)
 {
 	LIST_HEAD(freeable);
-	int nr_pruned = 0;
 	int nr_scanned;
 	unsigned long reap = 0;
 
@@ -484,9 +511,8 @@ static void prune_icache(int nr_to_scan)
 		list_move(&inode->i_list, &freeable);
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state |= I_FREEING;
-		nr_pruned++;
+		percpu_counter_dec(&nr_inodes_unused);
 	}
-	inodes_stat.nr_unused -= nr_pruned;
 	if (current_is_kswapd())
 		__count_vm_events(KSWAPD_INODESTEAL, reap);
 	else
@@ -518,7 +544,7 @@ static int shrink_icache_memory(struct shrinker *shrink, int nr, gfp_t gfp_mask)
 			return -1;
 		prune_icache(nr);
 	}
-	return (inodes_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
+	return (get_nr_inodes_unused() / 100) * sysctl_vfs_cache_pressure;
 }
 
 static struct shrinker icache_shrinker = {
@@ -595,7 +621,6 @@ static inline void
 __inode_add_to_lists(struct super_block *sb, struct hlist_head *head,
 			struct inode *inode)
 {
-	inodes_stat.nr_inodes++;
 	list_add(&inode->i_list, &inode_in_use);
 	list_add(&inode->i_sb_list, &sb->s_inodes);
 	if (head)
@@ -1215,7 +1240,7 @@ static void iput_final(struct inode *inode)
 	if (!drop) {
 		if (!(inode->i_state & (I_DIRTY|I_SYNC)))
 			list_move(&inode->i_list, &inode_unused);
-		inodes_stat.nr_unused++;
+		percpu_counter_inc(&nr_inodes_unused);
 		if (sb->s_flags & MS_ACTIVE) {
 			spin_unlock(&inode_lock);
 			return;
@@ -1227,14 +1252,13 @@ static void iput_final(struct inode *inode)
 		spin_lock(&inode_lock);
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state &= ~I_WILL_FREE;
-		inodes_stat.nr_unused--;
+		percpu_counter_dec(&nr_inodes_unused);
 		hlist_del_init(&inode->i_hash);
 	}
 	list_del_init(&inode->i_list);
 	list_del_init(&inode->i_sb_list);
 	WARN_ON(inode->i_state & I_NEW);
 	inode->i_state |= I_FREEING;
-	inodes_stat.nr_inodes--;
 	spin_unlock(&inode_lock);
 	evict(inode);
 	spin_lock(&inode_lock);
@@ -1489,6 +1513,7 @@ void __init inode_init_early(void)
 
 	for (loop = 0; loop < (1 << i_hash_shift); loop++)
 		INIT_HLIST_HEAD(&inode_hashtable[loop]);
+
 }
 
 void __init inode_init(void)
@@ -1503,6 +1528,8 @@ void __init inode_init(void)
 					 SLAB_MEM_SPREAD),
 					 init_once);
 	register_shrinker(&icache_shrinker);
+	percpu_counter_init(&nr_inodes, 0);
+	percpu_counter_init(&nr_inodes_unused, 0);
 
 	/* Hash may have been set up in inode_init_early */
 	if (!hashdist)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 63d069b..1fb92f9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -407,6 +407,7 @@ extern struct files_stat_struct files_stat;
 extern int get_max_files(void);
 extern int sysctl_nr_open;
 extern struct inodes_stat_t inodes_stat;
+extern int get_nr_dirty_inodes(void);
 extern int leases_enable, lease_break_time;
 
 struct buffer_head;
@@ -2474,7 +2475,8 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
 struct ctl_table;
 int proc_nr_files(struct ctl_table *table, int write,
 		  void __user *buffer, size_t *lenp, loff_t *ppos);
-
+int proc_nr_inodes(struct ctl_table *table, int write,
+		   void __user *buffer, size_t *lenp, loff_t *ppos);
 int __init get_filesystem_list(char *buf);
 
 #define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE])
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index f88552c..33d1733 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1340,14 +1340,14 @@ static struct ctl_table fs_table[] = {
 		.data		= &inodes_stat,
 		.maxlen		= 2*sizeof(int),
 		.mode		= 0444,
-		.proc_handler	= proc_dointvec,
+		.proc_handler	= proc_nr_inodes,
 	},
 	{
 		.procname	= "inode-state",
 		.data		= &inodes_stat,
 		.maxlen		= 7*sizeof(int),
 		.mode		= 0444,
-		.proc_handler	= proc_dointvec,
+		.proc_handler	= proc_nr_inodes,
 	},
 	{
 		.procname	= "file-nr",
-- 
1.7.1


^ permalink raw reply related

* fs: Inode cache scalability V2
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel

This patch set is derived from Nick Piggin's VFS scalability tree.
there doesn't appear to be any push to get that tree into shape for
.37, so this is an attempt to get finer grained review of the series
for upstream inclusion.  I'm hitting VFS lock contention problems
with XFS on 8-16p machines now, so I need to get this stuff moving.

This patch set is just the basic inode_lock breakup patches plus a
few more simple changes to the inode code. It stops short of
introducing RCU inode freeing because those changes are not
completely baked yet.

As a result, the full inode handling improvements of Nick's patch
set are not realised with this short series. However, my own testing
indicates that the amount of lock traffic and contention is down by
an order of magnitude on an 8-way box for parallel inode create and
unlink workloads, so there is still significant improvements from
just this patch set.

Version 2 of this series is a complete rework of the original patch
series.  Nick's original code nested list locks inside the the
inode->i_lock, resulting in a large mess of trylock operations to
get locks out of order all over the place. In many cases, the reason
fo this lock ordering is removed later on in Nick's series as
cleanups are introduced.

As a result I've pulled in several of the cleanups and re-ordered
the series such that cleanups, factoring and list splitting are done
before any of the locking changes. Instead of converting the inode
state flags first, I've converted them last, ensuring that
manipulations are kept inside other locks rather than outside them.

The series is made up of the following steps:

	- inode counters are made per-cpu
	- inode LRU manipulations are made lazy
	- i_list is split into two lists (grows inode by 2
	  pointers), one for tracking lru status, one for writeback
	  status
	- reference counting is factored, then renamed and locked
	  differently
	- inode hash operations are factored, then locked per bucket
	- superblock inode listis locked per-superblock
	- inode LRU is locked via a global lock
		- unclear what the best way to split this up from
		  here is, so no attempt is made to optimise
		  further.
		- Currently not showing signs of contention under
		  any workload on an 8p machine.
	- inode IO list are locked via a per-BDI lock
		- further analysis needed to determine the next step
		  in optimising this list. It is extremely contended
		  under parallel workloads because foreground
		  throttling (balance_dirty_pages) causes unbound
		  writeback parallelism and contention. Fixing the
		  unbound parallelism, I think, is a more important
		  first optimisation step than making the list
		  per-cpu.
	- lock i_state operations with i_lock
	- convert last_ino allocation to a percpu counter
	- protect iunique counter with it's own lock
	- remove inode_lock
	- kill dispose_list() and factor destroying an inode into
	  dispose_one_inode() which is called from reclaim, unmount
	  and iput_final.

None of the patcheѕ are unchanged, and several of them are new or
completely rewritten, so any previous testing is completely
invalidated. I have not tried to optimise locking by using trylock
loops - anywhere that requires out-of-order locking drops locks and
regains the locks needed for the next operation. This approach
simplified the code and lead to several improvments in the patch
series (e.g. moving inode->i_lock inside writeback_single_inode(),
and the dispose_one_inode factoring) that would have gone unnoticed
if I'd gone down the same trylock loop path that Nick used.

I've done some testing so far on ext3, ext4 and XFS (mostly sanity
and lock_stat profile testing), but I have not tested any other
filesystems. IOWs, it is light on testing at this point. I'm sending
out for review now that it passes basic sanity tests so that
comments on the reworked approach can be made.

Version 2:
- complete rework of series.

--

The following changes since commit cb655d0f3d57c23db51b981648e452988c0223f9:

  Linux 2.6.36-rc7 (2010-10-06 13:39:52 -0700)

are available in the git repository at:
  git://git.kernel.org/pub/scm/linux/kernel/git/dgc/xfsdev.git inode-scale

Dave Chinner (11):
      fs: Convert nr_inodes and nr_unused to per-cpu counters
      fs: Clean up inode reference counting
      exofs: use iput() for inode reference count decrements
      fs: add inode reference coutn read accessor
      fs: rework icount to be a locked variable
      fs: Factor inode hash operations into functions
      fs: add a per-superblock lock for the inode list
      fs: split locking of inode writeback and LRU lists
      fs: Protect inode->i_state with th einode->i_lock
      fs: icache remove inode_lock
      fs: Reduce inode I_FREEING and factor inode disposal

Eric Dumazet (1):
      fs: introduce a per-cpu last_ino allocator

Nick Piggin (6):
      kernel: add bl_list
      fs: keep inode with backing-dev
      fs: Implement lazy LRU updates for inodes.
      fs: inode split IO and LRU lists
      fs: Introduce per-bucket inode hash locks
      fs: Make iunique independent of inode_lock

 Documentation/filesystems/Locking        |    2 +-
 Documentation/filesystems/porting        |   10 +-
 Documentation/filesystems/vfs.txt        |    2 +-
 arch/powerpc/platforms/cell/spufs/file.c |    2 +-
 drivers/char/mem.c                       |    2 +-
 drivers/char/raw.c                       |    2 +-
 drivers/mtd/mtdchar.c                    |    2 +-
 drivers/staging/pohmelfs/inode.c         |   10 +-
 fs/9p/vfs_inode.c                        |    5 +-
 fs/affs/inode.c                          |    2 +-
 fs/afs/dir.c                             |    2 +-
 fs/afs/write.c                           |    6 +-
 fs/anon_inodes.c                         |    5 +-
 fs/bfs/dir.c                             |    2 +-
 fs/block_dev.c                           |   26 +-
 fs/btrfs/disk-io.c                       |    2 +-
 fs/btrfs/file.c                          |    2 +-
 fs/btrfs/inode.c                         |   28 +-
 fs/buffer.c                              |    4 +-
 fs/ceph/addr.c                           |    2 +-
 fs/ceph/inode.c                          |    4 +-
 fs/ceph/mds_client.c                     |    2 +-
 fs/cifs/file.c                           |    2 +-
 fs/cifs/inode.c                          |    4 +-
 fs/coda/dir.c                            |    2 +-
 fs/configfs/inode.c                      |    3 +-
 fs/drop_caches.c                         |   19 +-
 fs/exofs/inode.c                         |    6 +-
 fs/exofs/namei.c                         |    2 +-
 fs/ext2/ialloc.c                         |    2 +-
 fs/ext2/namei.c                          |    2 +-
 fs/ext3/ialloc.c                         |    4 +-
 fs/ext3/namei.c                          |    2 +-
 fs/ext4/ialloc.c                         |    4 +-
 fs/ext4/namei.c                          |    2 +-
 fs/fs-writeback.c                        |  184 ++++----
 fs/fuse/file.c                           |    6 +-
 fs/fuse/inode.c                          |    2 +-
 fs/gfs2/glock.c                          |    3 +-
 fs/gfs2/ops_inode.c                      |    2 +-
 fs/hfs/hfs_fs.h                          |    2 +-
 fs/hfs/inode.c                           |    2 +-
 fs/hfsplus/dir.c                         |    2 +-
 fs/hfsplus/hfsplus_fs.h                  |    2 +-
 fs/hfsplus/inode.c                       |    2 +-
 fs/hpfs/inode.c                          |    2 +-
 fs/hugetlbfs/inode.c                     |    3 +-
 fs/inode.c                               |  764 ++++++++++++++++++++----------
 fs/internal.h                            |    6 +
 fs/jffs2/dir.c                           |    4 +-
 fs/jfs/jfs_txnmgr.c                      |    2 +-
 fs/jfs/namei.c                           |    2 +-
 fs/libfs.c                               |    2 +-
 fs/locks.c                               |    2 +-
 fs/logfs/dir.c                           |    2 +-
 fs/logfs/inode.c                         |    2 +-
 fs/logfs/readwrite.c                     |    2 +-
 fs/minix/namei.c                         |    2 +-
 fs/namei.c                               |    2 +-
 fs/nfs/dir.c                             |    2 +-
 fs/nfs/getroot.c                         |    2 +-
 fs/nfs/inode.c                           |    7 +-
 fs/nfs/nfs4state.c                       |    2 +-
 fs/nfs/write.c                           |    9 +-
 fs/nilfs2/btnode.c                       |    2 +-
 fs/nilfs2/gcdat.c                        |    1 +
 fs/nilfs2/gcinode.c                      |   22 +-
 fs/nilfs2/mdt.c                          |    7 +-
 fs/nilfs2/namei.c                        |    2 +-
 fs/nilfs2/segment.c                      |    2 +-
 fs/nilfs2/the_nilfs.c                    |    2 +-
 fs/nilfs2/the_nilfs.h                    |    2 +-
 fs/notify/inode_mark.c                   |   47 ++-
 fs/notify/mark.c                         |    1 -
 fs/notify/vfsmount_mark.c                |    1 -
 fs/ntfs/file.c                           |    2 +-
 fs/ntfs/inode.c                          |    4 +-
 fs/ntfs/super.c                          |    4 +-
 fs/ocfs2/dlmfs/dlmfs.c                   |    4 +-
 fs/ocfs2/file.c                          |    2 +-
 fs/ocfs2/inode.c                         |    2 +-
 fs/ocfs2/namei.c                         |    2 +-
 fs/quota/dquot.c                         |   32 +-
 fs/ramfs/inode.c                         |    2 +-
 fs/reiserfs/namei.c                      |    2 +-
 fs/reiserfs/stree.c                      |    2 +-
 fs/reiserfs/xattr.c                      |    2 +-
 fs/romfs/super.c                         |    4 +-
 fs/smbfs/inode.c                         |    2 +-
 fs/super.c                               |    1 +
 fs/sysfs/inode.c                         |    2 +-
 fs/sysv/namei.c                          |    2 +-
 fs/ubifs/dir.c                           |    4 +-
 fs/ubifs/super.c                         |    4 +-
 fs/udf/namei.c                           |    2 +-
 fs/ufs/namei.c                           |    2 +-
 fs/xfs/linux-2.6/xfs_buf.c               |    4 +-
 fs/xfs/linux-2.6/xfs_file.c              |    2 +-
 fs/xfs/linux-2.6/xfs_iops.c              |    2 +-
 fs/xfs/linux-2.6/xfs_trace.h             |    2 +-
 fs/xfs/xfs_inode.h                       |    4 +-
 include/linux/backing-dev.h              |   17 +-
 include/linux/fs.h                       |   34 +-
 include/linux/list_bl.h                  |  127 +++++
 include/linux/poison.h                   |    2 +
 include/linux/writeback.h                |   13 +-
 ipc/mqueue.c                             |    2 +-
 kernel/cgroup.c                          |    2 +-
 kernel/futex.c                           |    2 +-
 kernel/sysctl.c                          |    4 +-
 mm/backing-dev.c                         |   90 ++++-
 mm/fadvise.c                             |    4 +-
 mm/filemap.c                             |   10 +-
 mm/filemap_xip.c                         |    2 +-
 mm/page-writeback.c                      |   15 +-
 mm/readahead.c                           |    6 +-
 mm/rmap.c                                |    6 +-
 mm/shmem.c                               |    8 +-
 mm/swap.c                                |    2 +-
 mm/swap_state.c                          |    2 +-
 mm/swapfile.c                            |    2 +-
 mm/truncate.c                            |    3 +-
 mm/vmscan.c                              |    2 +-
 net/socket.c                             |    2 +-
 124 files changed, 1131 insertions(+), 616 deletions(-)
 create mode 100644 include/linux/list_bl.h

^ permalink raw reply

* [PATCH 10/18] fs: Factor inode hash operations into functions
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Dave Chinner <dchinner@redhat.com>

Before replacing the inode hash locking with a more scalable
mechanism, factor the removal of the inode from the hashes rather
than open coding it in several places.

Based on a patch originally from Nick Piggin.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/inode.c |  100 +++++++++++++++++++++++++++++++++--------------------------
 1 files changed, 56 insertions(+), 44 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index 5c8a3ea..32da15e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -355,6 +355,59 @@ int iref_read(struct inode *inode)
 }
 EXPORT_SYMBOL_GPL(iref_read);
 
+static unsigned long hash(struct super_block *sb, unsigned long hashval)
+{
+	unsigned long tmp;
+
+	tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) /
+			L1_CACHE_BYTES;
+	tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> I_HASHBITS);
+	return tmp & I_HASHMASK;
+}
+
+/**
+ *	__insert_inode_hash - hash an inode
+ *	@inode: unhashed inode
+ *	@hashval: unsigned long value used to locate this object in the
+ *		inode_hashtable.
+ *
+ *	Add an inode to the inode hash for this superblock.
+ */
+void __insert_inode_hash(struct inode *inode, unsigned long hashval)
+{
+	struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
+	spin_lock(&inode_lock);
+	hlist_add_head(&inode->i_hash, head);
+	spin_unlock(&inode_lock);
+}
+EXPORT_SYMBOL(__insert_inode_hash);
+
+/**
+ *	__remove_inode_hash - remove an inode from the hash
+ *	@inode: inode to unhash
+ *
+ *	Remove an inode from the superblock. inode->i_lock must be
+ *	held.
+ */
+static void __remove_inode_hash(struct inode *inode)
+{
+	hlist_del_init(&inode->i_hash);
+}
+
+/**
+ *	remove_inode_hash - remove an inode from the hash
+ *	@inode: inode to unhash
+ *
+ *	Remove an inode from the superblock.
+ */
+void remove_inode_hash(struct inode *inode)
+{
+	spin_lock(&inode_lock);
+	hlist_del_init(&inode->i_hash);
+	spin_unlock(&inode_lock);
+}
+EXPORT_SYMBOL(remove_inode_hash);
+
 void end_writeback(struct inode *inode)
 {
 	might_sleep();
@@ -402,7 +455,7 @@ static void dispose_list(struct list_head *head)
 		evict(inode);
 
 		spin_lock(&inode_lock);
-		hlist_del_init(&inode->i_hash);
+		__remove_inode_hash(inode);
 		list_del_init(&inode->i_sb_list);
 		spin_unlock(&inode_lock);
 
@@ -657,16 +710,6 @@ repeat:
 	return node ? inode : NULL;
 }
 
-static unsigned long hash(struct super_block *sb, unsigned long hashval)
-{
-	unsigned long tmp;
-
-	tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) /
-			L1_CACHE_BYTES;
-	tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> I_HASHBITS);
-	return tmp & I_HASHMASK;
-}
-
 static inline void
 __inode_add_to_lists(struct super_block *sb, struct hlist_head *head,
 			struct inode *inode)
@@ -1231,36 +1274,6 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
 }
 EXPORT_SYMBOL(insert_inode_locked4);
 
-/**
- *	__insert_inode_hash - hash an inode
- *	@inode: unhashed inode
- *	@hashval: unsigned long value used to locate this object in the
- *		inode_hashtable.
- *
- *	Add an inode to the inode hash for this superblock.
- */
-void __insert_inode_hash(struct inode *inode, unsigned long hashval)
-{
-	struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
-	spin_lock(&inode_lock);
-	hlist_add_head(&inode->i_hash, head);
-	spin_unlock(&inode_lock);
-}
-EXPORT_SYMBOL(__insert_inode_hash);
-
-/**
- *	remove_inode_hash - remove an inode from the hash
- *	@inode: inode to unhash
- *
- *	Remove an inode from the superblock.
- */
-void remove_inode_hash(struct inode *inode)
-{
-	spin_lock(&inode_lock);
-	hlist_del_init(&inode->i_hash);
-	spin_unlock(&inode_lock);
-}
-EXPORT_SYMBOL(remove_inode_hash);
 
 int generic_delete_inode(struct inode *inode)
 {
@@ -1319,6 +1332,7 @@ static void iput_final(struct inode *inode)
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state &= ~I_WILL_FREE;
 		hlist_del_init(&inode->i_hash);
+		__remove_inode_hash(inode);
 	}
 	list_del_init(&inode->i_io);
 	WARN_ON(inode->i_state & I_NEW);
@@ -1337,9 +1351,7 @@ static void iput_final(struct inode *inode)
 	list_del_init(&inode->i_sb_list);
 	spin_unlock(&inode_lock);
 	evict(inode);
-	spin_lock(&inode_lock);
-	hlist_del_init(&inode->i_hash);
-	spin_unlock(&inode_lock);
+	remove_inode_hash(inode);
 	wake_up_inode(inode);
 	BUG_ON(inode->i_state != (I_FREEING | I_CLEAR));
 	destroy_inode(inode);
-- 
1.7.1


^ permalink raw reply related

* [PATCH 07/18] exofs: use iput() for inode reference count decrements
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Dave Chinner <dchinner@redhat.com>

Direct modification of the inode reference count is a no-no. Convert
the exofs decrements to call iput() instead of acting directly on
i_count.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/exofs/inode.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index b631ff3..0fb4d4c 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -1101,7 +1101,7 @@ static void create_done(struct exofs_io_state *ios, void *p)
 
 	set_obj_created(oi);
 
-	atomic_dec(&inode->i_count);
+	iput(inode);
 	wake_up(&oi->i_wq);
 }
 
@@ -1161,7 +1161,7 @@ struct inode *exofs_new_inode(struct inode *dir, int mode)
 	ios->cred = oi->i_cred;
 	ret = exofs_sbi_create(ios);
 	if (ret) {
-		atomic_dec(&inode->i_count);
+		iput(inode);
 		exofs_put_io_state(ios);
 		return ERR_PTR(ret);
 	}
-- 
1.7.1


^ permalink raw reply related

* [PATCH 01/18] kernel: add bl_list
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Nick Piggin <npiggin@suse.de>

Introduce a type of hlist that can support the use of the lowest bit
in the hlist_head. This will be subsequently used to implement
per-bucket bit spinlock for inode hashes.

Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 include/linux/list_bl.h |  127 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/poison.h  |    2 +
 2 files changed, 129 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/list_bl.h

diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h
new file mode 100644
index 0000000..961bc89
--- /dev/null
+++ b/include/linux/list_bl.h
@@ -0,0 +1,127 @@
+#ifndef _LINUX_LIST_BL_H
+#define _LINUX_LIST_BL_H
+
+#include <linux/list.h>
+#include <linux/bit_spinlock.h>
+
+/*
+ * Special version of lists, where head of the list has a bit spinlock
+ * in the lowest bit. This is useful for scalable hash tables without
+ * increasing memory footprint overhead.
+ *
+ * For modification operations, the 0 bit of hlist_bl_head->first
+ * pointer must be set.
+ */
+
+#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)
+#define LIST_BL_LOCKMASK	1UL
+#else
+#define LIST_BL_LOCKMASK	0UL
+#endif
+
+#ifdef CONFIG_DEBUG_LIST
+#define LIST_BL_BUG_ON(x) BUG_ON(x)
+#else
+#define LIST_BL_BUG_ON(x)
+#endif
+
+
+struct hlist_bl_head {
+	struct hlist_bl_node *first;
+};
+
+struct hlist_bl_node {
+	struct hlist_bl_node *next, **pprev;
+};
+#define INIT_HLIST_BL_HEAD(ptr) \
+	((ptr)->first = NULL)
+
+static inline void init_hlist_bl_node(struct hlist_bl_node *h)
+{
+	h->next = NULL;
+	h->pprev = NULL;
+}
+
+#define hlist_bl_entry(ptr, type, member) container_of(ptr, type, member)
+
+static inline int hlist_bl_unhashed(const struct hlist_bl_node *h)
+{
+	return !h->pprev;
+}
+
+static inline struct hlist_bl_node *hlist_bl_first(struct hlist_bl_head *h)
+{
+	return (struct hlist_bl_node *)
+		((unsigned long)h->first & ~LIST_BL_LOCKMASK);
+}
+
+static inline void hlist_bl_set_first(struct hlist_bl_head *h,
+					struct hlist_bl_node *n)
+{
+	LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
+	LIST_BL_BUG_ON(!bit_spin_is_locked(0, (unsigned long *)&h->first));
+	h->first = (struct hlist_bl_node *)((unsigned long)n | LIST_BL_LOCKMASK);
+}
+
+static inline int hlist_bl_empty(const struct hlist_bl_head *h)
+{
+	return !((unsigned long)h->first & ~LIST_BL_LOCKMASK);
+}
+
+static inline void hlist_bl_add_head(struct hlist_bl_node *n,
+					struct hlist_bl_head *h)
+{
+	struct hlist_bl_node *first = hlist_bl_first(h);
+
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	n->pprev = &h->first;
+	hlist_bl_set_first(h, n);
+}
+
+static inline void __hlist_bl_del(struct hlist_bl_node *n)
+{
+	struct hlist_bl_node *next = n->next;
+	struct hlist_bl_node **pprev = n->pprev;
+
+	LIST_BL_BUG_ON((unsigned long)n & LIST_BL_LOCKMASK);
+
+	/* pprev may be `first`, so be careful not to lose the lock bit */
+	*pprev = (struct hlist_bl_node *)
+			((unsigned long)next |
+			 ((unsigned long)*pprev & LIST_BL_LOCKMASK));
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_bl_del(struct hlist_bl_node *n)
+{
+	__hlist_bl_del(n);
+	n->next = BL_LIST_POISON1;
+	n->pprev = BL_LIST_POISON2;
+}
+
+static inline void hlist_bl_del_init(struct hlist_bl_node *n)
+{
+	if (!hlist_bl_unhashed(n)) {
+		__hlist_bl_del(n);
+		init_hlist_bl_node(n);
+	}
+}
+
+/**
+ * hlist_bl_for_each_entry	- iterate over list of given type
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct hlist_node to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ *
+ */
+#define hlist_bl_for_each_entry(tpos, pos, head, member)		\
+	for (pos = hlist_bl_first(head);				\
+	     pos &&							\
+		({ tpos = hlist_bl_entry(pos, typeof(*tpos), member); 1; }); \
+	     pos = pos->next)
+
+#endif
diff --git a/include/linux/poison.h b/include/linux/poison.h
index 2110a81..d367d39 100644
--- a/include/linux/poison.h
+++ b/include/linux/poison.h
@@ -22,6 +22,8 @@
 #define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
 #define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)
 
+#define BL_LIST_POISON1  ((void *) 0x00300300 + POISON_POINTER_DELTA)
+#define BL_LIST_POISON2  ((void *) 0x00400400 + POISON_POINTER_DELTA)
 /********** include/linux/timer.h **********/
 /*
  * Magic number "tsta" to indicate a static timer initializer
-- 
1.7.1


^ permalink raw reply related

* [PATCH 11/18] fs: Introduce per-bucket inode hash locks
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Nick Piggin <npiggin@suse.de>

Protect the inod hash with a single lock is not scalable.  Convert
the inode hash to use the new bit-locked hash list implementation
that allows per-bucket locks to be used. This allows us to replace
the global inode_lock with finer grained locking without increasing
the size of the hash table.

Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/btrfs/inode.c        |    2 +-
 fs/fs-writeback.c       |    2 +-
 fs/hfs/hfs_fs.h         |    2 +-
 fs/hfs/inode.c          |    2 +-
 fs/hfsplus/hfsplus_fs.h |    2 +-
 fs/hfsplus/inode.c      |    2 +-
 fs/inode.c              |  165 ++++++++++++++++++++++++++++++----------------
 fs/nilfs2/gcinode.c     |   22 ++++---
 fs/nilfs2/segment.c     |    2 +-
 fs/nilfs2/the_nilfs.h   |    2 +-
 fs/reiserfs/xattr.c     |    2 +-
 include/linux/fs.h      |    3 +-
 mm/shmem.c              |    4 +-
 13 files changed, 132 insertions(+), 80 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 9f04478..f908a12 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3855,7 +3855,7 @@ again:
 	p = &root->inode_tree.rb_node;
 	parent = NULL;
 
-	if (hlist_unhashed(&inode->i_hash))
+	if (hlist_bl_unhashed(&inode->i_hash))
 		return;
 
 	spin_lock(&root->inode_lock);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index ec7a689..d63ab47 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -959,7 +959,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 		 * dirty list.  Add blockdev inodes as well.
 		 */
 		if (!S_ISBLK(inode->i_mode)) {
-			if (hlist_unhashed(&inode->i_hash))
+			if (hlist_bl_unhashed(&inode->i_hash))
 				goto out;
 		}
 		if (inode->i_state & I_FREEING)
diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index 4f55651..24591be 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -148,7 +148,7 @@ struct hfs_sb_info {
 
 	int fs_div;
 
-	struct hlist_head rsrc_inodes;
+	struct hlist_bl_head rsrc_inodes;
 };
 
 #define HFS_FLG_BITMAP_DIRTY	0
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 397b7ad..7778298 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -524,7 +524,7 @@ static struct dentry *hfs_file_lookup(struct inode *dir, struct dentry *dentry,
 	HFS_I(inode)->rsrc_inode = dir;
 	HFS_I(dir)->rsrc_inode = inode;
 	igrab(dir);
-	hlist_add_head(&inode->i_hash, &HFS_SB(dir->i_sb)->rsrc_inodes);
+	hlist_bl_add_head(&inode->i_hash, &HFS_SB(dir->i_sb)->rsrc_inodes);
 	mark_inode_dirty(inode);
 out:
 	d_add(dentry, inode);
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index dc856be..499f5a5 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -144,7 +144,7 @@ struct hfsplus_sb_info {
 
 	unsigned long flags;
 
-	struct hlist_head rsrc_inodes;
+	struct hlist_bl_head rsrc_inodes;
 };
 
 #define HFSPLUS_SB_WRITEBACKUP	0x0001
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
index c5a979d..b755cf0 100644
--- a/fs/hfsplus/inode.c
+++ b/fs/hfsplus/inode.c
@@ -202,7 +202,7 @@ static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dent
 	HFSPLUS_I(inode).rsrc_inode = dir;
 	HFSPLUS_I(dir).rsrc_inode = inode;
 	igrab(dir);
-	hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes);
+	hlist_bl_add_head(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes);
 	mark_inode_dirty(inode);
 out:
 	d_add(dentry, inode);
diff --git a/fs/inode.c b/fs/inode.c
index 32da15e..3c07719 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -24,12 +24,20 @@
 #include <linux/mount.h>
 #include <linux/async.h>
 #include <linux/posix_acl.h>
+#include <linux/bit_spinlock.h>
 
 /*
  * Locking rules.
  *
  * inode->i_lock protects:
  *   i_ref
+ * inode_hash_bucket lock protects:
+ *   inode hash table, i_hash
+ *
+ * Lock orders
+ * inode_lock
+ *   inode hash bucket lock
+ *     inode->i_lock
  */
 
 /*
@@ -80,7 +88,22 @@ static unsigned int i_hash_shift __read_mostly;
  */
 
 LIST_HEAD(inode_unused);
-static struct hlist_head *inode_hashtable __read_mostly;
+
+struct inode_hash_bucket {
+	struct hlist_bl_head head;
+};
+
+static inline void spin_lock_bucket(struct inode_hash_bucket *b)
+{
+	bit_spin_lock(0, (unsigned long *)b);
+}
+
+static inline void spin_unlock_bucket(struct inode_hash_bucket *b)
+{
+	__bit_spin_unlock(0, (unsigned long *)b);
+}
+
+static struct inode_hash_bucket *inode_hashtable __read_mostly;
 
 /*
  * A simple spinlock to protect the list manipulations.
@@ -295,7 +318,7 @@ void destroy_inode(struct inode *inode)
 void inode_init_once(struct inode *inode)
 {
 	memset(inode, 0, sizeof(*inode));
-	INIT_HLIST_NODE(&inode->i_hash);
+	init_hlist_bl_node(&inode->i_hash);
 	INIT_LIST_HEAD(&inode->i_dentry);
 	INIT_LIST_HEAD(&inode->i_devices);
 	INIT_LIST_HEAD(&inode->i_io);
@@ -375,9 +398,13 @@ static unsigned long hash(struct super_block *sb, unsigned long hashval)
  */
 void __insert_inode_hash(struct inode *inode, unsigned long hashval)
 {
-	struct hlist_head *head = inode_hashtable + hash(inode->i_sb, hashval);
+	struct inode_hash_bucket *b;
+
+	b = inode_hashtable + hash(inode->i_sb, hashval);
 	spin_lock(&inode_lock);
-	hlist_add_head(&inode->i_hash, head);
+	spin_lock_bucket(b);
+	hlist_bl_add_head(&inode->i_hash, &b->head);
+	spin_unlock_bucket(b);
 	spin_unlock(&inode_lock);
 }
 EXPORT_SYMBOL(__insert_inode_hash);
@@ -391,7 +418,12 @@ EXPORT_SYMBOL(__insert_inode_hash);
  */
 static void __remove_inode_hash(struct inode *inode)
 {
-	hlist_del_init(&inode->i_hash);
+	struct inode_hash_bucket *b;
+
+	b = inode_hashtable + hash(inode->i_sb, inode->i_ino);
+	spin_lock_bucket(b);
+	hlist_bl_del_init(&inode->i_hash);
+	spin_unlock_bucket(b);
 }
 
 /**
@@ -403,7 +435,7 @@ static void __remove_inode_hash(struct inode *inode)
 void remove_inode_hash(struct inode *inode)
 {
 	spin_lock(&inode_lock);
-	hlist_del_init(&inode->i_hash);
+	__remove_inode_hash(inode);
 	spin_unlock(&inode_lock);
 }
 EXPORT_SYMBOL(remove_inode_hash);
@@ -663,25 +695,28 @@ static void __wait_on_freeing_inode(struct inode *inode);
  * add any additional branch in the common code.
  */
 static struct inode *find_inode(struct super_block *sb,
-				struct hlist_head *head,
+				struct inode_hash_bucket *b,
 				int (*test)(struct inode *, void *),
 				void *data)
 {
-	struct hlist_node *node;
+	struct hlist_bl_node *node;
 	struct inode *inode = NULL;
 
 repeat:
-	hlist_for_each_entry(inode, node, head, i_hash) {
+	spin_lock_bucket(b);
+	hlist_bl_for_each_entry(inode, node, &b->head, i_hash) {
 		if (inode->i_sb != sb)
 			continue;
 		if (!test(inode, data))
 			continue;
 		if (inode->i_state & (I_FREEING|I_WILL_FREE)) {
+			spin_unlock_bucket(b);
 			__wait_on_freeing_inode(inode);
 			goto repeat;
 		}
 		break;
 	}
+	spin_unlock_bucket(b);
 	return node ? inode : NULL;
 }
 
@@ -690,33 +725,40 @@ repeat:
  * iget_locked for details.
  */
 static struct inode *find_inode_fast(struct super_block *sb,
-				struct hlist_head *head, unsigned long ino)
+				struct inode_hash_bucket *b,
+				unsigned long ino)
 {
-	struct hlist_node *node;
+	struct hlist_bl_node *node;
 	struct inode *inode = NULL;
 
 repeat:
-	hlist_for_each_entry(inode, node, head, i_hash) {
+	spin_lock_bucket(b);
+	hlist_bl_for_each_entry(inode, node, &b->head, i_hash) {
 		if (inode->i_ino != ino)
 			continue;
 		if (inode->i_sb != sb)
 			continue;
 		if (inode->i_state & (I_FREEING|I_WILL_FREE)) {
+			spin_unlock_bucket(b);
 			__wait_on_freeing_inode(inode);
 			goto repeat;
 		}
 		break;
 	}
+	spin_unlock_bucket(b);
 	return node ? inode : NULL;
 }
 
 static inline void
-__inode_add_to_lists(struct super_block *sb, struct hlist_head *head,
+__inode_add_to_lists(struct super_block *sb, struct inode_hash_bucket *b,
 			struct inode *inode)
 {
 	list_add(&inode->i_sb_list, &sb->s_inodes);
-	if (head)
-		hlist_add_head(&inode->i_hash, head);
+	if (b) {
+		spin_lock_bucket(b);
+		hlist_bl_add_head(&inode->i_hash, &b->head);
+		spin_unlock_bucket(b);
+	}
 }
 
 /**
@@ -733,10 +775,10 @@ __inode_add_to_lists(struct super_block *sb, struct hlist_head *head,
  */
 void inode_add_to_lists(struct super_block *sb, struct inode *inode)
 {
-	struct hlist_head *head = inode_hashtable + hash(sb, inode->i_ino);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, inode->i_ino);
 
 	spin_lock(&inode_lock);
-	__inode_add_to_lists(sb, head, inode);
+	__inode_add_to_lists(sb, b, inode);
 	spin_unlock(&inode_lock);
 }
 EXPORT_SYMBOL_GPL(inode_add_to_lists);
@@ -819,7 +861,7 @@ EXPORT_SYMBOL(unlock_new_inode);
  *	-- rmk@arm.uk.linux.org
  */
 static struct inode *get_new_inode(struct super_block *sb,
-				struct hlist_head *head,
+				struct inode_hash_bucket *b,
 				int (*test)(struct inode *, void *),
 				int (*set)(struct inode *, void *),
 				void *data)
@@ -832,12 +874,12 @@ static struct inode *get_new_inode(struct super_block *sb,
 
 		spin_lock(&inode_lock);
 		/* We released the lock, so.. */
-		old = find_inode(sb, head, test, data);
+		old = find_inode(sb, b, test, data);
 		if (!old) {
 			if (set(inode, data))
 				goto set_failed;
 
-			__inode_add_to_lists(sb, head, inode);
+			__inode_add_to_lists(sb, b, inode);
 			inode->i_state = I_NEW;
 			spin_unlock(&inode_lock);
 
@@ -873,7 +915,7 @@ set_failed:
  * comment at iget_locked for details.
  */
 static struct inode *get_new_inode_fast(struct super_block *sb,
-				struct hlist_head *head, unsigned long ino)
+				struct inode_hash_bucket *b, unsigned long ino)
 {
 	struct inode *inode;
 
@@ -883,10 +925,10 @@ static struct inode *get_new_inode_fast(struct super_block *sb,
 
 		spin_lock(&inode_lock);
 		/* We released the lock, so.. */
-		old = find_inode_fast(sb, head, ino);
+		old = find_inode_fast(sb, b, ino);
 		if (!old) {
 			inode->i_ino = ino;
-			__inode_add_to_lists(sb, head, inode);
+			__inode_add_to_lists(sb, b, inode);
 			inode->i_state = I_NEW;
 			spin_unlock(&inode_lock);
 
@@ -935,7 +977,7 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved)
 	 */
 	static unsigned int counter;
 	struct inode *inode;
-	struct hlist_head *head;
+	struct inode_hash_bucket *b;
 	ino_t res;
 
 	spin_lock(&inode_lock);
@@ -943,8 +985,8 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved)
 		if (counter <= max_reserved)
 			counter = max_reserved + 1;
 		res = counter++;
-		head = inode_hashtable + hash(sb, res);
-		inode = find_inode_fast(sb, head, res);
+		b = inode_hashtable + hash(sb, res);
+		inode = find_inode_fast(sb, b, res);
 	} while (inode != NULL);
 	spin_unlock(&inode_lock);
 
@@ -991,13 +1033,14 @@ EXPORT_SYMBOL(igrab);
  * Note, @test is called with the inode_lock held, so can't sleep.
  */
 static struct inode *ifind(struct super_block *sb,
-		struct hlist_head *head, int (*test)(struct inode *, void *),
+		struct inode_hash_bucket *b,
+		int (*test)(struct inode *, void *),
 		void *data, const int wait)
 {
 	struct inode *inode;
 
 	spin_lock(&inode_lock);
-	inode = find_inode(sb, head, test, data);
+	inode = find_inode(sb, b, test, data);
 	if (inode) {
 		spin_lock(&inode->i_lock);
 		iref_locked(inode);
@@ -1027,12 +1070,13 @@ static struct inode *ifind(struct super_block *sb,
  * Otherwise NULL is returned.
  */
 static struct inode *ifind_fast(struct super_block *sb,
-		struct hlist_head *head, unsigned long ino)
+		struct inode_hash_bucket *b,
+		unsigned long ino)
 {
 	struct inode *inode;
 
 	spin_lock(&inode_lock);
-	inode = find_inode_fast(sb, head, ino);
+	inode = find_inode_fast(sb, b, ino);
 	if (inode) {
 		spin_lock(&inode->i_lock);
 		iref_locked(inode);
@@ -1069,9 +1113,9 @@ static struct inode *ifind_fast(struct super_block *sb,
 struct inode *ilookup5_nowait(struct super_block *sb, unsigned long hashval,
 		int (*test)(struct inode *, void *), void *data)
 {
-	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, hashval);
 
-	return ifind(sb, head, test, data, 0);
+	return ifind(sb, b, test, data, 0);
 }
 EXPORT_SYMBOL(ilookup5_nowait);
 
@@ -1097,9 +1141,9 @@ EXPORT_SYMBOL(ilookup5_nowait);
 struct inode *ilookup5(struct super_block *sb, unsigned long hashval,
 		int (*test)(struct inode *, void *), void *data)
 {
-	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, hashval);
 
-	return ifind(sb, head, test, data, 1);
+	return ifind(sb, b, test, data, 1);
 }
 EXPORT_SYMBOL(ilookup5);
 
@@ -1119,9 +1163,9 @@ EXPORT_SYMBOL(ilookup5);
  */
 struct inode *ilookup(struct super_block *sb, unsigned long ino)
 {
-	struct hlist_head *head = inode_hashtable + hash(sb, ino);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, ino);
 
-	return ifind_fast(sb, head, ino);
+	return ifind_fast(sb, b, ino);
 }
 EXPORT_SYMBOL(ilookup);
 
@@ -1149,17 +1193,17 @@ struct inode *iget5_locked(struct super_block *sb, unsigned long hashval,
 		int (*test)(struct inode *, void *),
 		int (*set)(struct inode *, void *), void *data)
 {
-	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, hashval);
 	struct inode *inode;
 
-	inode = ifind(sb, head, test, data, 1);
+	inode = ifind(sb, b, test, data, 1);
 	if (inode)
 		return inode;
 	/*
 	 * get_new_inode() will do the right thing, re-trying the search
 	 * in case it had to block at any point.
 	 */
-	return get_new_inode(sb, head, test, set, data);
+	return get_new_inode(sb, b, test, set, data);
 }
 EXPORT_SYMBOL(iget5_locked);
 
@@ -1180,17 +1224,17 @@ EXPORT_SYMBOL(iget5_locked);
  */
 struct inode *iget_locked(struct super_block *sb, unsigned long ino)
 {
-	struct hlist_head *head = inode_hashtable + hash(sb, ino);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, ino);
 	struct inode *inode;
 
-	inode = ifind_fast(sb, head, ino);
+	inode = ifind_fast(sb, b, ino);
 	if (inode)
 		return inode;
 	/*
 	 * get_new_inode_fast() will do the right thing, re-trying the search
 	 * in case it had to block at any point.
 	 */
-	return get_new_inode_fast(sb, head, ino);
+	return get_new_inode_fast(sb, b, ino);
 }
 EXPORT_SYMBOL(iget_locked);
 
@@ -1198,14 +1242,15 @@ int insert_inode_locked(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	ino_t ino = inode->i_ino;
-	struct hlist_head *head = inode_hashtable + hash(sb, ino);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, ino);
 
 	inode->i_state |= I_NEW;
 	while (1) {
-		struct hlist_node *node;
+		struct hlist_bl_node *node;
 		struct inode *old = NULL;
 		spin_lock(&inode_lock);
-		hlist_for_each_entry(old, node, head, i_hash) {
+		spin_lock_bucket(b);
+		hlist_bl_for_each_entry(old, node, &b->head, i_hash) {
 			if (old->i_ino != ino)
 				continue;
 			if (old->i_sb != sb)
@@ -1215,16 +1260,18 @@ int insert_inode_locked(struct inode *inode)
 			break;
 		}
 		if (likely(!node)) {
-			hlist_add_head(&inode->i_hash, head);
+			hlist_bl_add_head(&inode->i_hash, &b->head);
+			spin_unlock_bucket(b);
 			spin_unlock(&inode_lock);
 			return 0;
 		}
 		spin_lock(&old->i_lock);
 		iref_locked(old);
 		spin_unlock(&old->i_lock);
+		spin_unlock_bucket(b);
 		spin_unlock(&inode_lock);
 		wait_on_inode(old);
-		if (unlikely(!hlist_unhashed(&old->i_hash))) {
+		if (unlikely(!hlist_bl_unhashed(&old->i_hash))) {
 			iput(old);
 			return -EBUSY;
 		}
@@ -1237,16 +1284,17 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
 		int (*test)(struct inode *, void *), void *data)
 {
 	struct super_block *sb = inode->i_sb;
-	struct hlist_head *head = inode_hashtable + hash(sb, hashval);
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, hashval);
 
 	inode->i_state |= I_NEW;
 
 	while (1) {
-		struct hlist_node *node;
+		struct hlist_bl_node *node;
 		struct inode *old = NULL;
 
 		spin_lock(&inode_lock);
-		hlist_for_each_entry(old, node, head, i_hash) {
+		spin_lock_bucket(b);
+		hlist_bl_for_each_entry(old, node, &b->head, i_hash) {
 			if (old->i_sb != sb)
 				continue;
 			if (!test(old, data))
@@ -1256,16 +1304,18 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
 			break;
 		}
 		if (likely(!node)) {
-			hlist_add_head(&inode->i_hash, head);
+			hlist_bl_add_head(&inode->i_hash, &b->head);
+			spin_unlock_bucket(b);
 			spin_unlock(&inode_lock);
 			return 0;
 		}
 		spin_lock(&old->i_lock);
 		iref_locked(old);
 		spin_unlock(&old->i_lock);
+		spin_unlock_bucket(b);
 		spin_unlock(&inode_lock);
 		wait_on_inode(old);
-		if (unlikely(!hlist_unhashed(&old->i_hash))) {
+		if (unlikely(!hlist_bl_unhashed(&old->i_hash))) {
 			iput(old);
 			return -EBUSY;
 		}
@@ -1288,7 +1338,7 @@ EXPORT_SYMBOL(generic_delete_inode);
  */
 int generic_drop_inode(struct inode *inode)
 {
-	return !inode->i_nlink || hlist_unhashed(&inode->i_hash);
+	return !inode->i_nlink || hlist_bl_unhashed(&inode->i_hash);
 }
 EXPORT_SYMBOL_GPL(generic_drop_inode);
 
@@ -1331,7 +1381,6 @@ static void iput_final(struct inode *inode)
 		spin_lock(&inode_lock);
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state &= ~I_WILL_FREE;
-		hlist_del_init(&inode->i_hash);
 		__remove_inode_hash(inode);
 	}
 	list_del_init(&inode->i_io);
@@ -1599,7 +1648,7 @@ void __init inode_init_early(void)
 
 	inode_hashtable =
 		alloc_large_system_hash("Inode-cache",
-					sizeof(struct hlist_head),
+					sizeof(struct inode_hash_bucket),
 					ihash_entries,
 					14,
 					HASH_EARLY,
@@ -1608,7 +1657,7 @@ void __init inode_init_early(void)
 					0);
 
 	for (loop = 0; loop < (1 << i_hash_shift); loop++)
-		INIT_HLIST_HEAD(&inode_hashtable[loop]);
+		INIT_HLIST_BL_HEAD(&inode_hashtable[loop].head);
 
 }
 
@@ -1633,7 +1682,7 @@ void __init inode_init(void)
 
 	inode_hashtable =
 		alloc_large_system_hash("Inode-cache",
-					sizeof(struct hlist_head),
+					sizeof(struct inode_hash_bucket),
 					ihash_entries,
 					14,
 					0,
@@ -1642,7 +1691,7 @@ void __init inode_init(void)
 					0);
 
 	for (loop = 0; loop < (1 << i_hash_shift); loop++)
-		INIT_HLIST_HEAD(&inode_hashtable[loop]);
+		INIT_HLIST_BL_HEAD(&inode_hashtable[loop].head);
 }
 
 void init_special_inode(struct inode *inode, umode_t mode, dev_t rdev)
diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c
index bed3a78..ce7344e 100644
--- a/fs/nilfs2/gcinode.c
+++ b/fs/nilfs2/gcinode.c
@@ -196,13 +196,13 @@ int nilfs_init_gccache(struct the_nilfs *nilfs)
 	INIT_LIST_HEAD(&nilfs->ns_gc_inodes);
 
 	nilfs->ns_gc_inodes_h =
-		kmalloc(sizeof(struct hlist_head) * NILFS_GCINODE_HASH_SIZE,
+		kmalloc(sizeof(struct hlist_bl_head) * NILFS_GCINODE_HASH_SIZE,
 			GFP_NOFS);
 	if (nilfs->ns_gc_inodes_h == NULL)
 		return -ENOMEM;
 
 	for (loop = 0; loop < NILFS_GCINODE_HASH_SIZE; loop++)
-		INIT_HLIST_HEAD(&nilfs->ns_gc_inodes_h[loop]);
+		INIT_HLIST_BL_HEAD(&nilfs->ns_gc_inodes_h[loop]);
 	return 0;
 }
 
@@ -254,18 +254,18 @@ static unsigned long ihash(ino_t ino, __u64 cno)
  */
 struct inode *nilfs_gc_iget(struct the_nilfs *nilfs, ino_t ino, __u64 cno)
 {
-	struct hlist_head *head = nilfs->ns_gc_inodes_h + ihash(ino, cno);
-	struct hlist_node *node;
+	struct hlist_bl_head *head = nilfs->ns_gc_inodes_h + ihash(ino, cno);
+	struct hlist_bl_node *node;
 	struct inode *inode;
 
-	hlist_for_each_entry(inode, node, head, i_hash) {
+	hlist_bl_for_each_entry(inode, node, head, i_hash) {
 		if (inode->i_ino == ino && NILFS_I(inode)->i_cno == cno)
 			return inode;
 	}
 
 	inode = alloc_gcinode(nilfs, ino, cno);
 	if (likely(inode)) {
-		hlist_add_head(&inode->i_hash, head);
+		hlist_bl_add_head(&inode->i_hash, head);
 		list_add(&NILFS_I(inode)->i_dirty, &nilfs->ns_gc_inodes);
 	}
 	return inode;
@@ -284,16 +284,18 @@ void nilfs_clear_gcinode(struct inode *inode)
  */
 void nilfs_remove_all_gcinode(struct the_nilfs *nilfs)
 {
-	struct hlist_head *head = nilfs->ns_gc_inodes_h;
-	struct hlist_node *node, *n;
+	struct hlist_bl_head *head = nilfs->ns_gc_inodes_h;
+	struct hlist_bl_node *node;
 	struct inode *inode;
 	int loop;
 
 	for (loop = 0; loop < NILFS_GCINODE_HASH_SIZE; loop++, head++) {
-		hlist_for_each_entry_safe(inode, node, n, head, i_hash) {
-			hlist_del_init(&inode->i_hash);
+restart:
+		hlist_bl_for_each_entry(inode, node, head, i_hash) {
+			hlist_bl_del_init(&inode->i_hash);
 			list_del_init(&NILFS_I(inode)->i_dirty);
 			nilfs_clear_gcinode(inode); /* might sleep */
+			goto restart;
 		}
 	}
 }
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 9fd051a..038251c 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -2452,7 +2452,7 @@ nilfs_remove_written_gcinodes(struct the_nilfs *nilfs, struct list_head *head)
 	list_for_each_entry_safe(ii, n, head, i_dirty) {
 		if (!test_bit(NILFS_I_UPDATED, &ii->i_state))
 			continue;
-		hlist_del_init(&ii->vfs_inode.i_hash);
+		hlist_bl_del_init(&ii->vfs_inode.i_hash);
 		list_del_init(&ii->i_dirty);
 		nilfs_clear_gcinode(&ii->vfs_inode);
 	}
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index f785a7b..1ab441a 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -167,7 +167,7 @@ struct the_nilfs {
 
 	/* GC inode list and hash table head */
 	struct list_head	ns_gc_inodes;
-	struct hlist_head      *ns_gc_inodes_h;
+	struct hlist_bl_head      *ns_gc_inodes_h;
 
 	/* Disk layout information (static) */
 	unsigned int		ns_blocksize_bits;
diff --git a/fs/reiserfs/xattr.c b/fs/reiserfs/xattr.c
index 8c4cf27..ea2f55c 100644
--- a/fs/reiserfs/xattr.c
+++ b/fs/reiserfs/xattr.c
@@ -424,7 +424,7 @@ int reiserfs_prepare_write(struct file *f, struct page *page,
 static void update_ctime(struct inode *inode)
 {
 	struct timespec now = current_fs_time(inode->i_sb);
-	if (hlist_unhashed(&inode->i_hash) || !inode->i_nlink ||
+	if (hlist_bl_unhashed(&inode->i_hash) || !inode->i_nlink ||
 	    timespec_equal(&inode->i_ctime, &now))
 		return;
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 1162c10..34f983f 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -383,6 +383,7 @@ struct inodes_stat_t {
 #include <linux/capability.h>
 #include <linux/semaphore.h>
 #include <linux/fiemap.h>
+#include <linux/rculist_bl.h>
 
 #include <asm/atomic.h>
 #include <asm/byteorder.h>
@@ -724,7 +725,7 @@ struct posix_acl;
 #define ACL_NOT_CACHED ((void *)(-1))
 
 struct inode {
-	struct hlist_node	i_hash;
+	struct hlist_bl_node	i_hash;
 	struct list_head	i_io;		/* backing dev IO list */
 	struct list_head	i_lru;		/* backing dev IO list */
 	struct list_head	i_sb_list;
diff --git a/mm/shmem.c b/mm/shmem.c
index 4daaa24..7a2a5de 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2146,7 +2146,7 @@ static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
 	if (*len < 3)
 		return 255;
 
-	if (hlist_unhashed(&inode->i_hash)) {
+	if (hlist_bl_unhashed(&inode->i_hash)) {
 		/* Unfortunately insert_inode_hash is not idempotent,
 		 * so as we hash inodes here rather than at creation
 		 * time, we need a lock to ensure we only try
@@ -2154,7 +2154,7 @@ static int shmem_encode_fh(struct dentry *dentry, __u32 *fh, int *len,
 		 */
 		static DEFINE_SPINLOCK(lock);
 		spin_lock(&lock);
-		if (hlist_unhashed(&inode->i_hash))
+		if (hlist_bl_unhashed(&inode->i_hash))
 			__insert_inode_hash(inode,
 					    inode->i_ino + inode->i_generation);
 		spin_unlock(&lock);
-- 
1.7.1


^ permalink raw reply related

* [PATCH 18/18] fs: Reduce inode I_FREEING and factor inode disposal
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Dave Chinner <dchinner@redhat.com>

Inode reclaim can push many inodes into the I_FREEING state before
it actually frees them. During the time it gathers these inodes, it
can call iput(), invalidate_mapping_pages, be preempted, etc. As a
result, holding inodes in I_FREEING can cause pauses.

After the inode scalability work, there is not a big reason to batch
up inodes to reclaim them, so we can dispose them as they are found
from the LRU. With similar reasoning, we can do the same during
unmount, completely removing the need for the dispose_list()
function.

Further, iput_final() does the same inode cleanup as reclaim and
unmount, so convert them all to use a single function for destroying
inodes. This is written such that the callers can optimise list
removals to avoid unneccessary lock round trips when removing inodes
from lists.

Based on a patch originally from Nick Piggin.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/inode.c |  150 +++++++++++++++++++++++++-----------------------------------
 1 files changed, 63 insertions(+), 87 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index c778ec4..03ddd19 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -29,6 +29,8 @@
 /*
  * Locking rules.
  *
+ * inode->i_lock is *always* the innermost lock.
+ *
  * inode->i_lock protects:
  *   i_ref i_state
  * inode_hash_bucket lock protects:
@@ -46,8 +48,15 @@
  *
  *   sb inode lock
  *     inode_lru_lock
- *       wb->b_lock
- *         inode->i_lock
+ *     wb->b_lock
+ *     inode->i_lock
+ *
+ *   wb->b_lock
+ *     sb_lock (pin sb for writeback)
+ *     inode->i_lock
+ *
+ *   inode_lru
+ *     inode->i_lock
  */
 /*
  * This is needed for the following functions:
@@ -434,13 +443,12 @@ void __insert_inode_hash(struct inode *inode, unsigned long hashval)
 EXPORT_SYMBOL(__insert_inode_hash);
 
 /**
- *	__remove_inode_hash - remove an inode from the hash
+ *	remove_inode_hash - remove an inode from the hash
  *	@inode: inode to unhash
  *
- *	Remove an inode from the superblock. inode->i_lock must be
- *	held.
+ *	Remove an inode from the superblock.
  */
-static void __remove_inode_hash(struct inode *inode)
+void remove_inode_hash(struct inode *inode)
 {
 	struct inode_hash_bucket *b;
 
@@ -449,17 +457,6 @@ static void __remove_inode_hash(struct inode *inode)
 	hlist_bl_del_init(&inode->i_hash);
 	spin_unlock_bucket(b);
 }
-
-/**
- *	remove_inode_hash - remove an inode from the hash
- *	@inode: inode to unhash
- *
- *	Remove an inode from the superblock.
- */
-void remove_inode_hash(struct inode *inode)
-{
-	__remove_inode_hash(inode);
-}
 EXPORT_SYMBOL(remove_inode_hash);
 
 void end_writeback(struct inode *inode)
@@ -494,37 +491,53 @@ static void evict(struct inode *inode)
 }
 
 /*
- * dispose_list - dispose of the contents of a local list
- * @head: the head of the list to free
+ * Free the inode passed in, removing it from the lists it is still connected
+ * to but avoiding unnecessary lock round-trips for the lists it is no longer
+ * on.
  *
- * Dispose-list gets a local list with local inodes in it, so it doesn't
- * need to worry about list corruption and SMP locks.
+ * An inode must already be marked I_FREEING so that we avoid the inode being
+ * moved back onto lists if we race with other code that manipulates the lists
+ * (e.g. writeback_single_inode).
  */
-static void dispose_list(struct list_head *head)
+static void dispose_one_inode(struct inode *inode)
 {
-	while (!list_empty(head)) {
-		struct inode *inode;
+	BUG_ON(!(inode->i_state & I_FREEING));
 
-		inode = list_first_entry(head, struct inode, i_lru);
-		list_del_init(&inode->i_lru);
+	/*
+	 * move the inode off the IO lists and LRU once
+	 * I_FREEING is set so that it won't get moved back on
+	 * there if it is dirty.
+	 */
+	if (!list_empty(&inode->i_io)) {
+		struct backing_dev_info *bdi = inode_to_bdi(inode);
 
-		evict(inode);
+		spin_lock(&bdi->wb.b_lock);
+		list_del_init(&inode->i_io);
+		spin_unlock(&bdi->wb.b_lock);
+	}
+
+	if (!list_empty(&inode->i_lru))
+		inode_lru_list_del(inode);
 
-		__remove_inode_hash(inode);
+	if (!list_empty(&inode->i_sb_list)) {
 		spin_lock(&inode->i_sb->s_inodes_lock);
 		list_del_init(&inode->i_sb_list);
 		spin_unlock(&inode->i_sb->s_inodes_lock);
-
-		wake_up_inode(inode);
-		destroy_inode(inode);
 	}
+
+	evict(inode);
+
+	remove_inode_hash(inode);
+	wake_up_inode(inode);
+	BUG_ON(inode->i_state != (I_FREEING | I_CLEAR));
+	destroy_inode(inode);
 }
 
+
 /*
  * Invalidate all inodes for a device.
  */
-static int invalidate_list(struct super_block *sb, struct list_head *head,
-			struct list_head *dispose)
+static int invalidate_list(struct super_block *sb, struct list_head *head)
 {
 	struct list_head *next;
 	int busy = 0;
@@ -553,30 +566,22 @@ static int invalidate_list(struct super_block *sb, struct list_head *head,
 		}
 		invalidate_inode_buffers(inode);
 		if (!inode->i_ref) {
-			struct backing_dev_info *bdi = inode_to_bdi(inode);
-
 			WARN_ON(inode->i_state & I_NEW);
 			inode->i_state |= I_FREEING;
 			spin_unlock(&inode->i_lock);
 
-			/*
-			 * move the inode off the IO lists and LRU once
-			 * I_FREEING is set so that it won't get moved back on
-			 * there if it is dirty.
-			 */
-			spin_lock(&bdi->wb.b_lock);
-			list_del_init(&inode->i_io);
-			spin_unlock(&bdi->wb.b_lock);
+			/* save a lock round trip by removing the inode here. */
+			list_del_init(&inode->i_sb_list);
+			spin_unlock(&sb->s_inodes_lock);
 
-			spin_lock(&inode_lru_lock);
-			list_move(&inode->i_lru, dispose);
-			spin_unlock(&inode_lru_lock);
+			dispose_one_inode(inode);
 
-			percpu_counter_dec(&nr_inodes_unused);
+			spin_lock(&sb->s_inodes_lock);
 			continue;
 		}
 		spin_unlock(&inode->i_lock);
 		busy = 1;
+
 	}
 	return busy;
 }
@@ -592,15 +597,12 @@ static int invalidate_list(struct super_block *sb, struct list_head *head,
 int invalidate_inodes(struct super_block *sb)
 {
 	int busy;
-	LIST_HEAD(throw_away);
 
 	down_write(&iprune_sem);
 	spin_lock(&sb->s_inodes_lock);
 	fsnotify_unmount_inodes(&sb->s_inodes);
-	busy = invalidate_list(sb, &sb->s_inodes, &throw_away);
+	busy = invalidate_list(sb, &sb->s_inodes);
 	spin_unlock(&sb->s_inodes_lock);
-
-	dispose_list(&throw_away);
 	up_write(&iprune_sem);
 
 	return busy;
@@ -636,7 +638,6 @@ static int can_unuse(struct inode *inode)
  */
 static void prune_icache(int nr_to_scan)
 {
-	LIST_HEAD(freeable);
 	int nr_scanned;
 	unsigned long reap = 0;
 
@@ -644,7 +645,6 @@ static void prune_icache(int nr_to_scan)
 	spin_lock(&inode_lru_lock);
 	for (nr_scanned = 0; nr_scanned < nr_to_scan; nr_scanned++) {
 		struct inode *inode;
-		struct backing_dev_info *bdi;
 
 		if (list_empty(&inode_lru))
 			break;
@@ -691,18 +691,15 @@ static void prune_icache(int nr_to_scan)
 		inode->i_state |= I_FREEING;
 		spin_unlock(&inode->i_lock);
 
-		/*
-		 * move the inode off the IO lists and LRU once
-		 * I_FREEING is set so that it won't get moved back on
-		 * there if it is dirty.
-		 */
-		bdi = inode_to_bdi(inode);
-		spin_lock(&bdi->wb.b_lock);
-		list_del_init(&inode->i_io);
-		spin_unlock(&bdi->wb.b_lock);
-
-		list_move(&inode->i_lru, &freeable);
+		/* save a lock round trip by removing the inode here. */
+		list_del_init(&inode->i_lru);
 		percpu_counter_dec(&nr_inodes_unused);
+		spin_unlock(&inode_lru_lock);
+
+		dispose_one_inode(inode);
+		cond_resched();
+
+		spin_lock(&inode_lru_lock);
 	}
 	if (current_is_kswapd())
 		__count_vm_events(KSWAPD_INODESTEAL, reap);
@@ -710,7 +707,6 @@ static void prune_icache(int nr_to_scan)
 		__count_vm_events(PGINODESTEAL, reap);
 	spin_unlock(&inode_lru_lock);
 
-	dispose_list(&freeable);
 	up_read(&iprune_sem);
 }
 
@@ -1449,7 +1445,6 @@ static void iput_final(struct inode *inode)
 {
 	struct super_block *sb = inode->i_sb;
 	const struct super_operations *op = inode->i_sb->s_op;
-	struct backing_dev_info *bdi = inode_to_bdi(inode);
 	int drop;
 
 	assert_spin_locked(&inode->i_lock);
@@ -1475,35 +1470,16 @@ static void iput_final(struct inode *inode)
 		inode->i_state |= I_WILL_FREE;
 		spin_unlock(&inode->i_lock);
 		write_inode_now(inode, 1);
+		remove_inode_hash(inode);
 		spin_lock(&inode->i_lock);
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state &= ~I_WILL_FREE;
-		__remove_inode_hash(inode);
 	}
 	WARN_ON(inode->i_state & I_NEW);
 	inode->i_state |= I_FREEING;
 	spin_unlock(&inode->i_lock);
 
-	/*
-	 * move the inode off the IO lists and LRU once I_FREEING is set so
-	 * that it won't get moved back on there if it is dirty.
-	 * around.
-	 */
-	spin_lock(&bdi->wb.b_lock);
-	list_del_init(&inode->i_io);
-	spin_unlock(&bdi->wb.b_lock);
-
-	inode_lru_list_del(inode);
-
-	spin_lock(&sb->s_inodes_lock);
-	list_del_init(&inode->i_sb_list);
-	spin_unlock(&sb->s_inodes_lock);
-
-	evict(inode);
-	remove_inode_hash(inode);
-	wake_up_inode(inode);
-	BUG_ON(inode->i_state != (I_FREEING | I_CLEAR));
-	destroy_inode(inode);
+	dispose_one_inode(inode);
 }
 
 /**
-- 
1.7.1


^ permalink raw reply related

* [PATCH 15/18] fs: introduce a per-cpu last_ino allocator
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Eric Dumazet <eric.dumazet@gmail.com>

new_inode() dirties a contended cache line to get increasing
inode numbers. This limits performance on workloads that cause
significant parallel inode allocation.

Solve this problem by using a per_cpu variable fed by the shared
last_ino in batches of 1024 allocations.  This reduces contention on
the shared last_ino, and give same spreading ino numbers than before
(i.e. same wraparound after 2^32 allocations).

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/inode.c |   45 ++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index d3bd08a..13e1325 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -870,6 +870,43 @@ void inode_add_to_lists(struct super_block *sb, struct inode *inode)
 }
 EXPORT_SYMBOL_GPL(inode_add_to_lists);
 
+/*
+ * Each cpu owns a range of LAST_INO_BATCH numbers.
+ * 'shared_last_ino' is dirtied only once out of LAST_INO_BATCH allocations,
+ * to renew the exhausted range.
+ *
+ * This does not significantly increase overflow rate because every CPU can
+ * consume at most LAST_INO_BATCH-1 unused inode numbers. So there is
+ * NR_CPUS*(LAST_INO_BATCH-1) wastage. At 4096 and 1024, this is ~0.1% of the
+ * 2^32 range, and is a worst-case. Even a 50% wastage would only increase
+ * overflow rate by 2x, which does not seem too significant.
+ *
+ * On a 32bit, non LFS stat() call, glibc will generate an EOVERFLOW
+ * error if st_ino won't fit in target struct field. Use 32bit counter
+ * here to attempt to avoid that.
+ */
+#define LAST_INO_BATCH 1024
+static DEFINE_PER_CPU(unsigned int, last_ino);
+
+static unsigned int last_ino_get(void)
+{
+	unsigned int *p = &get_cpu_var(last_ino);
+	unsigned int res = *p;
+
+#ifdef CONFIG_SMP
+	if (unlikely((res & (LAST_INO_BATCH-1)) == 0)) {
+		static atomic_t shared_last_ino;
+		int next = atomic_add_return(LAST_INO_BATCH, &shared_last_ino);
+
+		res = next - LAST_INO_BATCH;
+	}
+#endif
+
+	*p = ++res;
+	put_cpu_var(last_ino);
+	return res;
+}
+
 /**
  *	new_inode 	- obtain an inode
  *	@sb: superblock
@@ -884,12 +921,6 @@ EXPORT_SYMBOL_GPL(inode_add_to_lists);
  */
 struct inode *new_inode(struct super_block *sb)
 {
-	/*
-	 * On a 32bit, non LFS stat() call, glibc will generate an EOVERFLOW
-	 * error if st_ino won't fit in target struct field. Use 32bit counter
-	 * here to attempt to avoid that.
-	 */
-	static unsigned int last_ino;
 	struct inode *inode;
 
 	spin_lock_prefetch(&inode_lock);
@@ -897,7 +928,7 @@ struct inode *new_inode(struct super_block *sb)
 	inode = alloc_inode(sb);
 	if (inode) {
 		spin_lock(&inode_lock);
-		inode->i_ino = ++last_ino;
+		inode->i_ino = last_ino_get();
 		inode->i_state = 0;
 		__inode_add_to_lists(sb, NULL, inode);
 		spin_unlock(&inode_lock);
-- 
1.7.1


^ permalink raw reply related

* [PATCH 16/18] fs: Make iunique independent of inode_lock
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Nick Piggin <npiggin@suse.de>

Before removing the inode_lock, the iunique counter needs to be made
independent of the inode_lock. Add a new lock to protect the iunique
counter and nest it inside the inode_lock to provide the same
protection that the inode_lock currently provides.

Signed-off-by: Nick Piggin <npiggin@suse.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/inode.c |   33 ++++++++++++++++++++++++++++-----
 1 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/fs/inode.c b/fs/inode.c
index 13e1325..4ec360e 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1070,6 +1070,30 @@ static struct inode *get_new_inode_fast(struct super_block *sb,
 	return inode;
 }
 
+/*
+ * search the inode cache for a matching inode number.
+ * If we find one, then the inode number we are trying to
+ * allocate is not unique and so we should not use it.
+ *
+ * Returns 1 if the inode number is unique, 0 if it is not.
+ */
+static int test_inode_iunique(struct super_block * sb, unsigned long ino)
+{
+	struct inode_hash_bucket *b = inode_hashtable + hash(sb, ino);
+	struct hlist_bl_node *node;
+	struct inode *inode;
+
+	spin_lock_bucket(b);
+	hlist_bl_for_each_entry(inode, node, &b->head, i_hash) {
+		if (inode->i_ino == ino && inode->i_sb == sb) {
+			spin_unlock_bucket(b);
+			return 0;
+		}
+	}
+	spin_unlock_bucket(b);
+	return 1;
+}
+
 /**
  *	iunique - get a unique inode number
  *	@sb: superblock
@@ -1091,19 +1115,18 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved)
 	 * error if st_ino won't fit in target struct field. Use 32bit counter
 	 * here to attempt to avoid that.
 	 */
+	static DEFINE_SPINLOCK(unique_lock);
 	static unsigned int counter;
-	struct inode *inode;
-	struct inode_hash_bucket *b;
 	ino_t res;
 
 	spin_lock(&inode_lock);
+	spin_lock(&unique_lock);
 	do {
 		if (counter <= max_reserved)
 			counter = max_reserved + 1;
 		res = counter++;
-		b = inode_hashtable + hash(sb, res);
-		inode = find_inode_fast(sb, b, res);
-	} while (inode != NULL);
+	} while (!test_inode_iunique(sb, res));
+	spin_unlock(&unique_lock);
 	spin_unlock(&inode_lock);
 
 	return res;
-- 
1.7.1


^ permalink raw reply related

* [PATCH 14/18] fs: Protect inode->i_state with th einode->i_lock
From: Dave Chinner @ 2010-10-08  5:21 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-kernel
In-Reply-To: <1286515292-15882-1-git-send-email-david@fromorbit.com>

From: Dave Chinner <dchinner@redhat.com>

We currently protect the per-inode state flags with the inode_lock.
Using a global lock to protect per-object state is overkill when we
coul duse a per-inode lock to protect the state.  Use the
inode->i_lock for this, and wrap all the state changes and checks
with the inode->i_lock.

Based on work originally written by Nick Piggin.

Signed-off-by: Dave Chinner <dchinner@redhat.com>
---
 fs/drop_caches.c       |    9 +++--
 fs/fs-writeback.c      |   49 ++++++++++++++++++++++------
 fs/inode.c             |   83 ++++++++++++++++++++++++++++++++---------------
 fs/nilfs2/gcdat.c      |    1 +
 fs/notify/inode_mark.c |   10 ++++--
 fs/quota/dquot.c       |   12 ++++---
 6 files changed, 115 insertions(+), 49 deletions(-)

diff --git a/fs/drop_caches.c b/fs/drop_caches.c
index c808ca8..00180dc 100644
--- a/fs/drop_caches.c
+++ b/fs/drop_caches.c
@@ -19,11 +19,14 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)
 	spin_lock(&inode_lock);
 	spin_lock(&sb->s_inodes_lock);
 	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
-		if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW))
-			continue;
-		if (inode->i_mapping->nrpages == 0)
+		spin_lock(&inode->i_lock);
+		if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
+		    (inode->i_mapping->nrpages == 0)) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 		iref_locked(inode);
+		spin_unlock(&inode->i_lock);
 		spin_unlock(&sb->s_inodes_lock);
 		spin_unlock(&inode_lock);
 		invalidate_mapping_pages(inode->i_mapping, 0, -1);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 49d44cc..404d449 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -281,10 +281,12 @@ static void inode_wait_for_writeback(struct inode *inode)
 	wait_queue_head_t *wqh;
 
 	wqh = bit_waitqueue(&inode->i_state, __I_SYNC);
-	 while (inode->i_state & I_SYNC) {
+	while (inode->i_state & I_SYNC) {
+		spin_unlock(&inode->i_lock);
 		spin_unlock(&inode_lock);
 		__wait_on_bit(wqh, &wq, inode_wait, TASK_UNINTERRUPTIBLE);
 		spin_lock(&inode_lock);
+		spin_lock(&inode->i_lock);
 	}
 }
 
@@ -309,7 +311,8 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 	unsigned dirty;
 	int ret;
 
-	if (!iref_read(inode))
+	spin_lock(&inode->i_lock);
+	if (!inode->i_ref)
 		WARN_ON(!(inode->i_state & (I_WILL_FREE|I_FREEING)));
 	else
 		WARN_ON(inode->i_state & I_WILL_FREE);
@@ -324,6 +327,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 		 * completed a full scan of b_io.
 		 */
 		if (wbc->sync_mode != WB_SYNC_ALL) {
+			spin_unlock(&inode->i_lock);
 			spin_lock(&bdi->wb.b_lock);
 			requeue_io(inode);
 			spin_unlock(&bdi->wb.b_lock);
@@ -341,6 +345,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 	/* Set I_SYNC, reset I_DIRTY_PAGES */
 	inode->i_state |= I_SYNC;
 	inode->i_state &= ~I_DIRTY_PAGES;
+	spin_unlock(&inode->i_lock);
 	spin_unlock(&inode_lock);
 
 	ret = do_writepages(mapping, wbc);
@@ -362,8 +367,10 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 	 * write_inode()
 	 */
 	spin_lock(&inode_lock);
+	spin_lock(&inode->i_lock);
 	dirty = inode->i_state & I_DIRTY;
 	inode->i_state &= ~(I_DIRTY_SYNC | I_DIRTY_DATASYNC);
+	spin_unlock(&inode->i_lock);
 	spin_unlock(&inode_lock);
 	/* Don't write the inode if only I_DIRTY_PAGES was set */
 	if (dirty & (I_DIRTY_SYNC | I_DIRTY_DATASYNC)) {
@@ -373,6 +380,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 	}
 
 	spin_lock(&inode_lock);
+	spin_lock(&inode->i_lock);
 	inode->i_state &= ~I_SYNC;
 	if (!(inode->i_state & I_FREEING)) {
 		if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) {
@@ -381,6 +389,7 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 			 * sometimes bales out without doing anything.
 			 */
 			inode->i_state |= I_DIRTY_PAGES;
+			spin_unlock(&inode->i_lock);
 			spin_lock(&bdi->wb.b_lock);
 			if (wbc->nr_to_write <= 0) {
 				/*
@@ -405,16 +414,21 @@ writeback_single_inode(struct inode *inode, struct writeback_control *wbc)
 			 * submission or metadata updates after data IO
 			 * completion.
 			 */
+			spin_unlock(&inode->i_lock);
 			spin_lock(&bdi->wb.b_lock);
 			redirty_tail(inode);
 			spin_unlock(&bdi->wb.b_lock);
 		} else {
 			/* The inode is clean */
+			spin_unlock(&inode->i_lock);
 			spin_lock(&bdi->wb.b_lock);
 			list_del_init(&inode->i_io);
 			spin_unlock(&bdi->wb.b_lock);
 			inode_lru_list_add(inode);
 		}
+	} else {
+		/* freer will clean up */
+		spin_unlock(&inode->i_lock);
 	}
 	inode_sync_complete(inode);
 	return ret;
@@ -483,7 +497,9 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb,
 			return 0;
 		}
 
+		spin_lock(&inode->i_lock);
 		if (inode->i_state & (I_NEW | I_WILL_FREE | I_FREEING)) {
+			spin_unlock(&inode->i_lock);
 			requeue_io(inode);
 			continue;
 		}
@@ -491,10 +507,11 @@ static int writeback_sb_inodes(struct super_block *sb, struct bdi_writeback *wb,
 		 * Was this inode dirtied after sync_sb_inodes was called?
 		 * This keeps sync from extra jobs and livelock.
 		 */
-		if (inode_dirtied_after(inode, wbc->wb_start))
+		if (inode_dirtied_after(inode, wbc->wb_start)) {
+			spin_unlock(&inode->i_lock);
 			return 1;
+		}
 
-		spin_lock(&inode->i_lock);
 		iref_locked(inode);
 		spin_unlock(&inode->i_lock);
 		spin_unlock(&wb->b_lock);
@@ -687,7 +704,9 @@ static long wb_writeback(struct bdi_writeback *wb,
 						struct inode, i_io);
 			spin_unlock(&wb->b_lock);
 			trace_wbc_writeback_wait(&wbc, wb->bdi);
+			spin_lock(&inode->i_lock);
 			inode_wait_for_writeback(inode);
+			spin_unlock(&inode->i_lock);
 		}
 		spin_unlock(&inode_lock);
 	}
@@ -953,6 +972,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 		block_dump___mark_inode_dirty(inode);
 
 	spin_lock(&inode_lock);
+	spin_lock(&inode->i_lock);
 	if ((inode->i_state & flags) != flags) {
 		const int was_dirty = inode->i_state & I_DIRTY;
 
@@ -964,7 +984,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 		 * superblock list, based upon its state.
 		 */
 		if (inode->i_state & I_SYNC)
-			goto out;
+			goto out_unlock;
 
 		/*
 		 * Only add valid (hashed) inodes to the superblock's
@@ -972,10 +992,10 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 		 */
 		if (!S_ISBLK(inode->i_mode)) {
 			if (hlist_bl_unhashed(&inode->i_hash))
-				goto out;
+				goto out_unlock;
 		}
 		if (inode->i_state & I_FREEING)
-			goto out;
+			goto out_unlock;
 
 		/*
 		 * If the inode was already on b_dirty/b_io/b_more_io, don't
@@ -998,12 +1018,16 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 					wakeup_bdi = true;
 			}
 
-			spin_lock(&bdi->wb.b_lock);
 			inode->dirtied_when = jiffies;
+			spin_unlock(&inode->i_lock);
+			spin_lock(&bdi->wb.b_lock);
 			list_move(&inode->i_io, &bdi->wb.b_dirty);
 			spin_unlock(&bdi->wb.b_lock);
+			goto out;
 		}
 	}
+out_unlock:
+	spin_unlock(&inode->i_lock);
 out:
 	spin_unlock(&inode_lock);
 
@@ -1052,12 +1076,15 @@ static void wait_sb_inodes(struct super_block *sb)
 	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
 		struct address_space *mapping;
 
-		if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW))
-			continue;
+		spin_lock(&inode->i_lock);
 		mapping = inode->i_mapping;
-		if (mapping->nrpages == 0)
+		if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
+		    (mapping->nrpages == 0)) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 		iref_locked(inode);
+		spin_unlock(&inode->i_lock);
 		spin_unlock(&sb->s_inodes_lock);
 		spin_unlock(&inode_lock);
 		/*
diff --git a/fs/inode.c b/fs/inode.c
index 4ad7900..d3bd08a 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -30,7 +30,7 @@
  * Locking rules.
  *
  * inode->i_lock protects:
- *   i_ref
+ *   i_ref i_state
  * inode_hash_bucket lock protects:
  *   inode hash table, i_hash
  * sb inode lock protects:
@@ -182,7 +182,7 @@ int proc_nr_inodes(ctl_table *table, int write,
 static void wake_up_inode(struct inode *inode)
 {
 	/*
-	 * Prevent speculative execution through spin_unlock(&inode_lock);
+	 * Prevent speculative execution through spin_unlock(&inode->i_lock);
 	 */
 	smp_mb();
 	wake_up_bit(&inode->i_state, __I_NEW);
@@ -361,6 +361,8 @@ static void init_once(void *foo)
  */
 void iref_locked(struct inode *inode)
 {
+	assert_spin_locked(&inode->i_lock);
+
 	inode->i_ref++;
 }
 EXPORT_SYMBOL_GPL(iref_locked);
@@ -484,7 +486,9 @@ void end_writeback(struct inode *inode)
 	BUG_ON(!(inode->i_state & I_FREEING));
 	BUG_ON(inode->i_state & I_CLEAR);
 	inode_sync_wait(inode);
+	spin_lock(&inode->i_lock);
 	inode->i_state = I_FREEING | I_CLEAR;
+	spin_unlock(&inode->i_lock);
 }
 EXPORT_SYMBOL(end_writeback);
 
@@ -561,17 +565,18 @@ static int invalidate_list(struct super_block *sb, struct list_head *head,
 		if (tmp == head)
 			break;
 		inode = list_entry(tmp, struct inode, i_sb_list);
-		if (inode->i_state & I_NEW)
+		spin_lock(&inode->i_lock);
+		if (inode->i_state & I_NEW) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 		invalidate_inode_buffers(inode);
-		spin_lock(&inode->i_lock);
 		if (!inode->i_ref) {
 			struct backing_dev_info *bdi = inode_to_bdi(inode);
 
-			spin_unlock(&inode->i_lock);
 			WARN_ON(inode->i_state & I_NEW);
 			inode->i_state |= I_FREEING;
-
+			spin_unlock(&inode->i_lock);
 
 			/*
 			 * move the inode off the IO lists and LRU once
@@ -625,11 +630,12 @@ EXPORT_SYMBOL(invalidate_inodes);
 
 static int can_unuse(struct inode *inode)
 {
+	assert_spin_locked(&inode->i_lock);
 	if (inode->i_state)
 		return 0;
 	if (inode_has_buffers(inode))
 		return 0;
-	if (iref_read(inode))
+	if (inode->i_ref)
 		return 0;
 	if (inode->i_data.nrpages)
 		return 0;
@@ -675,9 +681,9 @@ static void prune_icache(int nr_to_scan)
 			continue;
 		}
 		if (inode->i_state & I_REFERENCED) {
+			inode->i_state &= ~I_REFERENCED;
 			spin_unlock(&inode->i_lock);
 			list_move(&inode->i_lru, &inode_lru);
-			inode->i_state &= ~I_REFERENCED;
 			continue;
 		}
 		if (inode_has_buffers(inode) || inode->i_data.nrpages) {
@@ -691,6 +697,7 @@ static void prune_icache(int nr_to_scan)
 			iput(inode);
 			spin_lock(&inode_lock);
 			spin_lock(&inode_lru_lock);
+			spin_lock(&inode->i_lock);
 
 			/*
 			 * if we can't reclaim this inod immediately, give it
@@ -699,12 +706,14 @@ static void prune_icache(int nr_to_scan)
 			 */
 			if (!can_unuse(inode)) {
 				list_move(&inode->i_lru, &inode_lru);
+				spin_unlock(&inode->i_lock);
 				continue;
 			}
-		} else
-			spin_unlock(&inode->i_lock);
+		}
+
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state |= I_FREEING;
+		spin_unlock(&inode->i_lock);
 
 		/*
 		 * move the inode off the IO lists and LRU once
@@ -761,7 +770,7 @@ static struct shrinker icache_shrinker = {
 
 static void __wait_on_freeing_inode(struct inode *inode);
 /*
- * Called with the inode lock held.
+ * Returns with inode->i_lock held.
  * NOTE: we are not increasing the inode-refcount, you must call iref_locked()
  * by hand after calling find_inode now! This simplifies iunique and won't
  * add any additional branch in the common code.
@@ -779,8 +788,11 @@ repeat:
 	hlist_bl_for_each_entry(inode, node, &b->head, i_hash) {
 		if (inode->i_sb != sb)
 			continue;
-		if (!test(inode, data))
+		spin_lock(&inode->i_lock);
+		if (!test(inode, data)) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 		if (inode->i_state & (I_FREEING|I_WILL_FREE)) {
 			spin_unlock_bucket(b);
 			__wait_on_freeing_inode(inode);
@@ -810,6 +822,7 @@ repeat:
 			continue;
 		if (inode->i_sb != sb)
 			continue;
+		spin_lock(&inode->i_lock);
 		if (inode->i_state & (I_FREEING|I_WILL_FREE)) {
 			spin_unlock_bucket(b);
 			__wait_on_freeing_inode(inode);
@@ -884,9 +897,9 @@ struct inode *new_inode(struct super_block *sb)
 	inode = alloc_inode(sb);
 	if (inode) {
 		spin_lock(&inode_lock);
-		__inode_add_to_lists(sb, NULL, inode);
 		inode->i_ino = ++last_ino;
 		inode->i_state = 0;
+		__inode_add_to_lists(sb, NULL, inode);
 		spin_unlock(&inode_lock);
 	}
 	return inode;
@@ -953,8 +966,8 @@ static struct inode *get_new_inode(struct super_block *sb,
 			if (set(inode, data))
 				goto set_failed;
 
-			__inode_add_to_lists(sb, b, inode);
 			inode->i_state = I_NEW;
+			__inode_add_to_lists(sb, b, inode);
 			spin_unlock(&inode_lock);
 
 			/* Return the locked inode with I_NEW set, the
@@ -968,7 +981,6 @@ static struct inode *get_new_inode(struct super_block *sb,
 		 * us. Use the old inode instead of the one we just
 		 * allocated.
 		 */
-		spin_lock(&old->i_lock);
 		iref_locked(old);
 		spin_unlock(&old->i_lock);
 		spin_unlock(&inode_lock);
@@ -1017,7 +1029,6 @@ static struct inode *get_new_inode_fast(struct super_block *sb,
 		 * us. Use the old inode instead of the one we just
 		 * allocated.
 		 */
-		spin_lock(&old->i_lock);
 		iref_locked(old);
 		spin_unlock(&old->i_lock);
 		spin_unlock(&inode_lock);
@@ -1071,17 +1082,19 @@ EXPORT_SYMBOL(iunique);
 struct inode *igrab(struct inode *inode)
 {
 	spin_lock(&inode_lock);
+	spin_lock(&inode->i_lock);
 	if (!(inode->i_state & (I_FREEING|I_WILL_FREE))) {
-		spin_lock(&inode->i_lock);
 		iref_locked(inode);
 		spin_unlock(&inode->i_lock);
-	} else
+	} else {
+		spin_unlock(&inode->i_lock);
 		/*
 		 * Handle the case where s_op->clear_inode is not been
 		 * called yet, and somebody is calling igrab
 		 * while the inode is getting freed.
 		 */
 		inode = NULL;
+	}
 	spin_unlock(&inode_lock);
 	return inode;
 }
@@ -1116,7 +1129,6 @@ static struct inode *ifind(struct super_block *sb,
 	spin_lock(&inode_lock);
 	inode = find_inode(sb, b, test, data);
 	if (inode) {
-		spin_lock(&inode->i_lock);
 		iref_locked(inode);
 		spin_unlock(&inode->i_lock);
 		spin_unlock(&inode_lock);
@@ -1152,7 +1164,6 @@ static struct inode *ifind_fast(struct super_block *sb,
 	spin_lock(&inode_lock);
 	inode = find_inode_fast(sb, b, ino);
 	if (inode) {
-		spin_lock(&inode->i_lock);
 		iref_locked(inode);
 		spin_unlock(&inode->i_lock);
 		spin_unlock(&inode_lock);
@@ -1318,6 +1329,10 @@ int insert_inode_locked(struct inode *inode)
 	ino_t ino = inode->i_ino;
 	struct inode_hash_bucket *b = inode_hashtable + hash(sb, ino);
 
+	/*
+	 * Nobody else can see the new inode yet, so it is safe to set flags
+	 * without locking here.
+	 */
 	inode->i_state |= I_NEW;
 	while (1) {
 		struct hlist_bl_node *node;
@@ -1329,8 +1344,11 @@ int insert_inode_locked(struct inode *inode)
 				continue;
 			if (old->i_sb != sb)
 				continue;
-			if (old->i_state & (I_FREEING|I_WILL_FREE))
+			spin_lock(&old->i_lock);
+			if (old->i_state & (I_FREEING|I_WILL_FREE)) {
+				spin_unlock(&old->i_lock);
 				continue;
+			}
 			break;
 		}
 		if (likely(!node)) {
@@ -1339,7 +1357,6 @@ int insert_inode_locked(struct inode *inode)
 			spin_unlock(&inode_lock);
 			return 0;
 		}
-		spin_lock(&old->i_lock);
 		iref_locked(old);
 		spin_unlock(&old->i_lock);
 		spin_unlock_bucket(b);
@@ -1373,8 +1390,11 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
 				continue;
 			if (!test(old, data))
 				continue;
-			if (old->i_state & (I_FREEING|I_WILL_FREE))
+			spin_lock(&old->i_lock);
+			if (old->i_state & (I_FREEING|I_WILL_FREE)) {
+				spin_unlock(&old->i_lock);
 				continue;
+			}
 			break;
 		}
 		if (likely(!node)) {
@@ -1383,7 +1403,6 @@ int insert_inode_locked4(struct inode *inode, unsigned long hashval,
 			spin_unlock(&inode_lock);
 			return 0;
 		}
-		spin_lock(&old->i_lock);
 		iref_locked(old);
 		spin_unlock(&old->i_lock);
 		spin_unlock_bucket(b);
@@ -1433,6 +1452,8 @@ static void iput_final(struct inode *inode)
 	struct backing_dev_info *bdi = inode_to_bdi(inode);
 	int drop;
 
+	assert_spin_locked(&inode->i_lock);
+
 	if (op && op->drop_inode)
 		drop = op->drop_inode(inode);
 	else
@@ -1443,22 +1464,28 @@ static void iput_final(struct inode *inode)
 			inode->i_state |= I_REFERENCED;
 			if (!(inode->i_state & (I_DIRTY|I_SYNC)) &&
 			    list_empty(&inode->i_lru)) {
+				spin_unlock(&inode->i_lock);
 				inode_lru_list_add(inode);
+				return;
 			}
+			spin_unlock(&inode->i_lock);
 			spin_unlock(&inode_lock);
 			return;
 		}
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state |= I_WILL_FREE;
+		spin_unlock(&inode->i_lock);
 		spin_unlock(&inode_lock);
 		write_inode_now(inode, 1);
 		spin_lock(&inode_lock);
+		spin_lock(&inode->i_lock);
 		WARN_ON(inode->i_state & I_NEW);
 		inode->i_state &= ~I_WILL_FREE;
 		__remove_inode_hash(inode);
 	}
 	WARN_ON(inode->i_state & I_NEW);
 	inode->i_state |= I_FREEING;
+	spin_unlock(&inode->i_lock);
 
 	/*
 	 * move the inode off the IO lists and LRU once I_FREEING is set so
@@ -1495,13 +1522,12 @@ static void iput_final(struct inode *inode)
 void iput(struct inode *inode)
 {
 	if (inode) {
-		BUG_ON(inode->i_state & I_CLEAR);
-
 		spin_lock(&inode_lock);
 		spin_lock(&inode->i_lock);
+		BUG_ON(inode->i_state & I_CLEAR);
+
 		inode->i_ref--;
 		if (inode->i_ref == 0) {
-			spin_unlock(&inode->i_lock);
 			iput_final(inode);
 			return;
 		}
@@ -1687,6 +1713,8 @@ EXPORT_SYMBOL(inode_wait);
  * wake_up_inode() after removing from the hash list will DTRT.
  *
  * This is called with inode_lock held.
+ *
+ * Called with i_lock held and returns with it dropped.
  */
 static void __wait_on_freeing_inode(struct inode *inode)
 {
@@ -1694,6 +1722,7 @@ static void __wait_on_freeing_inode(struct inode *inode)
 	DEFINE_WAIT_BIT(wait, &inode->i_state, __I_NEW);
 	wq = bit_waitqueue(&inode->i_state, __I_NEW);
 	prepare_to_wait(wq, &wait.wait, TASK_UNINTERRUPTIBLE);
+	spin_unlock(&inode->i_lock);
 	spin_unlock(&inode_lock);
 	schedule();
 	finish_wait(wq, &wait.wait);
diff --git a/fs/nilfs2/gcdat.c b/fs/nilfs2/gcdat.c
index 84a45d1..c51f0e8 100644
--- a/fs/nilfs2/gcdat.c
+++ b/fs/nilfs2/gcdat.c
@@ -27,6 +27,7 @@
 #include "page.h"
 #include "mdt.h"
 
+/* XXX: what protects i_state? */
 int nilfs_init_gcdat_inode(struct the_nilfs *nilfs)
 {
 	struct inode *dat = nilfs->ns_dat, *gcdat = nilfs->ns_gc_dat;
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c
index 3389ff0..8a05213 100644
--- a/fs/notify/inode_mark.c
+++ b/fs/notify/inode_mark.c
@@ -249,8 +249,11 @@ void fsnotify_unmount_inodes(struct list_head *list)
 		 * I_WILL_FREE, or I_NEW which is fine because by that point
 		 * the inode cannot have any associated watches.
 		 */
-		if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW))
+		spin_lock(&inode->i_lock);
+		if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 
 		/*
 		 * If the inode is not referenced, the inode cannot have any
@@ -258,9 +261,10 @@ void fsnotify_unmount_inodes(struct list_head *list)
 		 * actually evict all unreferenced inodes from icache which is
 		 * unnecessarily violent and may in fact be illegal to do.
 		 */
-		spin_lock(&inode->i_lock);
-		if (!inode->i_ref)
+		if (!inode->i_ref) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 
 		need_iput_tmp = need_iput;
 		need_iput = NULL;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index b7cbc41..c7b5fc6 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -899,18 +899,20 @@ static void add_dquot_ref(struct super_block *sb, int type)
 	spin_lock(&inode_lock);
 	spin_lock(&sb->s_inodes_lock);
 	list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
-		if (inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW))
+		spin_lock(&inode->i_lock);
+		if ((inode->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) ||
+		    !atomic_read(&inode->i_writecount) ||
+		    !dqinit_needed(inode, type)) {
+			spin_unlock(&inode->i_lock);
 			continue;
+		}
 #ifdef CONFIG_QUOTA_DEBUG
 		if (unlikely(inode_get_rsv_space(inode) > 0))
 			reserved = 1;
 #endif
-		if (!atomic_read(&inode->i_writecount))
-			continue;
-		if (!dqinit_needed(inode, type))
-			continue;
 
 		iref_locked(inode);
+		spin_unlock(&inode->i_lock);
 		spin_unlock(&sb->s_inodes_lock);
 		spin_unlock(&inode_lock);
 
-- 
1.7.1


^ permalink raw reply related


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.