https://github.com/torvalds/linux
Revision 3ad216ee73abc554ed8f13f4f8b70845a7bef6da authored by David Howells on 14 November 2020, 17:27:57 UTC, committed by Linus Torvalds on 14 November 2020, 19:51:18 UTC
When afs_write_end() is called with copied == 0, it tries to set the
dirty region, but there's no way to actually encode a 0-length region in
the encoding in page->private.

"0,0", for example, indicates a 1-byte region at offset 0.  The maths
miscalculates this and sets it incorrectly.

Fix it to just do nothing but unlock and put the page in this case.  We
don't actually need to mark the page dirty as nothing presumably
changed.

Fixes: 65dd2d6072d3 ("afs: Alter dirty range encoding in page->private")
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent f01c30d
Raw File
Tip revision: 3ad216ee73abc554ed8f13f4f8b70845a7bef6da authored by David Howells on 14 November 2020, 17:27:57 UTC
afs: Fix afs_write_end() when called with copied == 0 [ver #3]
Tip revision: 3ad216e
devres.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * This file contains all networking devres helpers.
 */

#include <linux/device.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>

struct net_device_devres {
	struct net_device *ndev;
};

static void devm_free_netdev(struct device *dev, void *this)
{
	struct net_device_devres *res = this;

	free_netdev(res->ndev);
}

struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
					   unsigned int txqs, unsigned int rxqs)
{
	struct net_device_devres *dr;

	dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
	if (!dr)
		return NULL;

	dr->ndev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
	if (!dr->ndev) {
		devres_free(dr);
		return NULL;
	}

	devres_add(dev, dr);

	return dr->ndev;
}
EXPORT_SYMBOL(devm_alloc_etherdev_mqs);

static void devm_unregister_netdev(struct device *dev, void *this)
{
	struct net_device_devres *res = this;

	unregister_netdev(res->ndev);
}

static int netdev_devres_match(struct device *dev, void *this, void *match_data)
{
	struct net_device_devres *res = this;
	struct net_device *ndev = match_data;

	return ndev == res->ndev;
}

/**
 *	devm_register_netdev - resource managed variant of register_netdev()
 *	@dev: managing device for this netdev - usually the parent device
 *	@ndev: device to register
 *
 *	This is a devres variant of register_netdev() for which the unregister
 *	function will be call automatically when the managing device is
 *	detached. Note: the net_device used must also be resource managed by
 *	the same struct device.
 */
int devm_register_netdev(struct device *dev, struct net_device *ndev)
{
	struct net_device_devres *dr;
	int ret;

	/* struct net_device must itself be managed. For now a managed netdev
	 * can only be allocated by devm_alloc_etherdev_mqs() so the check is
	 * straightforward.
	 */
	if (WARN_ON(!devres_find(dev, devm_free_netdev,
				 netdev_devres_match, ndev)))
		return -EINVAL;

	dr = devres_alloc(devm_unregister_netdev, sizeof(*dr), GFP_KERNEL);
	if (!dr)
		return -ENOMEM;

	ret = register_netdev(ndev);
	if (ret) {
		devres_free(dr);
		return ret;
	}

	dr->ndev = ndev;
	devres_add(ndev->dev.parent, dr);

	return 0;
}
EXPORT_SYMBOL(devm_register_netdev);
back to top