* Re: [PATCH v2 1/5] dt: add of_alias_scan and of_alias_get_id
From: Shawn Guo @ 2011-07-15 15:24 UTC (permalink / raw)
To: Grant Likely
Cc: patches-QSEj5FYQhm4dnm+yROfE0A, netdev-u79uwXL29TY76Z2rM5mHXA,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-serial-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20110715025320.GE2927-e0URQFbLeQY2iJbIjFUEsiwD8/FfD2ys@public.gmane.org>
On Thu, Jul 14, 2011 at 08:53:20PM -0600, Grant Likely wrote:
> On Sat, Jun 25, 2011 at 02:04:32AM +0800, Shawn Guo wrote:
> > The patch adds function of_alias_scan to populate a global lookup
> > table with the properties of 'aliases' node and function
> > of_alias_get_id for drivers to find alias id from the lookup table.
> >
> > Signed-off-by: Shawn Guo <shawn.guo-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> > Cc: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
>
> Hey Shawn,
>
> Comments below, but I've gone ahead and fixed them because I'm keen to
> get this patch merged. I just wanted to let you know what I changed.
> I'll post the modified version so you can test it.
>
Thanks for doing this. It makes the wheel spin fast. I respect
changes you made, and will give it a test once you post it.
Regards,
Shawn
> > ---
> > drivers/of/base.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> > drivers/of/fdt.c | 6 ++
> > include/linux/of.h | 7 ++
> > 3 files changed, 194 insertions(+), 0 deletions(-)
> >
> > diff --git a/drivers/of/base.c b/drivers/of/base.c
> > index 632ebae..89efd10 100644
> > --- a/drivers/of/base.c
> > +++ b/drivers/of/base.c
> > @@ -17,14 +17,38 @@
> > * as published by the Free Software Foundation; either version
> > * 2 of the License, or (at your option) any later version.
> > */
> > +#include <linux/bootmem.h>
> > +#include <linux/ctype.h>
> > #include <linux/module.h>
> > #include <linux/of.h>
> > #include <linux/spinlock.h>
> > #include <linux/slab.h>
> > #include <linux/proc_fs.h>
> >
> > +/**
> > + * struct alias_prop - Alias property in 'aliases' node
> > + * @link: List node to link the structure in aliases_lookup list
> > + * @alias: Alias property name
> > + * @np: Pointer to device_node that the alias stands for
> > + * @alias_id: Alias id decoded from alias
> > + * @alias_stem: Alias stem name decoded from alias
> > + *
> > + * The structure represents one alias property of 'aliases' node as
> > + * an entry in aliases_lookup list.
> > + */
> > +struct alias_prop {
> > + struct list_head link;
> > + const char *alias;
> > + struct device_node *np;
> > + int alias_id;
> > + char alias_stem[0];
> > +};
> > +
> > +static LIST_HEAD(aliases_lookup);
> > +
> > struct device_node *allnodes;
> > struct device_node *of_chosen;
> > +struct device_node *of_aliases;
> >
> > /* use when traversing tree through the allnext, child, sibling,
> > * or parent members of struct device_node.
> > @@ -922,3 +946,160 @@ out_unlock:
> > }
> > #endif /* defined(CONFIG_OF_DYNAMIC) */
> >
> > +/*
> > + * of_alias_find_available_id - Find an available id for the given device_node
>
> Nit: kerneldoc format is "/**" to start the comment, and function
> names should have '()' on it.
>
> > + * @np: Pointer to the given device_node
> > + * @stem: Alias stem of the given device_node
> > + *
> > + * The function travels the lookup table to find an available id for the
> > + * given device_node with given alias stem. It returns the id found.
> > + */
> > +static int of_alias_find_available_id(struct device_node *np, const char *stem)
> > +{
> > + struct alias_prop *app;
> > + int id = 0;
> > +
> > + while (1) {
> > + bool used = false;
> > + list_for_each_entry(app, &aliases_lookup, link) {
> > + if (!strcmp(app->alias_stem, stem) &&
> > + app->alias_id == id) {
> > + used = true;
> > + break;
> > + }
> > + }
> > +
> > + if (used)
> > + id++;
> > + else
> > + return id;
> > + }
> > +}
>
> I've simplified this somewhat to do the list_for_each_entry() only
> once, and to bump up the id to +1 the largest entry:
>
> list_for_each_entry(app, &aliases_lookup, link)
> if ((strcmp(app->stem, stem) == 0) && id <= app->id)
> id = app->id + 1;
>
> .... actually, after looking further, I've rolled this code into the
> of_alias_get_id() function.
>
> > +
> > +/**
> > + * of_alias_lookup_add - Add alias into lookup table
> > + * @alias: Alias name
> > + * @id: Alias id
> > + * @stem: Alias stem name
> > + * @np: Pointer to device_node
> > + *
> > + * The function adds one alias into the lookup table and populate it
> > + * with the info passed in. It returns 0 in sucess, or error code.
> > + */
> > +static int of_alias_lookup_add(char *alias, int id, const char *stem,
> > + struct device_node *np)
> > +{
> > + struct alias_prop *app;
> > + size_t sz = sizeof(*app) + strlen(stem) + 1;
> > +
> > + app = kzalloc(sz, GFP_KERNEL);
> > + if (!app) {
> > + /*
> > + * It may fail due to being called by boot code,
> > + * so try alloc_bootmem before return error.
> > + */
> > + app = alloc_bootmem(sz);
> > + if (!app)
> > + return -ENOMEM;
> > + }
>
> I'm not too fond of this since we *know* when this is going to be
> called from early boot. So, instead, I've changed the code to use the
> already existing dt_alloc functions as an argument to of_alias_lookup_add().
>
> Also, alloc_bootmem() doesn't work on all platforms.
> early_init_dt_alloc_memory_arch() needs to be used instead.
>
>
> > +
> > + app->np = np;
> > + app->alias = alias;
> > + app->alias_id = id;
> > + strcpy(app->alias_stem, stem);
> > + list_add_tail(&app->link, &aliases_lookup);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * of_alias_decode - Decode alias stem and id from alias name
> > + * @alias: Alias name to be decoded
> > + * @stem: Pointer used to return stem name
> > + *
> > + * The function decodes alias stem name and alias id from the given
> > + * alias name. It returns stem name via parameter 'stem', and stem id
> > + * via the return value. If no id is found in alias name, it returns 0
> > + * as the default.
> > + */
> > +static int of_alias_decode(char *alias, char *stem)
> > +{
> > + int len = strlen(alias);
> > + char *end = alias + len;
> > +
> > + while (isdigit(*--end))
> > + len--;
>
> Need to protect against len < 0
>
> > +
> > + strncpy(stem, alias, len);
> > + stem[len] = '\0';
> > +
> > + return strlen(++end) ? simple_strtoul(end, NULL, 10) : 0;
> > +}
> > +
> > +/**
> > + * of_alias_scan - Scan all properties of 'aliases' node
> > + *
> > + * The function scans all the properties of 'aliases' node and populate
> > + * the the global lookup table with the properties. It returns the
> > + * number of alias_prop found, or error code in error case.
> > + */
> > +int of_alias_scan(void)
>
> Changed to '__init void of_aliases_scan(void)'. There is no point in
> returning an error code since it is never checked, and there is no
> point in bailing on an error just in case it is a problem with only
> one alias.
>
> > +{
> > + struct property *pp;
> > + int ret, num = 0;
> > +
> > + if (!of_aliases)
> > + return -ENODEV;
> > +
> > + for_each_property(pp, of_aliases->properties) {
> > + char stem[32];
> > + int id = of_alias_decode(pp->name, stem);
> > +
> > + /* Skip those we do not want to proceed */
> > + if (!strcmp(pp->name, "name") ||
> > + !strcmp(pp->name, "phandle") ||
> > + !strcmp(pp->name, "linux,phandle"))
> > + continue;
> > +
> > + ret = of_alias_lookup_add(pp->name, id, stem,
> > + of_find_node_by_path(pp->value));
> > + if (ret < 0)
> > + return ret;
> > + else
> > + num++;
> > + }
> > +
> > + return num;
> > +}
> > +
> > +/**
> > + * of_alias_get_id - Get alias id for the given device_node
> > + * @np: Pointer to the given device_node
> > + * @stem: Alias stem of the given device_node
> > + *
> > + * The function travels the lookup table to get alias id for the given
> > + * device_node and alias stem. It returns the alias id if find it.
> > + * If not, dynamically creates one in the lookup table and returns it,
> > + * or returns error code if fail to create.
> > + */
> > +int of_alias_get_id(struct device_node *np, const char *stem)
> > +{
> > + struct alias_prop *app;
> > + int id = -1;
> > +
> > + list_for_each_entry(app, &aliases_lookup, link) {
> > + if (np == app->np) {
> > + id = app->alias_id;
> > + break;
> > + }
> > + }
> > +
> > + if (id == -1) {
> > + id = of_alias_find_available_id(np, stem);
> > + if (of_alias_lookup_add(NULL, id, stem, np) < 0)
> > + return -ENODEV;
> > + }
> > +
> > + return id;
> > +}
>
> This whole function needs to be protected by a mutex.
>
> > +EXPORT_SYMBOL_GPL(of_alias_get_id);
> > diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> > index 65200af..998dc63 100644
> > --- a/drivers/of/fdt.c
> > +++ b/drivers/of/fdt.c
> > @@ -711,6 +711,12 @@ void __init unflatten_device_tree(void)
> > of_chosen = of_find_node_by_path("/chosen");
> > if (of_chosen == NULL)
> > of_chosen = of_find_node_by_path("/chosen@0");
> > +
> > + of_aliases = of_find_node_by_path("/aliases");
> > + if (of_aliases == NULL)
> > + of_aliases = of_find_node_by_path("/aliases@0");
>
> I don't think the /aliases@0 variant is necessary. I've dropped it.
>
> g.
>
> > +
> > + of_alias_scan();
> > }
> >
> > #endif /* CONFIG_OF_EARLY_FLATTREE */
> > diff --git a/include/linux/of.h b/include/linux/of.h
> > index bfc0ed1..c35cc47 100644
> > --- a/include/linux/of.h
> > +++ b/include/linux/of.h
> > @@ -68,6 +68,7 @@ struct device_node {
> > /* Pointer for first entry in chain of all nodes. */
> > extern struct device_node *allnodes;
> > extern struct device_node *of_chosen;
> > +extern struct device_node *of_aliases;
> > extern rwlock_t devtree_lock;
> >
> > static inline bool of_have_populated_dt(void)
> > @@ -201,6 +202,9 @@ extern int of_device_is_available(const struct device_node *device);
> > extern const void *of_get_property(const struct device_node *node,
> > const char *name,
> > int *lenp);
> > +#define for_each_property(pp, properties) \
> > + for (pp = properties; pp != NULL; pp = pp->next)
> > +
> > extern int of_n_addr_cells(struct device_node *np);
> > extern int of_n_size_cells(struct device_node *np);
> > extern const struct of_device_id *of_match_node(
> > @@ -213,6 +217,9 @@ extern int of_parse_phandles_with_args(struct device_node *np,
> > const char *list_name, const char *cells_name, int index,
> > struct device_node **out_node, const void **out_args);
> >
> > +extern int of_alias_scan(void);
> > +extern int of_alias_get_id(struct device_node *np, const char *stem);
> > +
> > extern int of_machine_is_compatible(const char *compat);
> >
> > extern int prom_add_property(struct device_node* np, struct property* prop);
> > --
> > 1.7.4.1
> >
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
^ permalink raw reply
* Re: [PATCH 09/10] nfs: use sk fragment destructors to delay I/O completion until page is released by network stack.
From: Ian Campbell @ 2011-07-15 15:21 UTC (permalink / raw)
To: Trond Myklebust
Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-nfs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1310738489.4381.20.camel-SyLVLa/KEI9HwK5hSS5vWB2eb7JE58TQ@public.gmane.org>
On Fri, 2011-07-15 at 15:01 +0100, Trond Myklebust wrote:
> On Fri, 2011-07-15 at 12:07 +0100, Ian Campbell wrote:
> > Thos prevents an issue where an ACK is delayed, a retransmit is queued (either
> > at the RPC or TCP level) and the ACK arrives before the retransmission hits the
> > wire. If this happens then the write() system call and the userspace process
> > can continue potentially modifying the data before the retransmission occurs.
> >
> > NB: this only covers the O_DIRECT write() case. I expect other cases to need
> > handling as well.
>
> That is why this belongs entirely in the RPC layer, and really should
> not touch the NFS layer.
> If you move your callback to the RPC layer and have it notify the
> rpc_task when the pages have been sent, then it should be possible to
> achieve the same thing.
>
> IOW: Add an extra state machine step after call_decode() which checks if
> all the page data has been transmitted and if not, puts the rpc_task on
> a wait queue, and has it wait for the fragment destructor callback
> before calling rpc_exit_task().
Make sense, I'll do that.
Thanks,
Ian.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] Fix panic in virtnet_remove
From: David Miller @ 2011-07-15 15:19 UTC (permalink / raw)
To: krkumar2; +Cc: netdev, shemminger
In-Reply-To: <20110715091650.23026.78221.sendpatchset@krkumar2.in.ibm.com>
From: Krishna Kumar <krkumar2@in.ibm.com>
Date: Fri, 15 Jul 2011 14:46:50 +0530
> modprobe -r virtio_net panics in free_netdev() as the
> dev is already freed in the newly introduced virtnet_free
> (commit 3fa2a1df9094). Since virtnet_remove doesn't require
> dev after unregister, I am removing the free_netdev call
> in virtnet_remove instead of in virtnet_free (which seems
> to be the right place to free the dev). Confirmed that
> the panic is fixed with this patch.
>
> Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com>
I guess the virtio NET maintainers don't deserve to get CC:'d on a fix
like this? :-(
Michael Tsirkin is who maintains this driver actively and is the one
who will merge this kind of fix to me, therefore if you don't CC:
him it might not get integrated at all.
^ permalink raw reply
* Re: [PATCH/RFC 0/10] enable SKB paged fragment lifetime visibility
From: David Miller @ 2011-07-15 15:17 UTC (permalink / raw)
To: Ian.Campbell-Sxgqhf6Nn4DQT0dZR+AlfA
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1310728006.20648.3.camel-o4Be2W7LfRlXesXXhkcM7miJhflN2719@public.gmane.org>
From: Ian Campbell <Ian.Campbell-Sxgqhf6Nn4DQT0dZR+AlfA@public.gmane.org>
Date: Fri, 15 Jul 2011 12:06:46 +0100
> What is the general feeling regarding this approach?
Not bad, I like that only the users of destructors pay the price of
the extra atomics.
Like you say in patch #8, I wouldn't bother adding a whole new
->sendpage_destructor() OP, just add the new argument to the existing
method.
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] [net][bna] Separate handling of irq type flags variable from the irq_flags request_irq variable
From: David Miller @ 2011-07-15 15:09 UTC (permalink / raw)
To: shyam.iyer.t; +Cc: netdev, rmody, ddutt, huangj, ivecera, shyam_iyer
In-Reply-To: <1310691632-22914-1-git-send-email-shyam_iyer@dell.com>
From: Shyam Iyer <shyam.iyer.t@gmail.com>
Date: Thu, 14 Jul 2011 21:00:32 -0400
> Commit 5f77898de17ff983ff0e2988b73a6bdf4b6f9f8b does not completely fix the problem of handling allocations with irqs disabled..
> The below patch on top of it fixes the problem completely.
>
> Based on review by "Ivan Vecera" <ivecera@redhat.com>..
> "
> Small note, the root of the problem was that non-atomic allocation was requested with IRQs disabled. Your patch description does not contain wwhy were the IRQs disabled.
>
> The function bnad_mbox_irq_alloc incorrectly uses 'flags' var for two different things, 1) to save current CPU flags and 2) for request_irq
> call.
> First the spin_lock_irqsave disables the IRQs and saves _all_ CPU flags (including one that enables/disables interrupts) to 'flags'. Then the 'flags' is overwritten by 0 or 0x80 (IRQF_SHARED). Finally the spin_unlock_irqrestore should restore saved flags, but these flags are now either 0x00 or 0x80. The interrupt bit value in flags register on x86 arch is 0x100.
> This means that the interrupt bit is zero (IRQs disabled) after spin_unlock_irqrestore so the request_irq function is called with disabled interrupts.
> "
>
> Signed-off-by: Shyam Iyer <shyam_iyer@dell.com>
Applied.
^ permalink raw reply
* Re: [PATCH 1/1] r6040: only disable RX interrupt if napi_schedule_prep is successful
From: David Miller @ 2011-07-15 15:10 UTC (permalink / raw)
To: Michael.Thalmeier; +Cc: florian, netdev, linux-kernel, michael
In-Reply-To: <1310729306-12190-1-git-send-email-Michael.Thalmeier@sigmatek.at>
From: Michael Thalmeier <Michael.Thalmeier@sigmatek.at>
Date: Fri, 15 Jul 2011 13:28:26 +0200
> When receiving the first RX interrupt before the internal call
> to napi_schedule_prep is successful the RX interrupt gets disabled
> and is never enabled again as the poll function never gets executed.
>
> Signed-off-by: Michael Thalmeier <Michael.Thalmeier@sigmatek.at>
Applied.
^ permalink raw reply
* Re: [PATCH net-next-2.6] slcan: remove obsolete code in slcan initialisation
From: David Miller @ 2011-07-15 15:09 UTC (permalink / raw)
To: socketcan; +Cc: netdev, matvejchikov
In-Reply-To: <4E1FF0FF.2020808@hartkopp.net>
From: Oliver Hartkopp <socketcan@hartkopp.net>
Date: Fri, 15 Jul 2011 09:49:19 +0200
> This patch removes obsolete code in the initialisation/creation of
> slcan devices.
>
> It follows the suggested cleanups from Ilya Matvejchikov in
> drivers/net/slip.c that where recently applied to net-next-2.6:
>
> - slip: remove dead code within the slip initialization
> - slip: remove redundant check slip_devs for NULL
>
> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Applied.
^ permalink raw reply
* Re: [PATCH] Remove redundant variable/code in __qdisc_run
From: David Miller @ 2011-07-15 15:09 UTC (permalink / raw)
To: krkumar2; +Cc: netdev
In-Reply-To: <20110715091621.23013.37893.sendpatchset@krkumar2.in.ibm.com>
From: Krishna Kumar <krkumar2@in.ibm.com>
Date: Fri, 15 Jul 2011 14:46:21 +0530
> Remove redundant variable "work".
>
> Signed-off-by: Krishna Kumar <krkumar2@in.ibm.com>
Applied.
^ permalink raw reply
* Re: [PATCH] 8139cp: convert to new VLAN model.
From: David Miller @ 2011-07-15 15:09 UTC (permalink / raw)
To: jpirko; +Cc: romieu, shemminger, netdev
In-Reply-To: <20110715113631.GB2250@minipsycho.brq.redhat.com>
From: Jiri Pirko <jpirko@redhat.com>
Date: Fri, 15 Jul 2011 13:36:31 +0200
> Similar patch is in my local git :)
>
> Reviewed-by: Jiri Pirko <jpirko@redhat.com>
Applied to net-next-2.6, thanks.
^ permalink raw reply
* Re: [PATCH] gianfar: rx parser
From: David Miller @ 2011-07-15 15:06 UTC (permalink / raw)
To: sebastian.belden; +Cc: netdev, jpirko, sandeep.kumar
In-Reply-To: <1310728141.28792.10.camel@DENEC1DT0191>
From: "Sebastian Pöhn" <sebastian.belden@googlemail.com>
Date: Fri, 15 Jul 2011 13:09:01 +0200
> + struct gfar __iomem *regs = NULL;
> + u32 tempval = 0;
> +
> + regs = priv->gfargrp[0].regs;
There is no reason to initialize the variable 'regs' to NULL only to
initialize it to something else 2 lines later.
The initial 'tempval' initialization is also redundant, for the same
reason.
^ permalink raw reply
* (unknown),
From: Mr. Vincent Cheng @ 2011-07-15 14:31 UTC (permalink / raw)
Good Day,
I have a business proposal of USD $22,500,000.00 only for you to transact with
me from my bank to your country.
Reply to address:choi_chu112@yahoo.co.jp and I will let you know what is
required of you.
Best Regards,
Mr. Vincent Cheng
^ permalink raw reply
* Re: iwlagn: Random "Time out reading EEPROM".
From: wwguy @ 2011-07-15 14:28 UTC (permalink / raw)
To: Nicolas de Pesloüan
Cc: dhalperi-GmWTxIRN22iJaUV4rX00uodd74u8MsAO@public.gmane.org,
netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, wireless,
wey-yi.w.guy-ral2JQCrhuEAvxtiuMwx3w
In-Reply-To: <4E1FF8B8.9020005-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Fri, 2011-07-15 at 01:22 -0700, Nicolas de Pesloüan wrote:
> Le 05/06/2011 16:31, wwguy a écrit :
> > On Sat, 2011-06-04 at 06:14 -0700, Nicolas de Pesloüan wrote:
> >> Hi,
> >>
> >> From time to time, my Intel Wifi adapter fail on cold boot, with the following in dmesg:
> >>
> >> [ 7.442634] iwlagn: Intel(R) Wireless WiFi Link AGN driver for Linux, 1.3.27ks
> >> [ 7.442636] iwlagn: Copyright(c) 2003-2009 Intel Corporation
> >> [ 7.442701] iwlagn 0000:05:00.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
> >> [ 7.442708] iwlagn 0000:05:00.0: setting latency timer to 64
> >> [ 7.442788] iwlagn 0000:05:00.0: Detected Intel Wireless WiFi Link 5100AGN REV=0x54
> >> [ 7.455547] iwlagn 0000:05:00.0: Time out reading EEPROM[0]
> >> [ 7.455611] iwlagn 0000:05:00.0: Unable to init EEPROM
> >> [ 7.455678] iwlagn 0000:05:00.0: PCI INT A disabled
> >> [ 7.455685] iwlagn: probe of 0000:05:00.0 failed with error -110
> >>
> >> After "modprobe -r iwlagn ; modprobe iwlagn" or after a reboot, everything work fine:
> >>
> >> [ 346.332166] iwlagn: Intel(R) Wireless WiFi Link AGN driver for Linux, 1.3.27ks
> >> [ 346.332169] iwlagn: Copyright(c) 2003-2009 Intel Corporation
> >> [ 346.332225] iwlagn 0000:05:00.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
> >> [ 346.332234] iwlagn 0000:05:00.0: setting latency timer to 64
> >> [ 346.332277] iwlagn 0000:05:00.0: Detected Intel Wireless WiFi Link 5100AGN REV=0x54
> >> [ 346.365739] iwlagn 0000:05:00.0: Tunable channels: 13 802.11bg, 24 802.11a channels
> >> [ 346.365787] alloc irq_desc for 33 on node -1
> >> [ 346.365788] alloc kstat_irqs on node -1
> >> [ 346.365804] iwlagn 0000:05:00.0: irq 33 for MSI/MSI-X
> >> [ 346.449989] phy0: Selected rate control algorithm 'iwl-agn-rs'
> >>
> >> This happens using 2.6.32-5 from Debian. I didn't had the opportunity to reproduce it using recent
> >> kernel, but it occurs too rarely to conclude anything.
> >>
> >> I had a look at the git log, without finding something that seems related to this problem. Does this
> >> ring a bell for someone?
> >>
> >> Nicolas.
> >>
> >> 05:00.0 Network controller: Intel Corporation WiFi Link 5100
> >> Subsystem: Intel Corporation WiFi Link 5100 AGN
> >> Control: I/O- Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR-
> >> FastB2B- DisINTx-
> >> Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast>TAbort-<TAbort-<MAbort->SERR-
> >> <PERR- INTx-
> >> Latency: 0, Cache Line Size: 64 bytes
> >> Interrupt: pin A routed to IRQ 33
> >> Region 0: Memory at d6d00000 (64-bit, non-prefetchable) [size=8K]
> >> Capabilities: [c8] Power Management version 3
> >> Flags: PMEClk- DSI+ D1- D2- AuxCurrent=0mA PME(D0+,D1-,D2-,D3hot+,D3cold+)
> >> Status: D0 NoSoftRst- PME-Enable- DSel=0 DScale=0 PME-
> >> Capabilities: [d0] MSI: Enable+ Count=1/1 Maskable- 64bit+
> >> Address: 00000000fee0300c Data: 41c9
> >> Capabilities: [e0] Express (v1) Endpoint, MSI 00
> >> DevCap: MaxPayload 128 bytes, PhantFunc 0, Latency L0s<512ns, L1 unlimited
> >> ExtTag- AttnBtn- AttnInd- PwrInd- RBE+ FLReset+
> >> DevCtl: Report errors: Correctable- Non-Fatal- Fatal- Unsupported-
> >> RlxdOrd+ ExtTag- PhantFunc- AuxPwr- NoSnoop+ FLReset-
> >> MaxPayload 128 bytes, MaxReadReq 128 bytes
> >> DevSta: CorrErr+ UncorrErr- FatalErr- UnsuppReq+ AuxPwr+ TransPend-
> >> LnkCap: Port #0, Speed 2.5GT/s, Width x1, ASPM L0s L1, Latency L0<128ns, L1<32us
> >> ClockPM+ Surprise- LLActRep- BwNot-
> >> LnkCtl: ASPM L1 Enabled; RCB 64 bytes Disabled- Retrain- CommClk+
> >> ExtSynch- ClockPM- AutWidDis- BWInt- AutBWInt-
> >> LnkSta: Speed 2.5GT/s, Width x1, TrErr- Train- SlotClk+ DLActive- BWMgmt- ABWMgmt-
> >> Capabilities: [100 v1] Advanced Error Reporting
> >> UESta: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC-
> >> UnsupReq- ACSViol-
> >> UEMsk: DLP- SDES- TLP- FCP- CmpltTO- CmpltAbrt- UnxCmplt- RxOF- MalfTLP- ECRC-
> >> UnsupReq- ACSViol-
> >> UESvrt: DLP+ SDES- TLP- FCP+ CmpltTO- CmpltAbrt- UnxCmplt- RxOF+ MalfTLP+ ECRC-
> >> UnsupReq- ACSViol-
> >> CESta: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
> >> CEMsk: RxErr- BadTLP- BadDLLP- Rollover- Timeout- NonFatalErr+
> >> AERCap: First Error Pointer: 00, GenCap- CGenEn- ChkCap- ChkEn-
> >> Capabilities: [140 v1] Device Serial Number 00-22-fb-ff-ff-28-46-c8
> >> Kernel driver in use: iwlagn
> >>
> >
> > no sure, you are the ONLY one report this issue, since the device works
> > after you reload the module, the HW/SW is ok. maybe the timing/sequence
> > have problem on the kernel you are using on your system. The bus is not
> > ready when you boot the system and access the EEPROM.
> >
> > What model of the laptop you have? could you try to upgrade to newer
> > kernel?
>
> This problem also happens using 2.6.39-2-amd64 from Debian.
>
> [ 8.507426] iwlagn: Intel(R) Wireless WiFi Link AGN driver for Linux, in-tree:
> [ 8.507429] iwlagn: Copyright(c) 2003-2010 Intel Corporation
> [ 8.507521] iwlagn 0000:05:00.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
> [ 8.507529] iwlagn 0000:05:00.0: setting latency timer to 64
> [ 8.507556] iwlagn 0000:05:00.0: Detected Intel(R) WiFi Link 5100 AGN, REV=0x54
> [ 8.516687] iwlagn 0000:05:00.0: Time out reading EEPROM[2]
> [ 8.516724] iwlagn 0000:05:00.0: Unable to init EEPROM
> [ 8.516739] iwlagn 0000:05:00.0: PCI INT A disabled
> [ 8.516745] iwlagn: probe of 0000:05:00.0 failed with error -110
>
> modprobe -r iwlagn ; modprobe iwlagn
>
> [ 208.948664] iwlagn: Intel(R) Wireless WiFi Link AGN driver for Linux, in-tree:
> [ 208.948666] iwlagn: Copyright(c) 2003-2010 Intel Corporation
> [ 208.948743] iwlagn 0000:05:00.0: PCI INT A -> GSI 18 (level, low) -> IRQ 18
> [ 208.948753] iwlagn 0000:05:00.0: setting latency timer to 64
> [ 208.948817] iwlagn 0000:05:00.0: Detected Intel(R) WiFi Link 5100 AGN, REV=0x54
> [ 208.969659] iwlagn 0000:05:00.0: device EEPROM VER=0x11f, CALIB=0x4
> [ 208.969661] iwlagn 0000:05:00.0: Device SKU: 0Xb
> [ 208.969701] iwlagn 0000:05:00.0: Tunable channels: 13 802.11bg, 24 802.11a channels
> [ 208.969766] iwlagn 0000:05:00.0: irq 50 for MSI/MSI-X
> [ 209.048366] iwlagn 0000:05:00.0: loaded firmware version 8.83.5.1 build 33692
>
> Because it happens not very often, I still didn't had the opportunity to reproduce it with up to
> date kernel.
>
> The laptop is a two years old Sony Vaio VGN-AW21M.
>
the error indicate fail to read data from EEPROM, your 2nd report is
even more strange, the number at the end the error message indicate the
index of DWORD driver trying to read from EEPROM.
"Time out reading EEPROM[2]" telling me the first 2 DWORD is reading ok
but not the 3rd read.
How many PCI-E slots you have in your system, could it possible for you
to switch to another PCI-E slot, or pull out and re-insert the NIC.
Also, it is possible to put the NIC into different system and see if you
are seeing the similar problem?
Thanks
Wey
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 00/14] Swap-over-NBD without deadlocking v5
From: Mel Gorman @ 2011-07-15 14:10 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Linux-MM, Linux-Netdev, LKML, David Miller,
Neil Brown, Peter Zijlstra
In-Reply-To: <20110707125831.GA15412@infradead.org>
On Thu, Jul 07, 2011 at 08:58:31AM -0400, Christoph Hellwig wrote:
> On Thu, Jul 07, 2011 at 10:47:37AM +0100, Mel Gorman wrote:
> > Additional complexity is required for swap-over-NFS but affects the
> > core kernel far less than this series. I do not have a series prepared
> > but from what's in a distro kernel, supporting NFS requires extending
> > address_space_operations for swapfile activation/deactivation with
> > some minor helpers and the bulk of the remaining complexity within
> > NFS itself.
>
> The biggest addition for swap over NFS is to add proper support for
> a filesystem interface to do I/O on random kernel pages instead of
> the current nasty bmap hack the swapfile code is using. Splitting
> that work from all the required VM infrastructure should make life
> easier for everyone involved and allows merging it independeny as
> both bits have other uses case as well.
>
The swap-over-nfs patches allows this possibility. There is a swapon
interface added that could be used by the filesystem to ensure the
underlying blocks are allocated and a swap_out/swap_in interface that
takes a struct file, struct page and writeback_control. This would
be an alternative to bmap being used to record the blocks backing
each extent.
Any objection to the swap-over-NBD stuff going ahead to get part of the
complexity out of the way?
--
Mel Gorman
SUSE Labs
--
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/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply
* Re: [PATCH 09/10] nfs: use sk fragment destructors to delay I/O completion until page is released by network stack.
From: Trond Myklebust @ 2011-07-15 14:01 UTC (permalink / raw)
To: Ian Campbell
Cc: netdev-u79uwXL29TY76Z2rM5mHXA, linux-nfs-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1310728031-19569-9-git-send-email-ian.campbell-Sxgqhf6Nn4DQT0dZR+AlfA@public.gmane.org>
On Fri, 2011-07-15 at 12:07 +0100, Ian Campbell wrote:
> Thos prevents an issue where an ACK is delayed, a retransmit is queued (either
> at the RPC or TCP level) and the ACK arrives before the retransmission hits the
> wire. If this happens then the write() system call and the userspace process
> can continue potentially modifying the data before the retransmission occurs.
>
> NB: this only covers the O_DIRECT write() case. I expect other cases to need
> handling as well.
That is why this belongs entirely in the RPC layer, and really should
not touch the NFS layer.
If you move your callback to the RPC layer and have it notify the
rpc_task when the pages have been sent, then it should be possible to
achieve the same thing.
IOW: Add an extra state machine step after call_decode() which checks if
all the page data has been transmitted and if not, puts the rpc_task on
a wait queue, and has it wait for the fragment destructor callback
before calling rpc_exit_task().
Cheers
Trond
--
Trond Myklebust
Linux NFS client maintainer
NetApp
Trond.Myklebust-HgOvQuBEEgTQT0dZR+AlfA@public.gmane.org
www.netapp.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Mail Quota alert -Error Code
From: System Administrator @ 2011-07-15 11:48 UTC (permalink / raw)
Your Mail Quota Has Exceeded The Set Quota/Limit. You Are Currently
Running On 23GB Due To Hidden Files And Folder On Your Mailbox,
you may not be able to receive or send new mails until you re-validate
Please Click the Link Below To Validate Your Mailbox And Increase Your Quota.
http://buzurl.com/ao70
Failure To Validate Your Quota May Result In Loss Of Important Information
In Your Mailbox Or Cause Limited Access To It.
Mail Quota alert -Error Code #1997142DDE
System Administrator
192.168.0.1
----------------------------------------------------------------
This message was sent using IMP, the Internet Messaging Program.
^ permalink raw reply
* Re: [PATCH] gianfar: rx parser
From: Jiri Pirko @ 2011-07-15 11:41 UTC (permalink / raw)
To: Sebastian Pöhn; +Cc: Linux Netdev, sandeep.kumar
In-Reply-To: <1310728141.28792.10.camel@DENEC1DT0191>
Fri, Jul 15, 2011 at 01:09:01PM CEST, sebastian.belden@googlemail.com wrote:
>Only let the rx parser be enabled if it is necessary (if VLAN extracrtion, IP or TCP checksumming or the rx queue filer are enabled). Otherwise disable it.
>
>The new routine gfar_check_rx_parser_mode should be runt after every change on this features and will enable/disable the parser as necessary.
>
>I will send a patch for the rx queue filer the next days.
Looks good to me.
Reviewed-by: Jiri Pirko <jpirko@redhat.com>
>
>
>
>Signed-off-by: Sebastian Poehn <sebastian.poehn@belden.com>
>---
>
> drivers/net/gianfar.c | 25 ++++++++++++++++++++-----
> drivers/net/gianfar.h | 3 ++-
> 2 files changed, 22 insertions(+), 6 deletions(-)
>
>diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
>index 3321d71..6da3712 100644
>--- a/drivers/net/gianfar.c
>+++ b/drivers/net/gianfar.c
>@@ -2287,6 +2287,23 @@ static int gfar_set_mac_address(struct net_device *dev)
> return 0;
> }
>
>+/* Check if rx parser should be activated */
>+void gfar_check_rx_parser_mode(struct gfar_private *priv)
>+{
>+ struct gfar __iomem *regs = NULL;
>+ u32 tempval = 0;
>+
>+ regs = priv->gfargrp[0].regs;
>+
>+ tempval = gfar_read(®s->rctrl);
>+ /* If parse is no longer required, then disable parser */
>+ if (tempval & RCTRL_REQ_PARSER)
>+ tempval |= RCTRL_PRSDEP_INIT;
>+ else
>+ tempval &= ~RCTRL_PRSDEP_INIT;
>+ gfar_write(®s->rctrl, tempval);
>+}
>+
>
> /* Enables and disables VLAN insertion/extraction */
> static void gfar_vlan_rx_register(struct net_device *dev,
>@@ -2323,12 +2340,10 @@ static void gfar_vlan_rx_register(struct net_device *dev,
> /* Disable VLAN tag extraction */
> tempval = gfar_read(®s->rctrl);
> tempval &= ~RCTRL_VLEX;
>- /* If parse is no longer required, then disable parser */
>- if (tempval & RCTRL_REQ_PARSER)
>- tempval |= RCTRL_PRSDEP_INIT;
>- else
>- tempval &= ~RCTRL_PRSDEP_INIT;
> gfar_write(®s->rctrl, tempval);
>+
>+ gfar_check_rx_parser_mode(priv);
>+
> }
>
> gfar_change_mtu(dev, dev->mtu);
>diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
>index 27499c6..87c1d86 100644
>--- a/drivers/net/gianfar.h
>+++ b/drivers/net/gianfar.h
>@@ -286,7 +286,7 @@ extern const char gfar_driver_version[];
> #define RCTRL_PROM 0x00000008
> #define RCTRL_EMEN 0x00000002
> #define RCTRL_REQ_PARSER (RCTRL_VLEX | RCTRL_IPCSEN | \
>- RCTRL_TUCSEN)
>+ RCTRL_TUCSEN | RCTRL_FILREN)
> #define RCTRL_CHECKSUMMING (RCTRL_IPCSEN | RCTRL_TUCSEN | \
> RCTRL_PRSDEP_INIT)
> #define RCTRL_EXTHASH (RCTRL_GHTX)
>@@ -1182,6 +1182,7 @@ extern void gfar_configure_coalescing(struct gfar_private *priv,
> unsigned long tx_mask, unsigned long rx_mask);
> void gfar_init_sysfs(struct net_device *dev);
> int gfar_set_features(struct net_device *dev, u32 features);
>+extern void gfar_check_rx_parser_mode(struct gfar_private *priv);
>
> extern const struct ethtool_ops gfar_ethtool_ops;
>
>
>
>
>
^ permalink raw reply
* Re: [PATCH] 8139cp: convert to new VLAN model.
From: Jiri Pirko @ 2011-07-15 11:36 UTC (permalink / raw)
To: Francois Romieu; +Cc: Stephen Hemminger, netdev
In-Reply-To: <20110715102144.GA6601@electric-eye.fr.zoreil.com>
Similar patch is in my local git :)
Reviewed-by: Jiri Pirko <jpirko@redhat.com>
Fri, Jul 15, 2011 at 12:21:44PM CEST, romieu@fr.zoreil.com wrote:
>The registers and descriptors bits are identical to the pre-8168
>8169 chipsets : {RxDesc / TxDesc}.opts2 can only contain VLAN information.
>
>Signed-off-by: Francois Romieu <romieu@fr.zoreil.com>
>---
> drivers/net/8139cp.c | 83 ++++++++++++++++---------------------------------
> 1 files changed, 27 insertions(+), 56 deletions(-)
>
>diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
>index 73b10b0..cc4c210 100644
>--- a/drivers/net/8139cp.c
>+++ b/drivers/net/8139cp.c
>@@ -78,17 +78,6 @@
> #include <asm/irq.h>
> #include <asm/uaccess.h>
>
>-/* VLAN tagging feature enable/disable */
>-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
>-#define CP_VLAN_TAG_USED 1
>-#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \
>- do { (tx_desc)->opts2 = cpu_to_le32(vlan_tag_value); } while (0)
>-#else
>-#define CP_VLAN_TAG_USED 0
>-#define CP_VLAN_TX_TAG(tx_desc,vlan_tag_value) \
>- do { (tx_desc)->opts2 = 0; } while (0)
>-#endif
>-
> /* These identify the driver base version and may not be removed. */
> static char version[] =
> DRV_NAME ": 10/100 PCI Ethernet driver v" DRV_VERSION " (" DRV_RELDATE ")\n";
>@@ -356,9 +345,6 @@ struct cp_private {
> unsigned rx_buf_sz;
> unsigned wol_enabled : 1; /* Is Wake-on-LAN enabled? */
>
>-#if CP_VLAN_TAG_USED
>- struct vlan_group *vlgrp;
>-#endif
> dma_addr_t ring_dma;
>
> struct mii_if_info mii_if;
>@@ -423,24 +409,6 @@ static struct {
> };
>
>
>-#if CP_VLAN_TAG_USED
>-static void cp_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
>-{
>- struct cp_private *cp = netdev_priv(dev);
>- unsigned long flags;
>-
>- spin_lock_irqsave(&cp->lock, flags);
>- cp->vlgrp = grp;
>- if (grp)
>- cp->cpcmd |= RxVlanOn;
>- else
>- cp->cpcmd &= ~RxVlanOn;
>-
>- cpw16(CpCmd, cp->cpcmd);
>- spin_unlock_irqrestore(&cp->lock, flags);
>-}
>-#endif /* CP_VLAN_TAG_USED */
>-
> static inline void cp_set_rxbufsize (struct cp_private *cp)
> {
> unsigned int mtu = cp->dev->mtu;
>@@ -455,18 +423,17 @@ static inline void cp_set_rxbufsize (struct cp_private *cp)
> static inline void cp_rx_skb (struct cp_private *cp, struct sk_buff *skb,
> struct cp_desc *desc)
> {
>+ u32 opts2 = le32_to_cpu(desc->opts2);
>+
> skb->protocol = eth_type_trans (skb, cp->dev);
>
> cp->dev->stats.rx_packets++;
> cp->dev->stats.rx_bytes += skb->len;
>
>-#if CP_VLAN_TAG_USED
>- if (cp->vlgrp && (desc->opts2 & cpu_to_le32(RxVlanTagged))) {
>- vlan_hwaccel_receive_skb(skb, cp->vlgrp,
>- swab16(le32_to_cpu(desc->opts2) & 0xffff));
>- } else
>-#endif
>- netif_receive_skb(skb);
>+ if (opts2 & RxVlanTagged)
>+ __vlan_hwaccel_put_tag(skb, swab16(opts2 & 0xffff));
>+
>+ napi_gro_receive(&cp->napi, skb);
> }
>
> static void cp_rx_err_acct (struct cp_private *cp, unsigned rx_tail,
>@@ -730,6 +697,12 @@ static void cp_tx (struct cp_private *cp)
> netif_wake_queue(cp->dev);
> }
>
>+static inline u32 cp_tx_vlan_tag(struct sk_buff *skb)
>+{
>+ return vlan_tx_tag_present(skb) ?
>+ TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00;
>+}
>+
> static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
> struct net_device *dev)
> {
>@@ -737,9 +710,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
> unsigned entry;
> u32 eor, flags;
> unsigned long intr_flags;
>-#if CP_VLAN_TAG_USED
>- u32 vlan_tag = 0;
>-#endif
>+ __le32 opts2;
> int mss = 0;
>
> spin_lock_irqsave(&cp->lock, intr_flags);
>@@ -752,15 +723,12 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
> return NETDEV_TX_BUSY;
> }
>
>-#if CP_VLAN_TAG_USED
>- if (vlan_tx_tag_present(skb))
>- vlan_tag = TxVlanTag | swab16(vlan_tx_tag_get(skb));
>-#endif
>-
> entry = cp->tx_head;
> eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0;
> mss = skb_shinfo(skb)->gso_size;
>
>+ opts2 = cpu_to_le32(cp_tx_vlan_tag(skb));
>+
> if (skb_shinfo(skb)->nr_frags == 0) {
> struct cp_desc *txd = &cp->tx_ring[entry];
> u32 len;
>@@ -768,7 +736,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
>
> len = skb->len;
> mapping = dma_map_single(&cp->pdev->dev, skb->data, len, PCI_DMA_TODEVICE);
>- CP_VLAN_TX_TAG(txd, vlan_tag);
>+ txd->opts2 = opts2;
> txd->addr = cpu_to_le64(mapping);
> wmb();
>
>@@ -839,7 +807,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
> ctrl |= LastFrag;
>
> txd = &cp->tx_ring[entry];
>- CP_VLAN_TX_TAG(txd, vlan_tag);
>+ txd->opts2 = opts2;
> txd->addr = cpu_to_le64(mapping);
> wmb();
>
>@@ -851,7 +819,7 @@ static netdev_tx_t cp_start_xmit (struct sk_buff *skb,
> }
>
> txd = &cp->tx_ring[first_entry];
>- CP_VLAN_TX_TAG(txd, vlan_tag);
>+ txd->opts2 = opts2;
> txd->addr = cpu_to_le64(first_mapping);
> wmb();
>
>@@ -1431,6 +1399,11 @@ static int cp_set_features(struct net_device *dev, u32 features)
> else
> cp->cpcmd &= ~RxChkSum;
>
>+ if (features & NETIF_F_HW_VLAN_RX)
>+ cp->cpcmd |= RxVlanOn;
>+ else
>+ cp->cpcmd &= ~RxVlanOn;
>+
> cpw16_f(CpCmd, cp->cpcmd);
> spin_unlock_irqrestore(&cp->lock, flags);
>
>@@ -1818,9 +1791,6 @@ static const struct net_device_ops cp_netdev_ops = {
> .ndo_start_xmit = cp_start_xmit,
> .ndo_tx_timeout = cp_tx_timeout,
> .ndo_set_features = cp_set_features,
>-#if CP_VLAN_TAG_USED
>- .ndo_vlan_rx_register = cp_vlan_rx_register,
>-#endif
> #ifdef BROKEN
> .ndo_change_mtu = cp_change_mtu,
> #endif
>@@ -1949,15 +1919,16 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
> dev->ethtool_ops = &cp_ethtool_ops;
> dev->watchdog_timeo = TX_TIMEOUT;
>
>-#if CP_VLAN_TAG_USED
> dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
>-#endif
>
> if (pci_using_dac)
> dev->features |= NETIF_F_HIGHDMA;
>
> /* disabled by default until verified */
>- dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
>+ dev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
>+ NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
>+ dev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO |
>+ NETIF_F_HIGHDMA;
>
> dev->irq = pdev->irq;
>
>--
>1.7.4.4
>
^ permalink raw reply
* [PATCH 1/1] r6040: only disable RX interrupt if napi_schedule_prep is successful
From: Michael Thalmeier @ 2011-07-15 11:28 UTC (permalink / raw)
To: Florian Fainelli; +Cc: netdev, linux-kernel, michael, Michael Thalmeier
When receiving the first RX interrupt before the internal call
to napi_schedule_prep is successful the RX interrupt gets disabled
and is never enabled again as the poll function never gets executed.
Signed-off-by: Michael Thalmeier <Michael.Thalmeier@sigmatek.at>
---
drivers/net/r6040.c | 8 +++++---
1 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c
index 200a363..0ffec46 100644
--- a/drivers/net/r6040.c
+++ b/drivers/net/r6040.c
@@ -677,9 +677,11 @@ static irqreturn_t r6040_interrupt(int irq, void *dev_id)
if (status & RX_FIFO_FULL)
dev->stats.rx_fifo_errors++;
- /* Mask off RX interrupt */
- misr &= ~RX_INTS;
- napi_schedule(&lp->napi);
+ if (likely(napi_schedule_prep(&lp->napi))) {
+ /* Mask off RX interrupt */
+ misr &= ~RX_INTS;
+ __napi_schedule(&lp->napi);
+ }
}
/* TX interrupt request */
--
1.7.1
^ permalink raw reply related
* [PATCH 07/10] net: add support for per-paged-fragment destructors
From: Ian Campbell @ 2011-07-15 11:07 UTC (permalink / raw)
To: netdev; +Cc: linux-nfs, Ian Campbell
In-Reply-To: <1310728006.20648.3.camel@zakaz.uk.xensource.com>
Entities which care about the complete lifecycle of pages which they inject
into the network stack via an skb paged fragment can choose to set this
destructor in order to receive a callback when the stack is really finished
with a page (including all clones, retransmits, pull-ups etc etc).
This destructor will always be propagated alongside the struct page when
copying skb_frag_t->page. This is the reason I chose to embed the destructor in
a "struct { } page" within the skb_frag_t, rather than as a separate field,
since it allows existing code which propagates ->frags[N].page to Just
Work(tm).
When the destructor is present the page reference counting is done slightly
differently. No references are held by the network stack on the struct page (it
is up to the caller to manage this as necessary) instead the network stack will
track references via the count embedded in the destructor structure. When this
reference count reaches zero then the destructor will be called and the caller
can take the necesary steps to release the page (i.e. release the struct page
reference itself).
The intention is that callers can use this callback to delay completion to
_their_ callers until the network stack has completely released the page, in
order to prevent use-after-free or modification of data pages which are still
in use by the stack.
It is allowable (indeed expected) for a caller to share a single destructor
instance between multiple pages injected into the stack e.g. a group of pages
included in a single higher level operation might share a destructor which is
used to complete that higher level operation.
NB: a small number of drivers use skb_frag_t independently of struct sk_buff so
this patch is slightly larger than necessary. I did consider leaving skb_frag_t
alone and defining a new (but similar) structure to be used in the struct
sk_buff itself. This would also have the advantage of more clearly separating
the two uses, which is useful since there are now special reference counting
accessors for skb_frag_t within a struct sk_buff but not (necessarily) for
those used outside of an skb.
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
---
drivers/net/cxgb4/sge.c | 14 +++++++-------
drivers/net/cxgb4vf/sge.c | 18 +++++++++---------
drivers/net/mlx4/en_rx.c | 2 +-
drivers/scsi/cxgbi/libcxgbi.c | 2 +-
include/linux/skbuff.h | 31 ++++++++++++++++++++++++++-----
net/core/skbuff.c | 17 +++++++++++++++++
6 files changed, 61 insertions(+), 23 deletions(-)
diff --git a/drivers/net/cxgb4/sge.c b/drivers/net/cxgb4/sge.c
index f1813b5..3e7c4b3 100644
--- a/drivers/net/cxgb4/sge.c
+++ b/drivers/net/cxgb4/sge.c
@@ -1416,7 +1416,7 @@ static inline void copy_frags(struct sk_buff *skb,
unsigned int n;
/* usually there's just one frag */
- skb_frag_set_page(skb, 0, gl->frags[0].page);
+ skb_frag_set_page(skb, 0, gl->frags[0].page.p); /* XXX */
ssi->frags[0].page_offset = gl->frags[0].page_offset + offset;
ssi->frags[0].size = gl->frags[0].size - offset;
ssi->nr_frags = gl->nfrags;
@@ -1425,7 +1425,7 @@ static inline void copy_frags(struct sk_buff *skb,
memcpy(&ssi->frags[1], &gl->frags[1], n * sizeof(skb_frag_t));
/* get a reference to the last page, we don't own it */
- get_page(gl->frags[n].page);
+ get_page(gl->frags[n].page.p); /* XXX */
}
/**
@@ -1482,7 +1482,7 @@ static void t4_pktgl_free(const struct pkt_gl *gl)
const skb_frag_t *p;
for (p = gl->frags, n = gl->nfrags - 1; n--; p++)
- put_page(p->page);
+ put_page(p->page.p); /* XXX */
}
/*
@@ -1635,7 +1635,7 @@ static void restore_rx_bufs(const struct pkt_gl *si, struct sge_fl *q,
else
q->cidx--;
d = &q->sdesc[q->cidx];
- d->page = si->frags[frags].page;
+ d->page = si->frags[frags].page.p; /* XXX */
d->dma_addr |= RX_UNMAPPED_BUF;
q->avail++;
}
@@ -1717,7 +1717,7 @@ static int process_responses(struct sge_rspq *q, int budget)
for (frags = 0, fp = si.frags; ; frags++, fp++) {
rsd = &rxq->fl.sdesc[rxq->fl.cidx];
bufsz = get_buf_size(rsd);
- fp->page = rsd->page;
+ fp->page.p = rsd->page; /* XXX */
fp->page_offset = q->offset;
fp->size = min(bufsz, len);
len -= fp->size;
@@ -1734,8 +1734,8 @@ static int process_responses(struct sge_rspq *q, int budget)
get_buf_addr(rsd),
fp->size, DMA_FROM_DEVICE);
- si.va = page_address(si.frags[0].page) +
- si.frags[0].page_offset;
+ si.va = page_address(si.frags[0].page.p) +
+ si.frags[0].page_offset; /* XXX */
prefetch(si.va);
diff --git a/drivers/net/cxgb4vf/sge.c b/drivers/net/cxgb4vf/sge.c
index f4c4480..0a0dda1 100644
--- a/drivers/net/cxgb4vf/sge.c
+++ b/drivers/net/cxgb4vf/sge.c
@@ -1397,7 +1397,7 @@ struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl,
skb_copy_to_linear_data(skb, gl->va, pull_len);
ssi = skb_shinfo(skb);
- skb_frag_set_page(skb, 0, gl->frags[0].page);
+ skb_frag_set_page(skb, 0, gl->frags[0].page.p); /* XXX */
ssi->frags[0].page_offset = gl->frags[0].page_offset + pull_len;
ssi->frags[0].size = gl->frags[0].size - pull_len;
if (gl->nfrags > 1)
@@ -1410,7 +1410,7 @@ struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl,
skb->truesize += skb->data_len;
/* Get a reference for the last page, we don't own it */
- get_page(gl->frags[gl->nfrags - 1].page);
+ get_page(gl->frags[gl->nfrags - 1].page.p); /* XXX */
}
out:
@@ -1430,7 +1430,7 @@ void t4vf_pktgl_free(const struct pkt_gl *gl)
frag = gl->nfrags - 1;
while (frag--)
- put_page(gl->frags[frag].page);
+ put_page(gl->frags[frag].page.p); /* XXX */
}
/**
@@ -1450,7 +1450,7 @@ static inline void copy_frags(struct sk_buff *skb,
unsigned int n;
/* usually there's just one frag */
- skb_frag_set_page(skb, 0, gl->frags[0].page);
+ skb_frag_set_page(skb, 0, gl->frags[0].page.p); /* XXX */
si->frags[0].page_offset = gl->frags[0].page_offset + offset;
si->frags[0].size = gl->frags[0].size - offset;
si->nr_frags = gl->nfrags;
@@ -1460,7 +1460,7 @@ static inline void copy_frags(struct sk_buff *skb,
memcpy(&si->frags[1], &gl->frags[1], n * sizeof(skb_frag_t));
/* get a reference to the last page, we don't own it */
- get_page(gl->frags[n].page);
+ get_page(gl->frags[n].page.p); /* XXX */
}
/**
@@ -1633,7 +1633,7 @@ static void restore_rx_bufs(const struct pkt_gl *gl, struct sge_fl *fl,
else
fl->cidx--;
sdesc = &fl->sdesc[fl->cidx];
- sdesc->page = gl->frags[frags].page;
+ sdesc->page = gl->frags[frags].page.p; /* XXX */
sdesc->dma_addr |= RX_UNMAPPED_BUF;
fl->avail++;
}
@@ -1721,7 +1721,7 @@ int process_responses(struct sge_rspq *rspq, int budget)
BUG_ON(rxq->fl.avail == 0);
sdesc = &rxq->fl.sdesc[rxq->fl.cidx];
bufsz = get_buf_size(sdesc);
- fp->page = sdesc->page;
+ fp->page.p = sdesc->page; /* XXX */
fp->page_offset = rspq->offset;
fp->size = min(bufsz, len);
len -= fp->size;
@@ -1739,8 +1739,8 @@ int process_responses(struct sge_rspq *rspq, int budget)
dma_sync_single_for_cpu(rspq->adapter->pdev_dev,
get_buf_addr(sdesc),
fp->size, DMA_FROM_DEVICE);
- gl.va = (page_address(gl.frags[0].page) +
- gl.frags[0].page_offset);
+ gl.va = (page_address(gl.frags[0].page.p) +
+ gl.frags[0].page_offset); /* XXX */
prefetch(gl.va);
/*
diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c
index 21a89e0..c5d01ce 100644
--- a/drivers/net/mlx4/en_rx.c
+++ b/drivers/net/mlx4/en_rx.c
@@ -418,7 +418,7 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv,
break;
/* Save page reference in skb */
- __skb_frag_set_page(&skb_frags_rx[nr], skb_frags[nr].page);
+ __skb_frag_set_page(&skb_frags_rx[nr], skb_frags[nr].page.p); /* XXX */
skb_frags_rx[nr].size = skb_frags[nr].size;
skb_frags_rx[nr].page_offset = skb_frags[nr].page_offset;
dma = be64_to_cpu(rx_desc->data[nr].addr);
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index 949ee48..8d16a74 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -1823,7 +1823,7 @@ static int sgl_read_to_frags(struct scatterlist *sg, unsigned int sgoffset,
return -EINVAL;
}
- frags[i].page = page;
+ frags[i].page.p = page;
frags[i].page_offset = sg->offset + sgoffset;
frags[i].size = copy;
i++;
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 982c6a3..99b56e3 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -135,8 +135,17 @@ struct sk_buff;
typedef struct skb_frag_struct skb_frag_t;
+struct skb_frag_destructor {
+ atomic_t ref;
+ int (*destroy)(void *data);
+ void *data;
+};
+
struct skb_frag_struct {
- struct page *page;
+ struct {
+ struct page *p;
+ struct skb_frag_destructor *destructor;
+ } page;
#if (BITS_PER_LONG > 32) || (PAGE_SIZE >= 65536)
__u32 page_offset;
__u32 size;
@@ -1129,7 +1138,8 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
{
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- frag->page = page;
+ frag->page.p = page;
+ frag->page.destructor = NULL;
frag->page_offset = off;
frag->size = size;
}
@@ -1648,7 +1658,7 @@ static inline void netdev_free_page(struct net_device *dev, struct page *page)
*/
static inline struct page *__skb_frag_page(const skb_frag_t *frag)
{
- return frag->page;
+ return frag->page.p;
}
/**
@@ -1659,9 +1669,12 @@ static inline struct page *__skb_frag_page(const skb_frag_t *frag)
*/
static inline const struct page *skb_frag_page(const skb_frag_t *frag)
{
- return frag->page;
+ return frag->page.p;
}
+extern void skb_frag_destructor_ref(struct skb_frag_destructor *destroy);
+extern void skb_frag_destructor_unref(struct skb_frag_destructor *destroy);
+
/**
* __skb_frag_ref - take an addition reference on a paged fragment.
* @frag: the paged fragment
@@ -1670,6 +1683,10 @@ static inline const struct page *skb_frag_page(const skb_frag_t *frag)
*/
static inline void __skb_frag_ref(skb_frag_t *frag)
{
+ if (unlikely(frag->page.destructor)) {
+ skb_frag_destructor_ref(frag->page.destructor);
+ return;
+ }
get_page(__skb_frag_page(frag));
}
@@ -1693,6 +1710,10 @@ static inline void skb_frag_ref(struct sk_buff *skb, int f)
*/
static inline void __skb_frag_unref(skb_frag_t *frag)
{
+ if (unlikely(frag->page.destructor)) {
+ skb_frag_destructor_unref(frag->page.destructor);
+ return;
+ }
put_page(__skb_frag_page(frag));
}
@@ -1745,7 +1766,7 @@ static inline void *skb_frag_address_safe(const skb_frag_t *frag)
*/
static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page)
{
- frag->page = page;
+ frag->page.p = page;
__skb_frag_ref(frag);
}
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 2133600..bdc6f6e 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -292,6 +292,23 @@ struct sk_buff *dev_alloc_skb(unsigned int length)
}
EXPORT_SYMBOL(dev_alloc_skb);
+void skb_frag_destructor_ref(struct skb_frag_destructor *destroy)
+{
+ BUG_ON(destroy == NULL);
+ atomic_inc(&destroy->ref);
+}
+EXPORT_SYMBOL(skb_frag_destructor_ref);
+
+void skb_frag_destructor_unref(struct skb_frag_destructor *destroy)
+{
+ if (destroy == NULL)
+ return;
+
+ if (atomic_dec_and_test(&destroy->ref))
+ destroy->destroy(destroy->data);
+}
+EXPORT_SYMBOL(skb_frag_destructor_unref);
+
static void skb_drop_list(struct sk_buff **listp)
{
struct sk_buff *list = *listp;
--
1.7.2.5
^ permalink raw reply related
* [PATCH 10/10] nfs: debugging for nfs destructor
From: Ian Campbell @ 2011-07-15 11:07 UTC (permalink / raw)
To: netdev; +Cc: linux-nfs, Ian Campbell
In-Reply-To: <1310728006.20648.3.camel@zakaz.uk.xensource.com>
---
fs/nfs/direct.c | 57 +++++++++++++++++++++++++++++++++++++++++++-
fs/nfs/nfs3xdr.c | 2 +
include/linux/mm.h | 4 +++
include/linux/page-flags.h | 2 +
mm/swap.c | 6 ++++
net/core/skbuff.c | 14 ++++++++++-
net/sunrpc/svcsock.c | 7 +++++
net/sunrpc/xdr.c | 12 ++++++++-
net/sunrpc/xprtsock.c | 8 ++++++
9 files changed, 109 insertions(+), 3 deletions(-)
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 4735fd9..9512f75 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -144,8 +144,12 @@ static void nfs_direct_dirty_pages(struct page **pages, unsigned int pgbase, siz
static void nfs_direct_release_pages(struct page **pages, unsigned int npages)
{
unsigned int i;
- for (i = 0; i < npages; i++)
+ for (i = 0; i < npages; i++) {
+ if (0) printk(KERN_CRIT "%s releasing %p (clearing debug)\n",
+ __func__, pages[i]);
+ ClearPageDebug(pages[i]);
page_cache_release(pages[i]);
+ }
}
static inline struct nfs_direct_req *nfs_direct_req_alloc(void)
@@ -216,6 +220,8 @@ out:
*/
static void nfs_direct_complete(struct nfs_direct_req *dreq)
{
+ if (0) printk(KERN_CRIT "%s for dreq:%p count:%d\n",
+ __func__, dreq, atomic_read(&dreq->io_count));
if (dreq->iocb) {
long res = (long) dreq->error;
if (!res)
@@ -521,6 +527,8 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
(unsigned long long)data->args.offset);
}
+ if (0) printk(KERN_CRIT "%s for w:%p dreq:%p count:%d\n", __func__,
+ data, dreq, atomic_read(&dreq->io_count));
if (put_dreq(dreq))
nfs_direct_write_complete(dreq, inode);
}
@@ -549,6 +557,8 @@ static void nfs_direct_commit_release(void *calldata)
}
dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status);
+ if (0) printk(KERN_CRIT "%s for w:%p dreq:%p count:%d\n", __func__,
+ data, dreq, atomic_read(&dreq->io_count));
nfs_direct_write_complete(dreq, data->inode);
nfs_commit_free(data);
}
@@ -609,20 +619,25 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
{
int flags = dreq->flags;
+ if (0) printk(KERN_CRIT "%s for dreq:%p\n", __func__, dreq);
dreq->flags = 0;
switch (flags) {
case NFS_ODIRECT_DO_COMMIT:
+ if (0) printk(KERN_CRIT "%s: DO_COMMIT\n", __func__);
nfs_direct_commit_schedule(dreq);
break;
case NFS_ODIRECT_RESCHED_WRITES:
+ if (0) printk(KERN_CRIT "%s: RESCHED\n", __func__);
nfs_direct_write_reschedule(dreq);
break;
default:
+ if (0) printk(KERN_CRIT "%s: DONE\n", __func__);
if (dreq->commit_data != NULL)
nfs_commit_free(dreq->commit_data);
nfs_direct_free_writedata(dreq);
nfs_zap_mapping(inode, inode->i_mapping);
nfs_direct_complete(dreq);
+ //set_all_pages_debug(data->pagevec, data->npages);
}
}
@@ -661,6 +676,7 @@ static void nfs_direct_write_release(void *calldata)
{
struct nfs_write_data *data = calldata;
struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+
int status = data->task.tk_status;
spin_lock(&dreq->lock);
@@ -691,6 +707,8 @@ static void nfs_direct_write_release(void *calldata)
out_unlock:
spin_unlock(&dreq->lock);
+ if (0) printk(KERN_CRIT "%s for w:%p dreq:%p count:%d\n", __func__,
+ data, dreq, atomic_read(&dreq->io_count));
skb_frag_destructor_unref(data->args.pages_destructor);
}
@@ -706,11 +724,29 @@ static int nfs_write_page_destroy(void *calldata)
{
struct nfs_write_data *data = calldata;
struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+ //int i;
+ if (0) printk(KERN_CRIT "%s for w:%p dreq:%p count:%d\n", __func__,
+ data, dreq,
+ atomic_read(&dreq->io_count));
+ //for (i = 0; i < data->npages; i++)
+ // put_page(data->pagevec[i]);
+
if (put_dreq(dreq))
nfs_direct_write_complete(dreq, data->inode);
return 0;
}
+static void set_all_pages_debug(struct page **pages, int npages)
+{
+ int i;
+ return;
+ for (i=0; i<npages; i++) {
+ if (0) printk(KERN_CRIT "Marking page %p as debug current count:%d\n",
+ pages[i],page_count(pages[i]));
+ SetPageDebug(pages[i]);
+ }
+}
+
/*
* For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
* operation. If nfs_writedata_alloc() or get_user_pages() fails,
@@ -754,6 +790,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
if (unlikely(!data))
break;
+ if (0) printk(KERN_CRIT "%s: getting user pages\n", __func__);
down_read(¤t->mm->mmap_sem);
result = get_user_pages(current, current->mm, user_addr,
data->npages, 0, 0, data->pagevec, NULL);
@@ -773,6 +810,8 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->npages = result;
}
+ set_all_pages_debug(data->pagevec, data->npages);
+
get_dreq(dreq);
list_move_tail(&data->pages, &dreq->rewrite_list);
@@ -790,7 +829,11 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->args.offset = pos;
data->args.pgbase = pgbase;
data->args.pages = data->pagevec;
+#if 1
data->args.pages_destructor = &data->pagevec_destructor;
+#else
+ data->args.pages_destructor = NULL;
+#endif
data->args.count = bytes;
data->args.stable = sync;
data->res.fattr = &data->fattr;
@@ -804,6 +847,16 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
msg.rpc_resp = &data->res;
NFS_PROTO(inode)->write_setup(data, &msg);
+ if (0) printk(KERN_CRIT "%s scheduling w:%p dreq:%p count:%d. args %p pages:%d dest:%p %pf\n",
+ __func__, data, dreq,
+ atomic_read(&dreq->io_count),
+ &data->args, data->npages,
+ data->args.pages_destructor,
+ data->args.pages_destructor->destroy);
+ if (0) printk(KERN_CRIT "%s page[0] is %p count:%d\n",
+ __func__, data->pagevec[0],
+ page_count(data->pagevec[0]));
+
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
break;
@@ -866,6 +919,8 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
return result < 0 ? result : -EIO;
}
+ if (0) printk(KERN_CRIT "%s for dreq:%p count:%d\n", __func__,
+ dreq, atomic_read(&dreq->io_count));
if (put_dreq(dreq))
nfs_direct_write_complete(dreq, dreq->inode);
return 0;
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index f7a83a1..2137550 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -994,6 +994,8 @@ static void encode_write3args(struct xdr_stream *xdr,
*p++ = cpu_to_be32(args->count);
*p++ = cpu_to_be32(args->stable);
*p = cpu_to_be32(args->count);
+ if (0) printk(KERN_CRIT "%s xdr %p args %p destructor %pF\n",
+ __func__, xdr, args, args->pages_destructor->destroy);
xdr_write_pages(xdr, args->pages, args->pages_destructor,
args->pgbase, args->count);
}
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 550ec8f..f992dfa 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -362,6 +362,10 @@ static inline int page_count(struct page *page)
static inline void get_page(struct page *page)
{
+ if (PageDebug(page)) printk(KERN_CRIT "%s(%p) from %pF count %d\n",
+ __func__, page,
+ __builtin_return_address(0),
+ page_count(page));
/*
* Getting a normal page or the head of a compound page
* requires to already have an elevated page->_count. Only if
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 7d632cc..8434345 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -107,6 +107,7 @@ enum pageflags {
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
PG_compound_lock,
#endif
+ PG_debug,
__NR_PAGEFLAGS,
/* Filesystems */
@@ -209,6 +210,7 @@ PAGEFLAG(Pinned, pinned) TESTSCFLAG(Pinned, pinned) /* Xen */
PAGEFLAG(SavePinned, savepinned); /* Xen */
PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved)
PAGEFLAG(SwapBacked, swapbacked) __CLEARPAGEFLAG(SwapBacked, swapbacked)
+PAGEFLAG(Debug, debug)
__PAGEFLAG(SlobFree, slob_free)
diff --git a/mm/swap.c b/mm/swap.c
index 3a442f1..4450105 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -61,6 +61,8 @@ static void __page_cache_release(struct page *page)
static void __put_single_page(struct page *page)
{
+ if (PageDebug(page)) printk(KERN_CRIT "%s(%p) from %pF\n", __func__, page, __builtin_return_address(0));
+ ClearPageDebug(page);
__page_cache_release(page);
free_hot_cold_page(page, 0);
}
@@ -153,6 +155,10 @@ static void put_compound_page(struct page *page)
void put_page(struct page *page)
{
+ if (PageDebug(page)) printk(KERN_CRIT "%s(%p) from %pF count %d\n",
+ __func__, page,
+ __builtin_return_address(0),
+ page_count(page));
if (unlikely(PageCompound(page)))
put_compound_page(page);
else if (put_page_testzero(page))
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index bdc6f6e..c3fdfb7 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -296,6 +296,9 @@ void skb_frag_destructor_ref(struct skb_frag_destructor *destroy)
{
BUG_ON(destroy == NULL);
atomic_inc(&destroy->ref);
+ if (0) printk(KERN_CRIT "%s from %pF: %d\n",
+ __func__, __builtin_return_address(0),
+ atomic_read(&destroy->ref));
}
EXPORT_SYMBOL(skb_frag_destructor_ref);
@@ -304,8 +307,17 @@ void skb_frag_destructor_unref(struct skb_frag_destructor *destroy)
if (destroy == NULL)
return;
- if (atomic_dec_and_test(&destroy->ref))
+ if (atomic_dec_and_test(&destroy->ref)) {
+ if (0) printk(KERN_CRIT "%s from %pF: calling destructor %p %pf(%p)\n",
+ __func__, __builtin_return_address(0),
+ destroy, destroy->destroy, destroy->data);
destroy->destroy(destroy->data);
+ } else {
+ if (0) printk(KERN_CRIT "%s from %pF: destructor %p %pf(%p) not called %d remaining\n",
+ __func__, __builtin_return_address(0),
+ destroy, destroy->destroy, destroy->data,
+ atomic_read(&destroy->ref));
+ }
}
EXPORT_SYMBOL(skb_frag_destructor_unref);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 40c2420..64ce9907 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -176,6 +176,10 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
int slen;
int len = 0;
+ if (xdr->destructor)
+ if (0) printk(KERN_CRIT "%s sending xdr %p with destructor %pF\n",
+ __func__, xdr, xdr->destructor->destroy);
+
slen = xdr->len;
/* send head */
@@ -194,6 +198,9 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
while (pglen > 0) {
if (slen == size)
flags = 0;
+ if (xdr->destructor)
+ if (0) printk(KERN_CRIT "%s sending xdr %p page %p with destructor %pF\n",
+ __func__, xdr, *ppage, xdr->destructor);
result = kernel_sendpage(sock, *ppage, xdr->destructor, base, size, flags);
if (result > 0)
len += result;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 9c7dded..fcaea6a 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -135,6 +135,10 @@ xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
struct kvec *tail = xdr->tail;
u32 *p;
+ if (xdr->destructor)
+ if (0) printk(KERN_CRIT "%s xdr %p with destructor %p\n",
+ __func__, xdr, xdr->destructor);
+
xdr->pages = pages;
xdr->page_base = base;
xdr->page_len = len;
@@ -165,6 +169,11 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
char *buf = (char *)head->iov_base;
unsigned int buflen = head->iov_len;
+
+ if (xdr->destructor)
+ if (0) printk(KERN_CRIT "%s xdr %p with destructor %p\n",
+ __func__, xdr, xdr->destructor);
+
head->iov_len = offset;
xdr->pages = pages;
@@ -535,7 +544,8 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
buf->destructor = destroy;
buf->page_base = base;
buf->page_len = len;
-
+ if (destroy)
+ if (0) printk(KERN_CRIT "%s xdr %p %p destructor %p\n", __func__, xdr, buf, destroy);
iov->iov_base = (char *)xdr->p;
iov->iov_len = 0;
xdr->iov = iov;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index aa31294..336d787 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -386,6 +386,10 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
unsigned int remainder;
int err, sent = 0;
+ if (xdr->destructor)
+ if (0) printk(KERN_CRIT "%s sending xdr %p with destructor %pF\n",
+ __func__, xdr, xdr->destructor->destroy);
+
remainder = xdr->page_len - base;
base += xdr->page_base;
ppage = xdr->pages + (base >> PAGE_SHIFT);
@@ -425,6 +429,10 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen,
unsigned int remainder = xdr->len - base;
int err, sent = 0;
+ if (xdr->destructor)
+ if (0) printk(KERN_CRIT "%s sending xdr %p with destructor %pF\n",
+ __func__, xdr, xdr->destructor->destroy);
+
if (unlikely(!sock))
return -ENOTSOCK;
--
1.7.2.5
^ permalink raw reply related
* [PATCH 01/10] mm: Make some struct page's const.
From: Ian Campbell @ 2011-07-15 11:07 UTC (permalink / raw)
To: netdev; +Cc: linux-nfs, Ian Campbell
In-Reply-To: <1310728006.20648.3.camel@zakaz.uk.xensource.com>
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
---
include/linux/mm.h | 10 +++++-----
mm/sparse.c | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 9670f71..550ec8f 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -636,7 +636,7 @@ static inline pte_t maybe_mkwrite(pte_t pte, struct vm_area_struct *vma)
#define SECTIONS_MASK ((1UL << SECTIONS_WIDTH) - 1)
#define ZONEID_MASK ((1UL << ZONEID_SHIFT) - 1)
-static inline enum zone_type page_zonenum(struct page *page)
+static inline enum zone_type page_zonenum(const struct page *page)
{
return (page->flags >> ZONES_PGSHIFT) & ZONES_MASK;
}
@@ -664,15 +664,15 @@ static inline int zone_to_nid(struct zone *zone)
}
#ifdef NODE_NOT_IN_PAGE_FLAGS
-extern int page_to_nid(struct page *page);
+extern int page_to_nid(const struct page *page);
#else
-static inline int page_to_nid(struct page *page)
+static inline int page_to_nid(const struct page *page)
{
return (page->flags >> NODES_PGSHIFT) & NODES_MASK;
}
#endif
-static inline struct zone *page_zone(struct page *page)
+static inline struct zone *page_zone(const struct page *page)
{
return &NODE_DATA(page_to_nid(page))->node_zones[page_zonenum(page)];
}
@@ -717,7 +717,7 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
*/
#include <linux/vmstat.h>
-static __always_inline void *lowmem_page_address(struct page *page)
+static __always_inline void *lowmem_page_address(const struct page *page)
{
return __va(PFN_PHYS(page_to_pfn(page)));
}
diff --git a/mm/sparse.c b/mm/sparse.c
index aa64b12..858e1df 100644
--- a/mm/sparse.c
+++ b/mm/sparse.c
@@ -40,7 +40,7 @@ static u8 section_to_node_table[NR_MEM_SECTIONS] __cacheline_aligned;
static u16 section_to_node_table[NR_MEM_SECTIONS] __cacheline_aligned;
#endif
-int page_to_nid(struct page *page)
+int page_to_nid(const struct page *page)
{
return section_to_node_table[page_to_section(page)];
}
--
1.7.2.5
^ permalink raw reply related
* [PATCH 03/10] net: add APIs for manipulating skb page fragments.
From: Ian Campbell @ 2011-07-15 11:07 UTC (permalink / raw)
To: netdev; +Cc: linux-nfs, Ian Campbell
In-Reply-To: <1310728006.20648.3.camel@zakaz.uk.xensource.com>
The primary aim is to add skb_frag_(ref|unref) in order to remove the use of
bare get/put_page on SKB pages fragments and to isolate users from subsequent
changes to the skb_frag_t data structure.
The API also includes an accessor for the struct page itself. The default
variant of this returns a *const* struct page in an attempt to catch bare uses
of get/put_page (which take a non-const struct page).
Also included are helper APIs for passing a paged fragment to kmap and
(pci|dma)_map_page since I was seeing the same pattern a lot.
Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
---
include/linux/skbuff.h | 225 +++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 223 insertions(+), 2 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index c0a4f3a..c061257 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -29,6 +29,8 @@
#include <linux/rcupdate.h>
#include <linux/dmaengine.h>
#include <linux/hrtimer.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
/* Don't change this without changing skb_csum_unnecessary! */
#define CHECKSUM_NONE 0
@@ -1109,14 +1111,47 @@ static inline int skb_pagelen(const struct sk_buff *skb)
return len + skb_headlen(skb);
}
-static inline void skb_fill_page_desc(struct sk_buff *skb, int i,
- struct page *page, int off, int size)
+/**
+ * __skb_fill_page_desc - initialise a paged fragment in an skb
+ * @skb: buffer containing fragment to be initialised
+ * @i: paged fragment index to initialise
+ * @page: the page to use for this fragment
+ * @off: the offset to the data with @page
+ * @size: the length of the data
+ *
+ * Initialises the @i'th fragment of @skb to point to &size bytes at
+ * offset @off within @page.
+ *
+ * Does not take any additional reference on the fragment.
+ */
+static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
+ struct page *page, int off, int size)
{
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
frag->page = page;
frag->page_offset = off;
frag->size = size;
+}
+
+/**
+ * skb_fill_page_desc - initialise a paged fragment in an skb
+ * @skb: buffer containing fragment to be initialised
+ * @i: paged fragment index to initialise
+ * @page: the page to use for this fragment
+ * @off: the offset to the data with @page
+ * @size: the length of the data
+ *
+ * As per __skb_fill_page_desc() -- initialises the @i'th fragment of
+ * @skb to point to &size bytes at offset @off within @page. In
+ * addition updates @skb such that @i is the last fragment.
+ *
+ * Does not take any additional reference on the fragment.
+ */
+static inline void skb_fill_page_desc(struct sk_buff *skb, int i,
+ struct page *page, int off, int size)
+{
+ __skb_fill_page_desc(skb, i, page, off, size);
skb_shinfo(skb)->nr_frags = i + 1;
}
@@ -1605,6 +1640,192 @@ static inline void netdev_free_page(struct net_device *dev, struct page *page)
}
/**
+ * __skb_frag_page - retrieve the page refered to by a paged fragment
+ * @frag: the paged fragment
+ *
+ * Returns the &struct page associated with @frag. Where possible you
+ * should use skb_frag_page() which returns a const &struct page.
+ */
+static inline struct page *__skb_frag_page(const skb_frag_t *frag)
+{
+ return frag->page;
+}
+
+/**
+ * __skb_frag_page - retrieve the page refered to by a paged fragment
+ * @frag: the paged fragment
+ *
+ * Returns the &struct page associated with @frag as a const.
+ */
+static inline const struct page *skb_frag_page(const skb_frag_t *frag)
+{
+ return frag->page;
+}
+
+/**
+ * __skb_frag_ref - take an addition reference on a paged fragment.
+ * @frag: the paged fragment
+ *
+ * Takes an additional reference on the paged fragment @frag.
+ */
+static inline void __skb_frag_ref(skb_frag_t *frag)
+{
+ get_page(__skb_frag_page(frag));
+}
+
+/**
+ * skb_frag_ref - take an addition reference on a paged fragment of an skb.
+ * @skb: the buffer
+ * @f: the fragment offset.
+ *
+ * Takes an additional reference on the @f'th paged fragment of @skb.
+ */
+static inline void skb_frag_ref(struct sk_buff *skb, int f)
+{
+ __skb_frag_ref(&skb_shinfo(skb)->frags[f]);
+}
+
+/**
+ * __skb_frag_unref - release a reference on a paged fragment.
+ * @frag: the paged fragment
+ *
+ * Releases a reference on the paged fragment @frag.
+ */
+static inline void __skb_frag_unref(skb_frag_t *frag)
+{
+ put_page(__skb_frag_page(frag));
+}
+
+/**
+ * skb_frag_unref - release a reference on a paged fragment of an skb.
+ * @skb: the buffer
+ * @f: the fragment offset
+ *
+ * Releases a reference on the @f'th paged fragment of @skb.
+ */
+static inline void skb_frag_unref(struct sk_buff *skb, int f)
+{
+ __skb_frag_unref(&skb_shinfo(skb)->frags[f]);
+}
+
+/**
+ * skb_frag_address - gets the address of the data contained in a paged fragment
+ * @frag: the paged fragment buffer
+ *
+ * Returns the address of the data within @frag. The page must already
+ * be mapped.
+ */
+static inline void *skb_frag_address(const skb_frag_t *frag)
+{
+ return page_address(skb_frag_page(frag)) + frag->page_offset;
+}
+
+/**
+ * skb_frag_address_safe - gets the address of the data contained in a paged fragment
+ * @frag: the paged fragment buffer
+ *
+ * Returns the address of the data within @frag. Checks that the page
+ * is mapped and returns %NULL otherwise.
+ */
+static inline void *skb_frag_address_safe(const skb_frag_t *frag)
+{
+ void *ptr = page_address(skb_frag_page(frag));
+ if (unlikely(!ptr))
+ return NULL;
+
+ return ptr + frag->page_offset;
+}
+
+/**
+ * __skb_frag_set_page - sets the page contained in a paged fragment
+ * @frag: the paged fragment
+ * @page: the page to set
+ *
+ * Sets the fragment @frag to contain @page.
+ */
+static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page)
+{
+ frag->page = page;
+ __skb_frag_ref(frag);
+}
+
+/**
+ * skb_frag_set_page - sets the page contained in a paged fragment of an skb
+ * @skb: the buffer
+ * @f: the fragment offset
+ * @page: the page to set
+ *
+ * Sets the @f'th fragment of @skb to contain @page.
+ */
+static inline void skb_frag_set_page(struct sk_buff *skb, int f,
+ struct page *page)
+{
+ __skb_frag_set_page(&skb_shinfo(skb)->frags[f], page);
+}
+
+/**
+ * skb_frag_kmap - kmaps a paged fragment
+ * @frag: the paged fragment
+ *
+ * kmap()s the paged fragment @frag and returns the virtual address.
+ */
+static inline void *skb_frag_kmap(skb_frag_t *frag)
+{
+ return kmap(__skb_frag_page(frag));
+}
+
+/**
+ * skb_frag_kmap - kunmaps a paged fragment
+ * @frag: the paged fragment
+ *
+ * kunmap()s the paged fragment @frag.
+ */
+static inline void skb_frag_kunmap(skb_frag_t *frag)
+{
+ kunmap(__skb_frag_page(frag));
+}
+
+/**
+ * skb_frag_pci_map - maps a paged fragment to a PCI device
+ * @hwdev: the PCI device to map the fragment to
+ * @frag: the paged fragment to map
+ * @offset: the offset within the fragment (starting at the fragments own offset)
+ * @size: the number of bytes to map
+ * @direction: the direction of the mapping (%PCI_DMA_*)
+ *
+ * Maps the page associated with @frag to the PCI device @hwdev.
+ */
+static inline dma_addr_t skb_frag_pci_map(struct pci_dev *hwdev,
+ const skb_frag_t *frag,
+ unsigned long offset,
+ size_t size,
+ int direction)
+
+{
+ return pci_map_page(hwdev, __skb_frag_page(frag),
+ frag->page_offset + offset, size, direction);
+}
+
+/**
+ * skb_frag_dma_map - maps a paged fragment via the DMA API
+ * @device: the device to map the fragment to
+ * @frag: the paged fragment to map
+ * @offset: the offset within the fragment (starting at the fragments own offset)
+ * @size: the number of bytes to map
+ * @direction: the direction of the mapping (%PCI_DMA_*)
+ *
+ * Maps the page associated with @frag to @device.
+ */
+static inline dma_addr_t skb_frag_dma_map(struct device *dev,
+ const skb_frag_t *frag,
+ size_t offset, size_t size,
+ enum dma_data_direction dir)
+{
+ return dma_map_page(dev, __skb_frag_page(frag),
+ frag->page_offset + offset, size, dir);
+}
+
+/**
* skb_clone_writable - is the header of a clone writable
* @skb: buffer to check
* @len: length up to which to write
--
1.7.2.5
^ permalink raw reply related
* [PATCH] gianfar: rx parser
From: Sebastian Pöhn @ 2011-07-15 11:09 UTC (permalink / raw)
To: Linux Netdev, jpirko; +Cc: sandeep.kumar
Only let the rx parser be enabled if it is necessary (if VLAN extracrtion, IP or TCP checksumming or the rx queue filer are enabled). Otherwise disable it.
The new routine gfar_check_rx_parser_mode should be runt after every change on this features and will enable/disable the parser as necessary.
I will send a patch for the rx queue filer the next days.
Signed-off-by: Sebastian Poehn <sebastian.poehn@belden.com>
---
drivers/net/gianfar.c | 25 ++++++++++++++++++++-----
drivers/net/gianfar.h | 3 ++-
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c
index 3321d71..6da3712 100644
--- a/drivers/net/gianfar.c
+++ b/drivers/net/gianfar.c
@@ -2287,6 +2287,23 @@ static int gfar_set_mac_address(struct net_device *dev)
return 0;
}
+/* Check if rx parser should be activated */
+void gfar_check_rx_parser_mode(struct gfar_private *priv)
+{
+ struct gfar __iomem *regs = NULL;
+ u32 tempval = 0;
+
+ regs = priv->gfargrp[0].regs;
+
+ tempval = gfar_read(®s->rctrl);
+ /* If parse is no longer required, then disable parser */
+ if (tempval & RCTRL_REQ_PARSER)
+ tempval |= RCTRL_PRSDEP_INIT;
+ else
+ tempval &= ~RCTRL_PRSDEP_INIT;
+ gfar_write(®s->rctrl, tempval);
+}
+
/* Enables and disables VLAN insertion/extraction */
static void gfar_vlan_rx_register(struct net_device *dev,
@@ -2323,12 +2340,10 @@ static void gfar_vlan_rx_register(struct net_device *dev,
/* Disable VLAN tag extraction */
tempval = gfar_read(®s->rctrl);
tempval &= ~RCTRL_VLEX;
- /* If parse is no longer required, then disable parser */
- if (tempval & RCTRL_REQ_PARSER)
- tempval |= RCTRL_PRSDEP_INIT;
- else
- tempval &= ~RCTRL_PRSDEP_INIT;
gfar_write(®s->rctrl, tempval);
+
+ gfar_check_rx_parser_mode(priv);
+
}
gfar_change_mtu(dev, dev->mtu);
diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h
index 27499c6..87c1d86 100644
--- a/drivers/net/gianfar.h
+++ b/drivers/net/gianfar.h
@@ -286,7 +286,7 @@ extern const char gfar_driver_version[];
#define RCTRL_PROM 0x00000008
#define RCTRL_EMEN 0x00000002
#define RCTRL_REQ_PARSER (RCTRL_VLEX | RCTRL_IPCSEN | \
- RCTRL_TUCSEN)
+ RCTRL_TUCSEN | RCTRL_FILREN)
#define RCTRL_CHECKSUMMING (RCTRL_IPCSEN | RCTRL_TUCSEN | \
RCTRL_PRSDEP_INIT)
#define RCTRL_EXTHASH (RCTRL_GHTX)
@@ -1182,6 +1182,7 @@ extern void gfar_configure_coalescing(struct gfar_private *priv,
unsigned long tx_mask, unsigned long rx_mask);
void gfar_init_sysfs(struct net_device *dev);
int gfar_set_features(struct net_device *dev, u32 features);
+extern void gfar_check_rx_parser_mode(struct gfar_private *priv);
extern const struct ethtool_ops gfar_ethtool_ops;
^ permalink raw reply related
* [PATCH 09/10] nfs: use sk fragment destructors to delay I/O completion until page is released by network stack.
From: Ian Campbell @ 2011-07-15 11:07 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: linux-nfs-u79uwXL29TY76Z2rM5mHXA, Ian Campbell
In-Reply-To: <1310728006.20648.3.camel-o4Be2W7LfRlXesXXhkcM7miJhflN2719@public.gmane.org>
Thos prevents an issue where an ACK is delayed, a retransmit is queued (either
at the RPC or TCP level) and the ACK arrives before the retransmission hits the
wire. If this happens then the write() system call and the userspace process
can continue potentially modifying the data before the retransmission occurs.
NB: this only covers the O_DIRECT write() case. I expect other cases to need
handling as well.
Signed-off-by: Ian Campbell <ian.campbell-Sxgqhf6Nn4DQT0dZR+AlfA@public.gmane.org>
---
fs/nfs/direct.c | 17 +++++++++++++++--
fs/nfs/nfs2xdr.c | 4 ++--
fs/nfs/nfs3xdr.c | 7 ++++---
fs/nfs/nfs4xdr.c | 6 +++---
include/linux/nfs_xdr.h | 4 ++++
include/linux/sunrpc/xdr.h | 2 ++
net/sunrpc/svcsock.c | 2 +-
net/sunrpc/xdr.c | 4 +++-
net/sunrpc/xprtsock.c | 2 +-
9 files changed, 35 insertions(+), 13 deletions(-)
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 8eea253..4735fd9 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -691,8 +691,7 @@ static void nfs_direct_write_release(void *calldata)
out_unlock:
spin_unlock(&dreq->lock);
- if (put_dreq(dreq))
- nfs_direct_write_complete(dreq, data->inode);
+ skb_frag_destructor_unref(data->args.pages_destructor);
}
static const struct rpc_call_ops nfs_write_direct_ops = {
@@ -703,6 +702,15 @@ static const struct rpc_call_ops nfs_write_direct_ops = {
.rpc_release = nfs_direct_write_release,
};
+static int nfs_write_page_destroy(void *calldata)
+{
+ struct nfs_write_data *data = calldata;
+ struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
+ if (put_dreq(dreq))
+ nfs_direct_write_complete(dreq, data->inode);
+ return 0;
+}
+
/*
* For each wsize'd chunk of the user's buffer, dispatch an NFS WRITE
* operation. If nfs_writedata_alloc() or get_user_pages() fails,
@@ -769,6 +777,10 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
list_move_tail(&data->pages, &dreq->rewrite_list);
+ atomic_set(&data->pagevec_destructor.ref, 1);
+ data->pagevec_destructor.destroy = nfs_write_page_destroy;
+ data->pagevec_destructor.data = data;
+
data->req = (struct nfs_page *) dreq;
data->inode = inode;
data->cred = msg.rpc_cred;
@@ -778,6 +790,7 @@ static ssize_t nfs_direct_write_schedule_segment(struct nfs_direct_req *dreq,
data->args.offset = pos;
data->args.pgbase = pgbase;
data->args.pages = data->pagevec;
+ data->args.pages_destructor = &data->pagevec_destructor;
data->args.count = bytes;
data->args.stable = sync;
data->res.fattr = &data->fattr;
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 792cb13..6dc77f0 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -431,7 +431,7 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length)
BUG_ON(length > NFS2_MAXPATHLEN);
p = xdr_reserve_space(xdr, 4);
*p = cpu_to_be32(length);
- xdr_write_pages(xdr, pages, 0, length);
+ xdr_write_pages(xdr, pages, NULL, 0, length);
}
static int decode_path(struct xdr_stream *xdr)
@@ -659,7 +659,7 @@ static void encode_writeargs(struct xdr_stream *xdr,
/* nfsdata */
*p = cpu_to_be32(count);
- xdr_write_pages(xdr, args->pages, args->pgbase, count);
+ xdr_write_pages(xdr, args->pages, NULL, args->pgbase, count);
}
static void nfs2_xdr_enc_writeargs(struct rpc_rqst *req,
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 183c6b1..f7a83a1 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -238,7 +238,7 @@ static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages,
{
BUG_ON(length > NFS3_MAXPATHLEN);
encode_uint32(xdr, length);
- xdr_write_pages(xdr, pages, 0, length);
+ xdr_write_pages(xdr, pages, NULL, 0, length);
}
static int decode_nfspath3(struct xdr_stream *xdr)
@@ -994,7 +994,8 @@ static void encode_write3args(struct xdr_stream *xdr,
*p++ = cpu_to_be32(args->count);
*p++ = cpu_to_be32(args->stable);
*p = cpu_to_be32(args->count);
- xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
+ xdr_write_pages(xdr, args->pages, args->pages_destructor,
+ args->pgbase, args->count);
}
static void nfs3_xdr_enc_write3args(struct rpc_rqst *req,
@@ -1331,7 +1332,7 @@ static void nfs3_xdr_enc_setacl3args(struct rpc_rqst *req,
base = req->rq_slen;
if (args->npages != 0)
- xdr_write_pages(xdr, args->pages, 0, args->len);
+ xdr_write_pages(xdr, args->pages, NULL, 0, args->len);
else
xdr_reserve_space(xdr, NFS_ACL_INLINE_BUFSIZE);
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 6870bc6..ac9931d 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1031,7 +1031,7 @@ static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *
case NF4LNK:
p = reserve_space(xdr, 4);
*p = cpu_to_be32(create->u.symlink.len);
- xdr_write_pages(xdr, create->u.symlink.pages, 0, create->u.symlink.len);
+ xdr_write_pages(xdr, create->u.symlink.pages, NULL, 0, create->u.symlink.len);
break;
case NF4BLK: case NF4CHR:
@@ -1573,7 +1573,7 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
BUG_ON(arg->acl_len % 4);
p = reserve_space(xdr, 4);
*p = cpu_to_be32(arg->acl_len);
- xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
+ xdr_write_pages(xdr, arg->acl_pages, NULL, arg->acl_pgbase, arg->acl_len);
hdr->nops++;
hdr->replen += decode_setacl_maxsz;
}
@@ -1647,7 +1647,7 @@ static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *arg
*p++ = cpu_to_be32(args->stable);
*p = cpu_to_be32(args->count);
- xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
+ xdr_write_pages(xdr, args->pages, NULL, args->pgbase, args->count);
hdr->nops++;
hdr->replen += decode_write_maxsz;
}
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 00848d8..4bbc2bf 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -5,6 +5,8 @@
#include <linux/nfs3.h>
#include <linux/sunrpc/gss_api.h>
+#include <linux/skbuff.h>
+
/*
* To change the maximum rsize and wsize supported by the NFS client, adjust
* NFS_MAX_FILE_IO_SIZE. 64KB is a typical maximum, but some servers can
@@ -470,6 +472,7 @@ struct nfs_writeargs {
enum nfs3_stable_how stable;
unsigned int pgbase;
struct page ** pages;
+ struct skb_frag_destructor *pages_destructor;
const u32 * bitmask;
struct nfs4_sequence_args seq_args;
};
@@ -1121,6 +1124,7 @@ struct nfs_write_data {
struct list_head pages; /* Coalesced requests we wish to flush */
struct nfs_page *req; /* multi ops per nfs_page */
struct page **pagevec;
+ struct skb_frag_destructor pagevec_destructor;
unsigned int npages; /* Max length of pagevec */
struct nfs_writeargs args; /* argument struct */
struct nfs_writeres res; /* result struct */
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index a20970e..cebb531 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -57,6 +57,7 @@ struct xdr_buf {
tail[1]; /* Appended after page data */
struct page ** pages; /* Array of contiguous pages */
+ struct skb_frag_destructor *destructor;
unsigned int page_base, /* Start of page data */
page_len, /* Length of page data */
flags; /* Flags for data disposition */
@@ -214,6 +215,7 @@ typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
+ struct skb_frag_destructor *destroy,
unsigned int base, unsigned int len);
extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
extern void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index a80b1d3..40c2420 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -194,7 +194,7 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
while (pglen > 0) {
if (slen == size)
flags = 0;
- result = kernel_sendpage(sock, *ppage, NULL, base, size, flags);
+ result = kernel_sendpage(sock, *ppage, xdr->destructor, base, size, flags);
if (result > 0)
len += result;
if (result != size)
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index f008c14..9c7dded 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -525,12 +525,14 @@ EXPORT_SYMBOL_GPL(xdr_reserve_space);
* @len: length of data in bytes
*
*/
-void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int base,
+void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
+ struct skb_frag_destructor *destroy, unsigned int base,
unsigned int len)
{
struct xdr_buf *buf = xdr->buf;
struct kvec *iov = buf->tail;
buf->pages = pages;
+ buf->destructor = destroy;
buf->page_base = base;
buf->page_len = len;
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 72abb73..aa31294 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -397,7 +397,7 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i
remainder -= len;
if (remainder != 0 || more)
flags |= MSG_MORE;
- err = sock->ops->sendpage(sock, *ppage, base, len, flags);
+ err = sock->ops->sendpage_destructor(sock, *ppage, xdr->destructor, base, len, flags);
if (remainder == 0 || err != len)
break;
sent += err;
--
1.7.2.5
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 08/10] net: add paged frag destructor support to kernel_sendpage.
From: Ian Campbell @ 2011-07-15 11:07 UTC (permalink / raw)
To: netdev-u79uwXL29TY76Z2rM5mHXA
Cc: linux-nfs-u79uwXL29TY76Z2rM5mHXA, Ian Campbell
In-Reply-To: <1310728006.20648.3.camel-o4Be2W7LfRlXesXXhkcM7miJhflN2719@public.gmane.org>
NB: I added a separate sendpage_destructor to struct proto_ops and struct proto
for this PoC but expect that it would be preferable to just add the new
parameter and update all callers in the tree for the final version.
Signed-off-by: Ian Campbell <ian.campbell-Sxgqhf6Nn4DQT0dZR+AlfA@public.gmane.org>
---
drivers/staging/pohmelfs/trans.c | 2 +-
fs/dlm/lowcomms.c | 2 +-
include/linux/net.h | 9 ++++++++-
include/net/inet_common.h | 4 ++++
include/net/sock.h | 4 ++++
include/net/tcp.h | 4 ++++
net/ceph/messenger.c | 2 +-
net/ipv4/af_inet.c | 20 ++++++++++++++++++--
net/ipv4/tcp.c | 29 ++++++++++++++++++++++++-----
net/ipv4/tcp_ipv4.c | 1 +
net/ipv6/af_inet6.c | 1 +
net/ipv6/tcp_ipv6.c | 1 +
net/socket.c | 28 +++++++++++++++++++++++-----
net/sunrpc/svcsock.c | 6 +++---
14 files changed, 94 insertions(+), 19 deletions(-)
diff --git a/drivers/staging/pohmelfs/trans.c b/drivers/staging/pohmelfs/trans.c
index 36a2535..b5d8411 100644
--- a/drivers/staging/pohmelfs/trans.c
+++ b/drivers/staging/pohmelfs/trans.c
@@ -104,7 +104,7 @@ static int netfs_trans_send_pages(struct netfs_trans *t, struct netfs_state *st)
msg.msg_flags = MSG_WAITALL | (attached_pages == 1 ? 0 :
MSG_MORE);
- err = kernel_sendpage(st->socket, page, 0, size, msg.msg_flags);
+ err = kernel_sendpage(st->socket, page, NULL, 0, size, msg.msg_flags);
if (err <= 0) {
printk("%s: %d/%d failed to send transaction page: t: %p, gen: %u, size: %u, err: %d.\n",
__func__, i, t->page_num, t, t->gen, size, err);
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c
index 5e2c71f..64933ff 100644
--- a/fs/dlm/lowcomms.c
+++ b/fs/dlm/lowcomms.c
@@ -1341,7 +1341,7 @@ static void send_to_sock(struct connection *con)
ret = 0;
if (len) {
- ret = kernel_sendpage(con->sock, e->page, offset, len,
+ ret = kernel_sendpage(con->sock, e->page, NULL, offset, len,
msg_flags);
if (ret == -EAGAIN || ret == 0) {
if (ret == -EAGAIN &&
diff --git a/include/linux/net.h b/include/linux/net.h
index b299230..dfedc46 100644
--- a/include/linux/net.h
+++ b/include/linux/net.h
@@ -157,6 +157,7 @@ struct kiocb;
struct sockaddr;
struct msghdr;
struct module;
+struct skb_frag_destructor;
struct proto_ops {
int family;
@@ -204,6 +205,10 @@ struct proto_ops {
struct vm_area_struct * vma);
ssize_t (*sendpage) (struct socket *sock, struct page *page,
int offset, size_t size, int flags);
+ ssize_t (*sendpage_destructor)
+ (struct socket *sock, struct page *page,
+ struct skb_frag_destructor *destroy,
+ int offset, size_t size, int flags);
ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len, unsigned int flags);
};
@@ -273,7 +278,9 @@ extern int kernel_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen);
extern int kernel_setsockopt(struct socket *sock, int level, int optname,
char *optval, unsigned int optlen);
-extern int kernel_sendpage(struct socket *sock, struct page *page, int offset,
+extern int kernel_sendpage(struct socket *sock, struct page *page,
+ struct skb_frag_destructor *destroy,
+ int offset,
size_t size, int flags);
extern int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg);
extern int kernel_sock_shutdown(struct socket *sock,
diff --git a/include/net/inet_common.h b/include/net/inet_common.h
index 22fac98..0c39b4b 100644
--- a/include/net/inet_common.h
+++ b/include/net/inet_common.h
@@ -23,6 +23,10 @@ extern int inet_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size);
extern ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
size_t size, int flags);
+extern ssize_t inet_sendpage_destructor(struct socket *sock, struct page *page,
+ struct skb_frag_destructor *frag,
+ int offset,
+ size_t size, int flags);
extern int inet_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t size, int flags);
extern int inet_shutdown(struct socket *sock, int how);
diff --git a/include/net/sock.h b/include/net/sock.h
index c0b938c..7836acf 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -764,6 +764,10 @@ struct proto {
int *addr_len);
int (*sendpage)(struct sock *sk, struct page *page,
int offset, size_t size, int flags);
+ int (*sendpage_destructor)
+ (struct sock *sk, struct page *page,
+ struct skb_frag_destructor *destroy,
+ int offset, size_t size, int flags);
int (*bind)(struct sock *sk,
struct sockaddr *uaddr, int addr_len);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index cda30ea..2b42320 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -319,6 +319,10 @@ extern int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size);
extern int tcp_sendpage(struct sock *sk, struct page *page, int offset,
size_t size, int flags);
+extern int tcp_sendpage_destructor(struct sock *sk, struct page *page,
+ struct skb_frag_destructor *destroy,
+ int offset,
+ size_t size, int flags);
extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
extern int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, unsigned len);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 78b55f4..ec7955b 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -852,7 +852,7 @@ static int write_partial_msg_pages(struct ceph_connection *con)
cpu_to_le32(crc32c(tmpcrc, base, len));
con->out_msg_pos.did_page_crc = 1;
}
- ret = kernel_sendpage(con->sock, page,
+ ret = kernel_sendpage(con->sock, page, NULL,
con->out_msg_pos.page_pos + page_shift,
len,
MSG_DONTWAIT | MSG_NOSIGNAL |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index eae1f67..7954809 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -738,7 +738,9 @@ int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
}
EXPORT_SYMBOL(inet_sendmsg);
-ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
+ssize_t inet_sendpage_destructor(struct socket *sock, struct page *page,
+ struct skb_frag_destructor *destroy,
+ int offset,
size_t size, int flags)
{
struct sock *sk = sock->sk;
@@ -750,10 +752,21 @@ ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
inet_autobind(sk))
return -EAGAIN;
- if (sk->sk_prot->sendpage)
+ if (destroy) {
+ if (sk->sk_prot->sendpage_destructor)
+ return sk->sk_prot->sendpage_destructor
+ (sk, page, destroy, offset, size, flags);
+ } else if (sk->sk_prot->sendpage)
return sk->sk_prot->sendpage(sk, page, offset, size, flags);
return sock_no_sendpage(sock, page, offset, size, flags);
}
+EXPORT_SYMBOL(inet_sendpage_destructor);
+
+ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
+ size_t size, int flags)
+{
+ return inet_sendpage_destructor(sock, page, NULL, offset, size, flags);
+}
EXPORT_SYMBOL(inet_sendpage);
int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
@@ -917,6 +930,7 @@ const struct proto_ops inet_stream_ops = {
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
+ .sendpage_destructor = inet_sendpage_destructor,
.splice_read = tcp_splice_read,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
@@ -945,6 +959,7 @@ const struct proto_ops inet_dgram_ops = {
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
+ .sendpage_destructor = inet_sendpage_destructor,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
@@ -976,6 +991,7 @@ static const struct proto_ops inet_sockraw_ops = {
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
+ .sendpage_destructor = inet_sendpage_destructor,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 3a3703c..bfc778e 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -757,7 +757,10 @@ static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
return mss_now;
}
-static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffset,
+static ssize_t do_tcp_sendpages(struct sock *sk,
+ struct page **pages,
+ struct skb_frag_destructor **destructors,
+ int poffset,
size_t psize, int flags)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -783,6 +786,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse
while (psize > 0) {
struct sk_buff *skb = tcp_write_queue_tail(sk);
struct page *page = pages[poffset / PAGE_SIZE];
+ struct skb_frag_destructor *destructor = destructors ? destructors[poffset / PAGE_SIZE] : NULL;
int copy, i, can_coalesce;
int offset = poffset % PAGE_SIZE;
int size = min_t(size_t, psize, PAGE_SIZE - offset);
@@ -815,8 +819,9 @@ new_segment:
if (can_coalesce) {
skb_shinfo(skb)->frags[i - 1].size += copy;
} else {
- get_page(page);
skb_fill_page_desc(skb, i, page, offset, copy);
+ skb_shinfo(skb)->frags[i].page.destructor = destructor;
+ skb_frag_ref(skb, i);
}
skb->len += copy;
@@ -871,8 +876,11 @@ out_err:
return sk_stream_error(sk, flags, err);
}
-int tcp_sendpage(struct sock *sk, struct page *page, int offset,
- size_t size, int flags)
+int tcp_sendpage_destructor(struct sock *sk,
+ struct page *page,
+ struct skb_frag_destructor *destructor,
+ int offset,
+ size_t size, int flags)
{
ssize_t res;
@@ -882,10 +890,21 @@ int tcp_sendpage(struct sock *sk, struct page *page, int offset,
flags);
lock_sock(sk);
- res = do_tcp_sendpages(sk, &page, offset, size, flags);
+ res = do_tcp_sendpages(sk, &page,
+ destructor ? &destructor : NULL,
+ offset, size, flags);
release_sock(sk);
return res;
}
+EXPORT_SYMBOL(tcp_sendpage_destructor);
+
+int tcp_sendpage(struct sock *sk,
+ struct page *page,
+ int offset,
+ size_t size, int flags)
+{
+ return tcp_sendpage_destructor(sk, page, NULL, offset, size, flags);
+}
EXPORT_SYMBOL(tcp_sendpage);
#define TCP_PAGE(sk) (sk->sk_sndmsg_page)
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 708dc20..9baa996 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -2587,6 +2587,7 @@ struct proto tcp_prot = {
.recvmsg = tcp_recvmsg,
.sendmsg = tcp_sendmsg,
.sendpage = tcp_sendpage,
+ .sendpage_destructor = tcp_sendpage_destructor,
.backlog_rcv = tcp_v4_do_rcv,
.hash = inet_hash,
.unhash = inet_unhash,
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index d450a2f..58d2520 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -531,6 +531,7 @@ const struct proto_ops inet6_stream_ops = {
.recvmsg = inet_recvmsg, /* ok */
.mmap = sock_no_mmap,
.sendpage = inet_sendpage,
+ .sendpage_destructor = inet_sendpage_destructor,
.splice_read = tcp_splice_read,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 87551ca..98a2576 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -2210,6 +2210,7 @@ struct proto tcpv6_prot = {
.recvmsg = tcp_recvmsg,
.sendmsg = tcp_sendmsg,
.sendpage = tcp_sendpage,
+ .sendpage_destructor = tcp_sendpage_destructor,
.backlog_rcv = tcp_v6_do_rcv,
.hash = tcp_v6_hash,
.unhash = inet_unhash,
diff --git a/net/socket.c b/net/socket.c
index 02dc82d..f1c39a4 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -795,7 +795,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page,
if (more)
flags |= MSG_MORE;
- return kernel_sendpage(sock, page, offset, size, flags);
+ return kernel_sendpage(sock, page, NULL, offset, size, flags);
}
static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
@@ -3343,15 +3343,33 @@ int kernel_setsockopt(struct socket *sock, int level, int optname,
}
EXPORT_SYMBOL(kernel_setsockopt);
-int kernel_sendpage(struct socket *sock, struct page *page, int offset,
+int kernel_sendpage(struct socket *sock, struct page *page,
+ struct skb_frag_destructor *destroy,
+ int offset,
size_t size, int flags)
{
+ int ret;
sock_update_classid(sock->sk);
- if (sock->ops->sendpage)
- return sock->ops->sendpage(sock, page, offset, size, flags);
+ /*
+ * If we have a destructor but the socket does not support
+ * sendpage_destructor then fallback to sock_no_sendpage which
+ * is copying...
+ */
+ if (destroy) {
+ if (sock->ops->sendpage_destructor)
+ return sock->ops->sendpage_destructor(sock, page, destroy,
+ offset, size, flags);
+ } else {
+ if (sock->ops->sendpage)
+ return sock->ops->sendpage(sock, page,
+ offset, size, flags);
+ }
- return sock_no_sendpage(sock, page, offset, size, flags);
+ ret = sock_no_sendpage(sock, page, offset, size, flags);
+ /* sock_no_sendpage copies so we can destroy immediately */
+ skb_frag_destructor_unref(destroy);
+ return ret;
}
EXPORT_SYMBOL(kernel_sendpage);
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index af04f77..a80b1d3 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -181,7 +181,7 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
/* send head */
if (slen == xdr->head[0].iov_len)
flags = 0;
- len = kernel_sendpage(sock, headpage, headoffset,
+ len = kernel_sendpage(sock, headpage, NULL, headoffset,
xdr->head[0].iov_len, flags);
if (len != xdr->head[0].iov_len)
goto out;
@@ -194,7 +194,7 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
while (pglen > 0) {
if (slen == size)
flags = 0;
- result = kernel_sendpage(sock, *ppage, base, size, flags);
+ result = kernel_sendpage(sock, *ppage, NULL, base, size, flags);
if (result > 0)
len += result;
if (result != size)
@@ -208,7 +208,7 @@ int svc_send_common(struct socket *sock, struct xdr_buf *xdr,
/* send tail */
if (xdr->tail[0].iov_len) {
- result = kernel_sendpage(sock, tailpage, tailoffset,
+ result = kernel_sendpage(sock, tailpage, NULL, tailoffset,
xdr->tail[0].iov_len, 0);
if (result > 0)
len += result;
--
1.7.2.5
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox