aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/mca/git:/ssh:/git@git.zx2c4.com
diff options
context:
space:
mode:
authorKotresh HR <khiremat@redhat.com>2025-09-11 15:02:35 +0530
committerIlya Dryomov <idryomov@gmail.com>2025-10-08 23:30:47 +0200
commit22c73d52a6d05c5a2053385c0d6cd9984732799d (patch)
tree56247f73b799f1ceeeef9959bc54114a1821c52b /drivers/mca/git:/ssh:/git@git.zx2c4.com
parentceph: cleanup in ceph_alloc_readdir_reply_buffer() (diff)
ceph: fix multifs mds auth caps issue
The mds auth caps check should also validate the fsname along with the associated caps. Not doing so would result in applying the mds auth caps of one fs on to the other fs in a multifs ceph cluster. The bug causes multiple issues w.r.t user authentication, following is one such example. Steps to Reproduce (on vstart cluster): 1. Create two file systems in a cluster, say 'fsname1' and 'fsname2' 2. Authorize read only permission to the user 'client.usr' on fs 'fsname1' $ceph fs authorize fsname1 client.usr / r 3. Authorize read and write permission to the same user 'client.usr' on fs 'fsname2' $ceph fs authorize fsname2 client.usr / rw 4. Update the keyring $ceph auth get client.usr >> ./keyring With above permssions for the user 'client.usr', following is the expectation. a. The 'client.usr' should be able to only read the contents and not allowed to create or delete files on file system 'fsname1'. b. The 'client.usr' should be able to read/write on file system 'fsname2'. But, with this bug, the 'client.usr' is allowed to read/write on file system 'fsname1'. See below. 5. Mount the file system 'fsname1' with the user 'client.usr' $sudo bin/mount.ceph usr@.fsname1=/ /kmnt_fsname1_usr/ 6. Try creating a file on file system 'fsname1' with user 'client.usr'. This should fail but passes with this bug. $touch /kmnt_fsname1_usr/file1 7. Mount the file system 'fsname1' with the user 'client.admin' and create a file. $sudo bin/mount.ceph admin@.fsname1=/ /kmnt_fsname1_admin $echo "data" > /kmnt_fsname1_admin/admin_file1 8. Try removing an existing file on file system 'fsname1' with the user 'client.usr'. This shoudn't succeed but succeeds with the bug. $rm -f /kmnt_fsname1_usr/admin_file1 For more information, please take a look at the corresponding mds/fuse patch and tests added by looking into the tracker mentioned below. v2: Fix a possible null dereference in doutc v3: Don't store fsname from mdsmap, validate against ceph_mount_options's fsname and use it v4: Code refactor, better warning message and fix possible compiler warning [ Slava.Dubeyko: "fsname check failed" -> "fsname mismatch" ] Link: https://tracker.ceph.com/issues/72167 Signed-off-by: Kotresh HR <khiremat@redhat.com> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Diffstat (limited to 'drivers/mca/git:/ssh:/git@git.zx2c4.com')
0 files changed, 0 insertions, 0 deletions
id='n230' href='#n230'>230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
// SPDX-License-Identifier: GPL-2.0
/*
 * Altera PCIe MSI support
 *
 * Author: Ley Foon Tan <lftan@altera.com>
 *
 * Copyright Altera Corporation (C) 2013-2015. All rights reserved
 */

#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

#define MSI_STATUS		0x0
#define MSI_ERROR		0x4
#define MSI_INTMASK		0x8

#define MAX_MSI_VECTORS		32

struct altera_msi {
	DECLARE_BITMAP(used, MAX_MSI_VECTORS);
	struct mutex		lock;	/* protect "used" bitmap */
	struct platform_device	*pdev;
	struct irq_domain	*msi_domain;
	struct irq_domain	*inner_domain;
	void __iomem		*csr_base;
	void __iomem		*vector_base;
	phys_addr_t		vector_phy;
	u32			num_of_vectors;
	int			irq;
};

static inline void msi_writel(struct altera_msi *msi, const u32 value,
			      const u32 reg)
{
	writel_relaxed(value, msi->csr_base + reg);
}

static inline u32 msi_readl(struct altera_msi *msi, const u32 reg)
{
	return readl_relaxed(msi->csr_base + reg);
}

static void altera_msi_isr(struct irq_desc *desc)
{
	struct irq_chip *chip = irq_desc_get_chip(desc);
	struct altera_msi *msi;
	unsigned long status;
	u32 bit;
	int ret;

	chained_irq_enter(chip, desc);
	msi = irq_desc_get_handler_data(desc);

	while ((status = msi_readl(msi, MSI_STATUS)) != 0) {
		for_each_set_bit(bit, &status, msi->num_of_vectors) {
			/* Dummy read from vector to clear the interrupt */
			readl_relaxed(msi->vector_base + (bit * sizeof(u32)));

			ret = generic_handle_domain_irq(msi->inner_domain, bit);
			if (ret)
				dev_err_ratelimited(&msi->pdev->dev, "unexpected MSI\n");
		}
	}

	chained_irq_exit(chip, desc);
}

static struct irq_chip altera_msi_irq_chip = {
	.name = "Altera PCIe MSI",
	.irq_mask = pci_msi_mask_irq,
	.irq_unmask = pci_msi_unmask_irq,
};

static struct msi_domain_info altera_msi_domain_info = {
	.flags	= (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
		     MSI_FLAG_PCI_MSIX),
	.chip	= &altera_msi_irq_chip,
};

static void altera_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
	struct altera_msi *msi = irq_data_get_irq_chip_data(data);
	phys_addr_t addr = msi->vector_phy + (data->hwirq * sizeof(u32));

	msg->address_lo = lower_32_bits(addr);
	msg->address_hi = upper_32_bits(addr);
	msg->data = data->hwirq;

	dev_dbg(&msi->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
		(int)data->hwirq, msg->address_hi, msg->address_lo);
}

static int altera_msi_set_affinity(struct irq_data *irq_data,
				   const struct cpumask *mask, bool force)
{
	 return -EINVAL;
}

static struct irq_chip altera_msi_bottom_irq_chip = {
	.name			= "Altera MSI",
	.irq_compose_msi_msg	= altera_compose_msi_msg,
	.irq_set_affinity	= altera_msi_set_affinity,
};

static int altera_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
				   unsigned int nr_irqs, void *args)
{
	struct altera_msi *msi = domain->host_data;
	unsigned long bit;
	u32 mask;

	WARN_ON(nr_irqs != 1);
	mutex_lock(&msi->lock);

	bit = find_first_zero_bit(msi->used, msi->num_of_vectors);
	if (bit >= msi->num_of_vectors) {
		mutex_unlock(&msi->lock);
		return -ENOSPC;
	}

	set_bit(bit, msi->used);

	mutex_unlock(&msi->lock);

	irq_domain_set_info(domain, virq, bit, &altera_msi_bottom_irq_chip,
			    domain->host_data, handle_simple_irq,
			    NULL, NULL);

	mask = msi_readl(msi, MSI_INTMASK);
	mask |= 1 << bit;
	msi_writel(msi, mask, MSI_INTMASK);

	return 0;
}

static void altera_irq_domain_free(struct irq_domain *domain,
				   unsigned int virq, unsigned int nr_irqs)
{
	struct irq_data *d = irq_domain_get_irq_data(domain, virq);
	struct altera_msi *msi = irq_data_get_irq_chip_data(d);
	u32 mask;

	mutex_lock(&msi->lock);

	if (!test_bit(d->hwirq, msi->used)) {
		dev_err(&msi->pdev->dev, "trying to free unused MSI#%lu\n",
			d->hwirq);
	} else {
		__clear_bit(d->hwirq, msi->used);
		mask = msi_readl(msi, MSI_INTMASK);
		mask &= ~(1 << d->hwirq);
		msi_writel(msi, mask, MSI_INTMASK);
	}

	mutex_unlock(&msi->lock);
}

static const struct irq_domain_ops msi_domain_ops = {
	.alloc	= altera_irq_domain_alloc,
	.free	= altera_irq_domain_free,
};

static int altera_allocate_domains(struct altera_msi *msi)
{
	struct fwnode_handle *fwnode = of_node_to_fwnode(msi->pdev->dev.of_node);

	msi->inner_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
					     &msi_domain_ops, msi);
	if (!msi->inner_domain) {
		dev_err(&msi->pdev->dev, "failed to create IRQ domain\n");
		return -ENOMEM;
	}

	msi->msi_domain = pci_msi_create_irq_domain(fwnode,
				&altera_msi_domain_info, msi->inner_domain);
	if (!msi->msi_domain) {
		dev_err(&msi->pdev->dev, "failed to create MSI domain\n");
		irq_domain_remove(msi->inner_domain);
		return -ENOMEM;
	}

	return 0;
}

static void altera_free_domains(struct altera_msi *msi)
{
	irq_domain_remove(msi->msi_domain);
	irq_domain_remove(msi->inner_domain);
}

static void altera_msi_remove(struct platform_device *pdev)
{
	struct altera_msi *msi = platform_get_drvdata(pdev);

	msi_writel(msi, 0, MSI_INTMASK);
	irq_set_chained_handler_and_data(msi->irq, NULL, NULL);

	altera_free_domains(msi);

	platform_set_drvdata(pdev, NULL);
}

static int altera_msi_probe(struct platform_device *pdev)
{
	struct altera_msi *msi;
	struct device_node *np = pdev->dev.of_node;
	struct resource *res;
	int ret;

	msi = devm_kzalloc(&pdev->dev, sizeof(struct altera_msi),
			   GFP_KERNEL);
	if (!msi)
		return -ENOMEM;

	mutex_init(&msi->lock);
	msi->pdev = pdev;

	msi->csr_base = devm_platform_ioremap_resource_byname(pdev, "csr");
	if (IS_ERR(msi->csr_base)) {
		dev_err(&pdev->dev, "failed to map csr memory\n");
		return PTR_ERR(msi->csr_base);
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
					   "vector_slave");
	msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(msi->vector_base))
		return PTR_ERR(msi->vector_base);

	msi->vector_phy = res->start;

	if (of_property_read_u32(np, "num-vectors", &msi->num_of_vectors)) {
		dev_err(&pdev->dev, "failed to parse the number of vectors\n");
		return -EINVAL;
	}

	ret = altera_allocate_domains(msi);
	if (ret)
		return ret;

	msi->irq = platform_get_irq(pdev, 0);
	if (msi->irq < 0) {
		ret = msi->irq;
		goto err;
	}

	irq_set_chained_handler_and_data(msi->irq, altera_msi_isr, msi);
	platform_set_drvdata(pdev, msi);

	return 0;

err:
	altera_msi_remove(pdev);
	return ret;
}

static const struct of_device_id altera_msi_of_match[] = {
	{ .compatible = "altr,msi-1.0", NULL },
	{ },
};

static struct platform_driver altera_msi_driver = {
	.driver = {
		.name = "altera-msi",
		.of_match_table = altera_msi_of_match,
	},
	.probe = altera_msi_probe,
	.remove_new = altera_msi_remove,
};

static int __init altera_msi_init(void)
{
	return platform_driver_register(&altera_msi_driver);
}

static void __exit altera_msi_exit(void)
{
	platform_driver_unregister(&altera_msi_driver);
}

subsys_initcall(altera_msi_init);
MODULE_DEVICE_TABLE(of, altera_msi_of_match);
module_exit(altera_msi_exit);
MODULE_LICENSE("GPL v2");