This is the 6.1.71 stable release
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEZH8oZUiU471FcZm+ONu9yGCSaT4FAmWYD8QACgkQONu9yGCS aT5cEA//UKwVnselP3QHU6yEm2j8Vuq5IOEIqIeYTDTyS7TGP83SsyM4n2KRlTwC /vaY3HWNsZHLqsNICPOPSdQn9STa7MYTnf/ackBbPglDnDz/A6mSB3zkXtCKFm6+ UBmk6Y8pZwpdvk3aa6Z62Kr5bGGHdzvXdiJitERLlD2PFUOZT9/IHSncGnts3TQv PjFXy1KVIGsThKbtjtYPpa100RAti5HeLv/NbsaVbuKYMME/QCFmqyNRAp9k2iHx 3nkze70aoREShEDjaLkcsirzwRKJu7qqNriYLt+wd7HmcD328R2UlTR8L3ZM0xOq qxBHnzbFtQyGR7NAudi2pStqwctPhFP6vRz1aJvt+w9tmbeKAWQWMd2pNvG8GhJm nxYFGyPLzTgPifK5SELCNIW4WXf8rnrRNgZ+Ph/JIGuhp+603//ATHRlVEwHcnl+ M0GRbL06nWFVvfdKCYuu0autb9sW5T/vq02cbE5vRVVaziazry8S8EmxYQyOg9X/ CBAd1XTybVZki9VkIP5zbdvWJL3LhFfsabBFy7TPZor/YCJQDvxzw1iwtY/BPVDT MryHjrYwH/n5RvibANRcTbCamMQY4IrJ4X3afJGgh7BK5N5C5ug4HYJ7oG5QB++x xC4A5x3L6D9SE/St8hFWghjYcd6lFcjlz1wJ5MyLImwYqfr8DnY= =Vt0s -----END PGP SIGNATURE----- Merge 6.1.71 into android14-6.1-lts Changes in 6.1.71 ksmbd: replace one-element arrays with flexible-array members ksmbd: set SMB2_SESSION_FLAG_ENCRYPT_DATA when enforcing data encryption for this share ksmbd: use F_SETLK when unlocking a file ksmbd: Fix resource leak in smb2_lock() ksmbd: Convert to use sysfs_emit()/sysfs_emit_at() APIs ksmbd: Implements sess->rpc_handle_list as xarray ksmbd: fix typo, syncronous->synchronous ksmbd: Remove duplicated codes ksmbd: update Kconfig to note Kerberos support and fix indentation ksmbd: Fix spelling mistake "excceed" -> "exceeded" ksmbd: Fix parameter name and comment mismatch ksmbd: remove unused is_char_allowed function ksmbd: delete asynchronous work from list ksmbd: set NegotiateContextCount once instead of every inc ksmbd: avoid duplicate negotiate ctx offset increments ksmbd: remove unused compression negotiate ctx packing fs: introduce lock_rename_child() helper ksmbd: fix racy issue from using ->d_parent and ->d_name ksmbd: fix uninitialized pointer read in ksmbd_vfs_rename() ksmbd: fix uninitialized pointer read in smb2_create_link() ksmbd: call putname after using the last component ksmbd: fix posix_acls and acls dereferencing possible ERR_PTR() ksmbd: add mnt_want_write to ksmbd vfs functions ksmbd: remove unused ksmbd_tree_conn_share function ksmbd: use kzalloc() instead of __GFP_ZERO ksmbd: return a literal instead of 'err' in ksmbd_vfs_kern_path_locked() ksmbd: Change the return value of ksmbd_vfs_query_maximal_access to void ksmbd: use kvzalloc instead of kvmalloc ksmbd: Replace the ternary conditional operator with min() ksmbd: Use struct_size() helper in ksmbd_negotiate_smb_dialect() ksmbd: Replace one-element array with flexible-array member ksmbd: Fix unsigned expression compared with zero ksmbd: check if a mount point is crossed during path lookup ksmbd: switch to use kmemdup_nul() helper ksmbd: add support for read compound ksmbd: fix wrong interim response on compound ksmbd: fix `force create mode' and `force directory mode' ksmbd: Fix one kernel-doc comment ksmbd: add missing calling smb2_set_err_rsp() on error ksmbd: remove experimental warning ksmbd: remove unneeded mark_inode_dirty in set_info_sec() ksmbd: fix passing freed memory 'aux_payload_buf' ksmbd: return invalid parameter error response if smb2 request is invalid ksmbd: check iov vector index in ksmbd_conn_write() ksmbd: fix race condition with fp ksmbd: fix race condition from parallel smb2 logoff requests ksmbd: fix race condition from parallel smb2 lock requests ksmbd: fix race condition between tree conn lookup and disconnect ksmbd: fix wrong error response status by using set_smb2_rsp_status() ksmbd: fix Null pointer dereferences in ksmbd_update_fstate() ksmbd: fix potential double free on smb2_read_pipe() error path ksmbd: Remove unused field in ksmbd_user struct ksmbd: reorganize ksmbd_iov_pin_rsp() ksmbd: fix kernel-doc comment of ksmbd_vfs_setxattr() ksmbd: fix recursive locking in vfs helpers ksmbd: fix missing RDMA-capable flag for IPoIB device in ksmbd_rdma_capable_netdev() ksmbd: add support for surrogate pair conversion ksmbd: no need to wait for binded connection termination at logoff ksmbd: fix kernel-doc comment of ksmbd_vfs_kern_path_locked() ksmbd: prevent memory leak on error return ksmbd: fix possible deadlock in smb2_open ksmbd: separately allocate ci per dentry ksmbd: move oplock handling after unlock parent dir ksmbd: release interim response after sending status pending response ksmbd: move setting SMB2_FLAGS_ASYNC_COMMAND and AsyncId ksmbd: don't update ->op_state as OPLOCK_STATE_NONE on error ksmbd: set epoch in create context v2 lease ksmbd: set v2 lease capability ksmbd: downgrade RWH lease caching state to RH for directory ksmbd: send v2 lease break notification for directory ksmbd: lazy v2 lease break on smb2_write() ksmbd: avoid duplicate opinfo_put() call on error of smb21_lease_break_ack() ksmbd: fix wrong allocation size update in smb2_open() ARM: dts: Fix occasional boot hang for am3 usb usb: fotg210-hcd: delete an incorrect bounds test spi: Introduce spi_get_device_match_data() helper iio: imu: adis16475: add spi_device_id table nfsd: separate nfsd_last_thread() from nfsd_put() nfsd: call nfsd_last_thread() before final nfsd_put() linux/export: Ensure natural alignment of kcrctab array spi: Reintroduce spi_set_cs_timing() spi: Add APIs in spi core to set/get spi->chip_select and spi->cs_gpiod spi: atmel: Fix clock issue when using devices with different polarities block: renumber QUEUE_FLAG_HW_WC ksmbd: fix slab-out-of-bounds in smb_strndup_from_utf16() platform/x86: p2sb: Allow p2sb_bar() calls during PCI device probe mm/filemap: avoid buffered read/write race to read inconsistent data mm: migrate high-order folios in swap cache correctly mm/memory-failure: cast index to loff_t before shifting it mm/memory-failure: check the mapcount of the precise page ring-buffer: Fix wake ups when buffer_percent is set to 100 tracing: Fix blocked reader of snapshot buffer ring-buffer: Remove useless update to write_stamp in rb_try_to_discard() netfilter: nf_tables: skip set commit for deleted/destroyed sets ring-buffer: Fix slowpath of interrupted event NFSD: fix possible oops when nfsd/pool_stats is closed. spi: Constify spi parameters of chip select APIs device property: Allow const parameter to dev_fwnode() kallsyms: Make module_kallsyms_on_each_symbol generally available tracing/kprobes: Fix symbol counting logic by looking at modules as well Revert "platform/x86: p2sb: Allow p2sb_bar() calls during PCI device probe" Linux 6.1.71 Change-Id: I7bc16d981b90e8e0b633628438f79fce898ad15a Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
commit
8eac30b25e
2
Makefile
2
Makefile
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
VERSION = 6
|
||||
PATCHLEVEL = 1
|
||||
SUBLEVEL = 70
|
||||
SUBLEVEL = 71
|
||||
EXTRAVERSION =
|
||||
NAME = Curry Ramen
|
||||
|
||||
|
@ -349,6 +349,7 @@ usb: target-module@47400000 {
|
||||
<SYSC_IDLE_NO>,
|
||||
<SYSC_IDLE_SMART>,
|
||||
<SYSC_IDLE_SMART_WKUP>;
|
||||
ti,sysc-delay-us = <2>;
|
||||
clocks = <&l3s_clkctrl AM3_L3S_USB_OTG_HS_CLKCTRL 0>;
|
||||
clock-names = "fck";
|
||||
#address-cells = <1>;
|
||||
|
@ -17,12 +17,19 @@
|
||||
#include <linux/property.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
struct fwnode_handle *dev_fwnode(const struct device *dev)
|
||||
struct fwnode_handle *__dev_fwnode(struct device *dev)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
|
||||
of_fwnode_handle(dev->of_node) : dev->fwnode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_fwnode);
|
||||
EXPORT_SYMBOL_GPL(__dev_fwnode);
|
||||
|
||||
const struct fwnode_handle *__dev_fwnode_const(const struct device *dev)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
|
||||
of_fwnode_handle(dev->of_node) : dev->fwnode;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__dev_fwnode_const);
|
||||
|
||||
/**
|
||||
* device_property_present - check if a property of a device is present
|
||||
|
@ -1243,6 +1243,59 @@ static int adis16475_config_irq_pin(struct adis16475 *st)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int adis16475_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16475 *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->info = spi_get_device_match_data(spi);
|
||||
if (!st->info)
|
||||
return -EINVAL;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &st->info->adis_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->name = st->info->name;
|
||||
indio_dev->channels = st->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
indio_dev->info = &adis16475_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = __adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16475_config_irq_pin(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16475_config_sync_mode(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev,
|
||||
adis16475_trigger_handler);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adis16475_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id adis16475_of_match[] = {
|
||||
{ .compatible = "adi,adis16470",
|
||||
.data = &adis16475_chip_info[ADIS16470] },
|
||||
@ -1288,57 +1341,30 @@ static const struct of_device_id adis16475_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adis16475_of_match);
|
||||
|
||||
static int adis16475_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis16475 *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->info = device_get_match_data(&spi->dev);
|
||||
if (!st->info)
|
||||
return -EINVAL;
|
||||
|
||||
ret = adis_init(&st->adis, indio_dev, spi, &st->info->adis_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->name = st->info->name;
|
||||
indio_dev->channels = st->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
indio_dev->info = &adis16475_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = __adis_initial_startup(&st->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16475_config_irq_pin(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16475_config_sync_mode(st);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev,
|
||||
adis16475_trigger_handler);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
adis16475_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static const struct spi_device_id adis16475_ids[] = {
|
||||
{ "adis16470", (kernel_ulong_t)&adis16475_chip_info[ADIS16470] },
|
||||
{ "adis16475-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_1] },
|
||||
{ "adis16475-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_2] },
|
||||
{ "adis16475-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16475_3] },
|
||||
{ "adis16477-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_1] },
|
||||
{ "adis16477-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_2] },
|
||||
{ "adis16477-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16477_3] },
|
||||
{ "adis16465-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_1] },
|
||||
{ "adis16465-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_2] },
|
||||
{ "adis16465-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16465_3] },
|
||||
{ "adis16467-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_1] },
|
||||
{ "adis16467-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_2] },
|
||||
{ "adis16467-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16467_3] },
|
||||
{ "adis16500", (kernel_ulong_t)&adis16475_chip_info[ADIS16500] },
|
||||
{ "adis16505-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_1] },
|
||||
{ "adis16505-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_2] },
|
||||
{ "adis16505-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16505_3] },
|
||||
{ "adis16507-1", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_1] },
|
||||
{ "adis16507-2", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_2] },
|
||||
{ "adis16507-3", (kernel_ulong_t)&adis16475_chip_info[ADIS16507_3] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16475_ids);
|
||||
|
||||
static struct spi_driver adis16475_driver = {
|
||||
.driver = {
|
||||
@ -1346,6 +1372,7 @@ static struct spi_driver adis16475_driver = {
|
||||
.of_match_table = adis16475_of_match,
|
||||
},
|
||||
.probe = adis16475_probe,
|
||||
.id_table = adis16475_ids,
|
||||
};
|
||||
module_spi_driver(adis16475_driver);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <trace/events/spi.h>
|
||||
|
||||
/* SPI register offsets */
|
||||
@ -278,6 +279,7 @@ struct atmel_spi {
|
||||
bool keep_cs;
|
||||
|
||||
u32 fifo_size;
|
||||
bool last_polarity;
|
||||
u8 native_cs_free;
|
||||
u8 native_cs_for_gpio;
|
||||
};
|
||||
@ -290,6 +292,22 @@ struct atmel_spi_device {
|
||||
#define SPI_MAX_DMA_XFER 65535 /* true for both PDC and DMA */
|
||||
#define INVALID_DMA_ADDRESS 0xffffffff
|
||||
|
||||
/*
|
||||
* This frequency can be anything supported by the controller, but to avoid
|
||||
* unnecessary delay, the highest possible frequency is chosen.
|
||||
*
|
||||
* This frequency is the highest possible which is not interfering with other
|
||||
* chip select registers (see Note for Serial Clock Bit Rate configuration in
|
||||
* Atmel-11121F-ATARM-SAMA5D3-Series-Datasheet_02-Feb-16, page 1283)
|
||||
*/
|
||||
#define DUMMY_MSG_FREQUENCY 0x02
|
||||
/*
|
||||
* 8 bits is the minimum data the controller is capable of sending.
|
||||
*
|
||||
* This message can be anything as it should not be treated by any SPI device.
|
||||
*/
|
||||
#define DUMMY_MSG 0xAA
|
||||
|
||||
/*
|
||||
* Version 2 of the SPI controller has
|
||||
* - CR.LASTXFER
|
||||
@ -303,6 +321,43 @@ static bool atmel_spi_is_v2(struct atmel_spi *as)
|
||||
return as->caps.is_spi2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a dummy message.
|
||||
*
|
||||
* This is sometimes needed when using a CS GPIO to force clock transition when
|
||||
* switching between devices with different polarities.
|
||||
*/
|
||||
static void atmel_spi_send_dummy(struct atmel_spi *as, struct spi_device *spi, int chip_select)
|
||||
{
|
||||
u32 status;
|
||||
u32 csr;
|
||||
|
||||
/*
|
||||
* Set a clock frequency to allow sending message on SPI bus.
|
||||
* The frequency here can be anything, but is needed for
|
||||
* the controller to send the data.
|
||||
*/
|
||||
csr = spi_readl(as, CSR0 + 4 * chip_select);
|
||||
csr = SPI_BFINS(SCBR, DUMMY_MSG_FREQUENCY, csr);
|
||||
spi_writel(as, CSR0 + 4 * chip_select, csr);
|
||||
|
||||
/*
|
||||
* Read all data coming from SPI bus, needed to be able to send
|
||||
* the message.
|
||||
*/
|
||||
spi_readl(as, RDR);
|
||||
while (spi_readl(as, SR) & SPI_BIT(RDRF)) {
|
||||
spi_readl(as, RDR);
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
spi_writel(as, TDR, DUMMY_MSG);
|
||||
|
||||
readl_poll_timeout_atomic(as->regs + SPI_SR, status,
|
||||
(status & SPI_BIT(TXEMPTY)), 1, 1000);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby
|
||||
* they assume that spi slave device state will not change on deselect, so
|
||||
@ -319,11 +374,17 @@ static bool atmel_spi_is_v2(struct atmel_spi *as)
|
||||
* Master on Chip Select 0.") No workaround exists for that ... so for
|
||||
* nCS0 on that chip, we (a) don't use the GPIO, (b) can't support CS_HIGH,
|
||||
* and (c) will trigger that first erratum in some cases.
|
||||
*
|
||||
* When changing the clock polarity, the SPI controller waits for the next
|
||||
* transmission to enforce the default clock state. This may be an issue when
|
||||
* using a GPIO as Chip Select: the clock level is applied only when the first
|
||||
* packet is sent, once the CS has already been asserted. The workaround is to
|
||||
* avoid this by sending a first (dummy) message before toggling the CS state.
|
||||
*/
|
||||
|
||||
static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
|
||||
{
|
||||
struct atmel_spi_device *asd = spi->controller_state;
|
||||
bool new_polarity;
|
||||
int chip_select;
|
||||
u32 mr;
|
||||
|
||||
@ -352,6 +413,25 @@ static void cs_activate(struct atmel_spi *as, struct spi_device *spi)
|
||||
}
|
||||
|
||||
mr = spi_readl(as, MR);
|
||||
|
||||
/*
|
||||
* Ensures the clock polarity is valid before we actually
|
||||
* assert the CS to avoid spurious clock edges to be
|
||||
* processed by the spi devices.
|
||||
*/
|
||||
if (spi_get_csgpiod(spi, 0)) {
|
||||
new_polarity = (asd->csr & SPI_BIT(CPOL)) != 0;
|
||||
if (new_polarity != as->last_polarity) {
|
||||
/*
|
||||
* Need to disable the GPIO before sending the dummy
|
||||
* message because it is already set by the spi core.
|
||||
*/
|
||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 0);
|
||||
atmel_spi_send_dummy(as, spi, chip_select);
|
||||
as->last_polarity = new_polarity;
|
||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
u32 cpol = (spi->mode & SPI_CPOL) ? SPI_BIT(CPOL) : 0;
|
||||
int i;
|
||||
|
@ -360,6 +360,18 @@ const struct spi_device_id *spi_get_device_id(const struct spi_device *sdev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_get_device_id);
|
||||
|
||||
const void *spi_get_device_match_data(const struct spi_device *sdev)
|
||||
{
|
||||
const void *match;
|
||||
|
||||
match = device_get_match_data(&sdev->dev);
|
||||
if (match)
|
||||
return match;
|
||||
|
||||
return (const void *)spi_get_device_id(sdev)->driver_data;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_get_device_match_data);
|
||||
|
||||
static int spi_match_device(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
const struct spi_device *spi = to_spi_device(dev);
|
||||
@ -592,7 +604,7 @@ static void spi_dev_set_name(struct spi_device *spi)
|
||||
}
|
||||
|
||||
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->controller->dev),
|
||||
spi->chip_select);
|
||||
spi_get_chipselect(spi, 0));
|
||||
}
|
||||
|
||||
static int spi_dev_check(struct device *dev, void *data)
|
||||
@ -601,7 +613,7 @@ static int spi_dev_check(struct device *dev, void *data)
|
||||
struct spi_device *new_spi = data;
|
||||
|
||||
if (spi->controller == new_spi->controller &&
|
||||
spi->chip_select == new_spi->chip_select)
|
||||
spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
@ -626,7 +638,7 @@ static int __spi_add_device(struct spi_device *spi)
|
||||
status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
|
||||
if (status) {
|
||||
dev_err(dev, "chipselect %d already in use\n",
|
||||
spi->chip_select);
|
||||
spi_get_chipselect(spi, 0));
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -637,7 +649,7 @@ static int __spi_add_device(struct spi_device *spi)
|
||||
}
|
||||
|
||||
if (ctlr->cs_gpiods)
|
||||
spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
|
||||
spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]);
|
||||
|
||||
/*
|
||||
* Drivers may modify this initial i/o setup, but will
|
||||
@ -680,8 +692,8 @@ int spi_add_device(struct spi_device *spi)
|
||||
int status;
|
||||
|
||||
/* Chipselects are numbered 0..max; validate. */
|
||||
if (spi->chip_select >= ctlr->num_chipselect) {
|
||||
dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
|
||||
if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
|
||||
dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
|
||||
ctlr->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -702,8 +714,8 @@ static int spi_add_device_locked(struct spi_device *spi)
|
||||
struct device *dev = ctlr->dev.parent;
|
||||
|
||||
/* Chipselects are numbered 0..max; validate. */
|
||||
if (spi->chip_select >= ctlr->num_chipselect) {
|
||||
dev_err(dev, "cs%d >= max %d\n", spi->chip_select,
|
||||
if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
|
||||
dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
|
||||
ctlr->num_chipselect);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -749,7 +761,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
|
||||
|
||||
WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
|
||||
|
||||
proxy->chip_select = chip->chip_select;
|
||||
spi_set_chipselect(proxy, 0, chip->chip_select);
|
||||
proxy->max_speed_hz = chip->max_speed_hz;
|
||||
proxy->mode = chip->mode;
|
||||
proxy->irq = chip->irq;
|
||||
@ -958,24 +970,23 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
||||
* Avoid calling into the driver (or doing delays) if the chip select
|
||||
* isn't actually changing from the last time this was called.
|
||||
*/
|
||||
if (!force && ((enable && spi->controller->last_cs == spi->chip_select) ||
|
||||
(!enable && spi->controller->last_cs != spi->chip_select)) &&
|
||||
if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) ||
|
||||
(!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) &&
|
||||
(spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
|
||||
return;
|
||||
|
||||
trace_spi_set_cs(spi, activate);
|
||||
|
||||
spi->controller->last_cs = enable ? spi->chip_select : -1;
|
||||
spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
|
||||
spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
|
||||
|
||||
if ((spi->cs_gpiod || !spi->controller->set_cs_timing) && !activate) {
|
||||
if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate)
|
||||
spi_delay_exec(&spi->cs_hold, NULL);
|
||||
}
|
||||
|
||||
if (spi->mode & SPI_CS_HIGH)
|
||||
enable = !enable;
|
||||
|
||||
if (spi->cs_gpiod) {
|
||||
if (spi_get_csgpiod(spi, 0)) {
|
||||
if (!(spi->mode & SPI_NO_CS)) {
|
||||
/*
|
||||
* Historically ACPI has no means of the GPIO polarity and
|
||||
@ -988,10 +999,10 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
||||
* into account.
|
||||
*/
|
||||
if (has_acpi_companion(&spi->dev))
|
||||
gpiod_set_value_cansleep(spi->cs_gpiod, !enable);
|
||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
|
||||
else
|
||||
/* Polarity handled by GPIO library */
|
||||
gpiod_set_value_cansleep(spi->cs_gpiod, activate);
|
||||
gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
|
||||
}
|
||||
/* Some SPI masters need both GPIO CS & slave_select */
|
||||
if ((spi->controller->flags & SPI_MASTER_GPIO_SS) &&
|
||||
@ -1001,7 +1012,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
|
||||
spi->controller->set_cs(spi, !enable);
|
||||
}
|
||||
|
||||
if (spi->cs_gpiod || !spi->controller->set_cs_timing) {
|
||||
if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
|
||||
if (activate)
|
||||
spi_delay_exec(&spi->cs_setup, NULL);
|
||||
else
|
||||
@ -2291,7 +2302,7 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
|
||||
nc, rc);
|
||||
return rc;
|
||||
}
|
||||
spi->chip_select = value;
|
||||
spi_set_chipselect(spi, 0, value);
|
||||
|
||||
/* Device speed */
|
||||
if (!of_property_read_u32(nc, "spi-max-frequency", &value))
|
||||
@ -2405,7 +2416,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
|
||||
strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
|
||||
|
||||
/* Use provided chip-select for ancillary device */
|
||||
ancillary->chip_select = chip_select;
|
||||
spi_set_chipselect(ancillary, 0, chip_select);
|
||||
|
||||
/* Take over SPI mode/speed from SPI main device */
|
||||
ancillary->max_speed_hz = spi->max_speed_hz;
|
||||
@ -2652,7 +2663,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
|
||||
spi->mode |= lookup.mode;
|
||||
spi->irq = lookup.irq;
|
||||
spi->bits_per_word = lookup.bits_per_word;
|
||||
spi->chip_select = lookup.chip_select;
|
||||
spi_set_chipselect(spi, 0, lookup.chip_select);
|
||||
|
||||
return spi;
|
||||
}
|
||||
@ -3611,6 +3622,37 @@ static int __spi_validate_bits_per_word(struct spi_controller *ctlr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_set_cs_timing - configure CS setup, hold, and inactive delays
|
||||
* @spi: the device that requires specific CS timing configuration
|
||||
*
|
||||
* Return: zero on success, else a negative error code.
|
||||
*/
|
||||
static int spi_set_cs_timing(struct spi_device *spi)
|
||||
{
|
||||
struct device *parent = spi->controller->dev.parent;
|
||||
int status = 0;
|
||||
|
||||
if (spi->controller->set_cs_timing && !spi_get_csgpiod(spi, 0)) {
|
||||
if (spi->controller->auto_runtime_pm) {
|
||||
status = pm_runtime_get_sync(parent);
|
||||
if (status < 0) {
|
||||
pm_runtime_put_noidle(parent);
|
||||
dev_err(&spi->controller->dev, "Failed to power device: %d\n",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = spi->controller->set_cs_timing(spi);
|
||||
pm_runtime_mark_last_busy(parent);
|
||||
pm_runtime_put_autosuspend(parent);
|
||||
} else {
|
||||
status = spi->controller->set_cs_timing(spi);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_setup - setup SPI mode and clock rate
|
||||
* @spi: the device whose settings are being modified
|
||||
@ -3707,6 +3749,12 @@ int spi_setup(struct spi_device *spi)
|
||||
}
|
||||
}
|
||||
|
||||
status = spi_set_cs_timing(spi);
|
||||
if (status) {
|
||||
mutex_unlock(&spi->controller->io_mutex);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (spi->controller->auto_runtime_pm && spi->controller->set_cs) {
|
||||
status = pm_runtime_resume_and_get(spi->controller->dev.parent);
|
||||
if (status < 0) {
|
||||
@ -3790,7 +3838,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
|
||||
* cs_change is set for each transfer.
|
||||
*/
|
||||
if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
|
||||
spi->cs_gpiod)) {
|
||||
spi_get_csgpiod(spi, 0))) {
|
||||
size_t maxsize;
|
||||
int ret;
|
||||
|
||||
|
@ -429,8 +429,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
|
||||
temp = size;
|
||||
size -= temp;
|
||||
next += temp;
|
||||
if (temp == size)
|
||||
goto done;
|
||||
}
|
||||
|
||||
temp = snprintf(next, size, "\n");
|
||||
@ -440,7 +438,6 @@ static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh,
|
||||
size -= temp;
|
||||
next += temp;
|
||||
|
||||
done:
|
||||
*sizep = size;
|
||||
*nextp = next;
|
||||
}
|
||||
|
125
fs/namei.c
125
fs/namei.c
@ -253,6 +253,7 @@ getname_kernel(const char * filename)
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(getname_kernel);
|
||||
|
||||
void putname(struct filename *name)
|
||||
{
|
||||
@ -270,6 +271,7 @@ void putname(struct filename *name)
|
||||
} else
|
||||
__putname(name);
|
||||
}
|
||||
EXPORT_SYMBOL(putname);
|
||||
|
||||
/**
|
||||
* check_acl - perform ACL permission checking
|
||||
@ -1580,8 +1582,9 @@ static struct dentry *lookup_dcache(const struct qstr *name,
|
||||
* when directory is guaranteed to have no in-lookup children
|
||||
* at all.
|
||||
*/
|
||||
static struct dentry *__lookup_hash(const struct qstr *name,
|
||||
struct dentry *base, unsigned int flags)
|
||||
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
|
||||
struct dentry *base,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct dentry *dentry = lookup_dcache(name, base, flags);
|
||||
struct dentry *old;
|
||||
@ -1605,6 +1608,7 @@ static struct dentry *__lookup_hash(const struct qstr *name,
|
||||
}
|
||||
return dentry;
|
||||
}
|
||||
EXPORT_SYMBOL(lookup_one_qstr_excl);
|
||||
|
||||
static struct dentry *lookup_fast(struct nameidata *nd)
|
||||
{
|
||||
@ -2531,16 +2535,17 @@ static int path_parentat(struct nameidata *nd, unsigned flags,
|
||||
}
|
||||
|
||||
/* Note: this does not consume "name" */
|
||||
static int filename_parentat(int dfd, struct filename *name,
|
||||
unsigned int flags, struct path *parent,
|
||||
struct qstr *last, int *type)
|
||||
static int __filename_parentat(int dfd, struct filename *name,
|
||||
unsigned int flags, struct path *parent,
|
||||
struct qstr *last, int *type,
|
||||
const struct path *root)
|
||||
{
|
||||
int retval;
|
||||
struct nameidata nd;
|
||||
|
||||
if (IS_ERR(name))
|
||||
return PTR_ERR(name);
|
||||
set_nameidata(&nd, dfd, name, NULL);
|
||||
set_nameidata(&nd, dfd, name, root);
|
||||
retval = path_parentat(&nd, flags | LOOKUP_RCU, parent);
|
||||
if (unlikely(retval == -ECHILD))
|
||||
retval = path_parentat(&nd, flags, parent);
|
||||
@ -2555,6 +2560,13 @@ static int filename_parentat(int dfd, struct filename *name,
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int filename_parentat(int dfd, struct filename *name,
|
||||
unsigned int flags, struct path *parent,
|
||||
struct qstr *last, int *type)
|
||||
{
|
||||
return __filename_parentat(dfd, name, flags, parent, last, type, NULL);
|
||||
}
|
||||
|
||||
/* does lookup, returns the object with parent locked */
|
||||
static struct dentry *__kern_path_locked(struct filename *name, struct path *path)
|
||||
{
|
||||
@ -2570,7 +2582,7 @@ static struct dentry *__kern_path_locked(struct filename *name, struct path *pat
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
|
||||
d = __lookup_hash(&last, path->dentry, 0);
|
||||
d = lookup_one_qstr_excl(&last, path->dentry, 0);
|
||||
if (IS_ERR(d)) {
|
||||
inode_unlock(path->dentry->d_inode);
|
||||
path_put(path);
|
||||
@ -2598,6 +2610,24 @@ int kern_path(const char *name, unsigned int flags, struct path *path)
|
||||
}
|
||||
EXPORT_SYMBOL(kern_path);
|
||||
|
||||
/**
|
||||
* vfs_path_parent_lookup - lookup a parent path relative to a dentry-vfsmount pair
|
||||
* @filename: filename structure
|
||||
* @flags: lookup flags
|
||||
* @parent: pointer to struct path to fill
|
||||
* @last: last component
|
||||
* @type: type of the last component
|
||||
* @root: pointer to struct path of the base directory
|
||||
*/
|
||||
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
|
||||
struct path *parent, struct qstr *last, int *type,
|
||||
const struct path *root)
|
||||
{
|
||||
return __filename_parentat(AT_FDCWD, filename, flags, parent, last,
|
||||
type, root);
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_path_parent_lookup);
|
||||
|
||||
/**
|
||||
* vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair
|
||||
* @dentry: pointer to dentry of the base directory
|
||||
@ -2979,20 +3009,10 @@ static inline int may_create(struct user_namespace *mnt_userns,
|
||||
return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
|
||||
}
|
||||
|
||||
/*
|
||||
* p1 and p2 should be directories on the same fs.
|
||||
*/
|
||||
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
||||
static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
|
||||
{
|
||||
struct dentry *p;
|
||||
|
||||
if (p1 == p2) {
|
||||
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
|
||||
|
||||
p = d_ancestor(p2, p1);
|
||||
if (p) {
|
||||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
|
||||
@ -3011,8 +3031,64 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
||||
I_MUTEX_PARENT, I_MUTEX_PARENT2);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* p1 and p2 should be directories on the same fs.
|
||||
*/
|
||||
struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
|
||||
{
|
||||
if (p1 == p2) {
|
||||
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
|
||||
return lock_two_directories(p1, p2);
|
||||
}
|
||||
EXPORT_SYMBOL(lock_rename);
|
||||
|
||||
/*
|
||||
* c1 and p2 should be on the same fs.
|
||||
*/
|
||||
struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
|
||||
{
|
||||
if (READ_ONCE(c1->d_parent) == p2) {
|
||||
/*
|
||||
* hopefully won't need to touch ->s_vfs_rename_mutex at all.
|
||||
*/
|
||||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
|
||||
/*
|
||||
* now that p2 is locked, nobody can move in or out of it,
|
||||
* so the test below is safe.
|
||||
*/
|
||||
if (likely(c1->d_parent == p2))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* c1 got moved out of p2 while we'd been taking locks;
|
||||
* unlock and fall back to slow case.
|
||||
*/
|
||||
inode_unlock(p2->d_inode);
|
||||
}
|
||||
|
||||
mutex_lock(&c1->d_sb->s_vfs_rename_mutex);
|
||||
/*
|
||||
* nobody can move out of any directories on this fs.
|
||||
*/
|
||||
if (likely(c1->d_parent != p2))
|
||||
return lock_two_directories(c1->d_parent, p2);
|
||||
|
||||
/*
|
||||
* c1 got moved into p2 while we were taking locks;
|
||||
* we need p2 locked and ->s_vfs_rename_mutex unlocked,
|
||||
* for consistency with lock_rename().
|
||||
*/
|
||||
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT);
|
||||
mutex_unlock(&c1->d_sb->s_vfs_rename_mutex);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(lock_rename_child);
|
||||
|
||||
void unlock_rename(struct dentry *p1, struct dentry *p2)
|
||||
{
|
||||
inode_unlock(p1->d_inode);
|
||||
@ -3805,7 +3881,8 @@ static struct dentry *filename_create(int dfd, struct filename *name,
|
||||
if (last.name[last.len] && !want_dir)
|
||||
create_flags = 0;
|
||||
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&last, path->dentry, reval_flag | create_flags);
|
||||
dentry = lookup_one_qstr_excl(&last, path->dentry,
|
||||
reval_flag | create_flags);
|
||||
if (IS_ERR(dentry))
|
||||
goto unlock;
|
||||
|
||||
@ -4167,7 +4244,7 @@ int do_rmdir(int dfd, struct filename *name)
|
||||
goto exit2;
|
||||
|
||||
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&last, path.dentry, lookup_flags);
|
||||
dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
|
||||
error = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry))
|
||||
goto exit3;
|
||||
@ -4301,7 +4378,7 @@ int do_unlinkat(int dfd, struct filename *name)
|
||||
goto exit2;
|
||||
retry_deleg:
|
||||
inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&last, path.dentry, lookup_flags);
|
||||
dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
|
||||
error = PTR_ERR(dentry);
|
||||
if (!IS_ERR(dentry)) {
|
||||
struct user_namespace *mnt_userns;
|
||||
@ -4875,7 +4952,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
||||
retry_deleg:
|
||||
trap = lock_rename(new_path.dentry, old_path.dentry);
|
||||
|
||||
old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags);
|
||||
old_dentry = lookup_one_qstr_excl(&old_last, old_path.dentry,
|
||||
lookup_flags);
|
||||
error = PTR_ERR(old_dentry);
|
||||
if (IS_ERR(old_dentry))
|
||||
goto exit3;
|
||||
@ -4883,7 +4961,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
|
||||
error = -ENOENT;
|
||||
if (d_is_negative(old_dentry))
|
||||
goto exit4;
|
||||
new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags);
|
||||
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
|
||||
lookup_flags | target_flags);
|
||||
error = PTR_ERR(new_dentry);
|
||||
if (IS_ERR(new_dentry))
|
||||
goto exit4;
|
||||
|
@ -716,8 +716,10 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred
|
||||
|
||||
err = svc_addsock(nn->nfsd_serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred);
|
||||
|
||||
if (err >= 0 &&
|
||||
!nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
|
||||
if (err < 0 && !nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
|
||||
nfsd_last_thread(net);
|
||||
else if (err >= 0 &&
|
||||
!nn->nfsd_serv->sv_nrthreads && !xchg(&nn->keep_active, 1))
|
||||
svc_get(nn->nfsd_serv);
|
||||
|
||||
nfsd_put(net);
|
||||
@ -767,6 +769,9 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cr
|
||||
svc_xprt_put(xprt);
|
||||
}
|
||||
out_err:
|
||||
if (!nn->nfsd_serv->sv_nrthreads && !nn->keep_active)
|
||||
nfsd_last_thread(net);
|
||||
|
||||
nfsd_put(net);
|
||||
return err;
|
||||
}
|
||||
|
@ -97,7 +97,12 @@ int nfsd_pool_stats_open(struct inode *, struct file *);
|
||||
int nfsd_pool_stats_release(struct inode *, struct file *);
|
||||
void nfsd_shutdown_threads(struct net *net);
|
||||
|
||||
void nfsd_put(struct net *net);
|
||||
static inline void nfsd_put(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
svc_put(nn->nfsd_serv);
|
||||
}
|
||||
|
||||
bool i_am_nfsd(void);
|
||||
|
||||
@ -134,6 +139,7 @@ int nfsd_vers(struct nfsd_net *nn, int vers, enum vers_op change);
|
||||
int nfsd_minorversion(struct nfsd_net *nn, u32 minorversion, enum vers_op change);
|
||||
void nfsd_reset_versions(struct nfsd_net *nn);
|
||||
int nfsd_create_serv(struct net *net);
|
||||
void nfsd_last_thread(struct net *net);
|
||||
|
||||
extern int nfsd_max_blksize;
|
||||
|
||||
|
@ -523,9 +523,14 @@ static struct notifier_block nfsd_inet6addr_notifier = {
|
||||
/* Only used under nfsd_mutex, so this atomic may be overkill: */
|
||||
static atomic_t nfsd_notifier_refcount = ATOMIC_INIT(0);
|
||||
|
||||
static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
|
||||
void nfsd_last_thread(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv = nn->nfsd_serv;
|
||||
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
nn->nfsd_serv = NULL;
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
|
||||
/* check if the notifier still has clients */
|
||||
if (atomic_dec_return(&nfsd_notifier_refcount) == 0) {
|
||||
@ -535,6 +540,8 @@ static void nfsd_last_thread(struct svc_serv *serv, struct net *net)
|
||||
#endif
|
||||
}
|
||||
|
||||
svc_xprt_destroy_all(serv, net);
|
||||
|
||||
/*
|
||||
* write_ports can create the server without actually starting
|
||||
* any threads--if we get shut down before any threads are
|
||||
@ -625,7 +632,8 @@ void nfsd_shutdown_threads(struct net *net)
|
||||
svc_get(serv);
|
||||
/* Kill outstanding nfsd threads */
|
||||
svc_set_num_threads(serv, NULL, 0);
|
||||
nfsd_put(net);
|
||||
nfsd_last_thread(net);
|
||||
svc_put(serv);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
}
|
||||
|
||||
@ -655,9 +663,6 @@ int nfsd_create_serv(struct net *net)
|
||||
serv->sv_maxconn = nn->max_connections;
|
||||
error = svc_bind(serv, net);
|
||||
if (error < 0) {
|
||||
/* NOT nfsd_put() as notifiers (see below) haven't
|
||||
* been set up yet.
|
||||
*/
|
||||
svc_put(serv);
|
||||
return error;
|
||||
}
|
||||
@ -700,29 +705,6 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is the callback for kref_put() below.
|
||||
* There is no code here as the first thing to be done is
|
||||
* call svc_shutdown_net(), but we cannot get the 'net' from
|
||||
* the kref. So do all the work when kref_put returns true.
|
||||
*/
|
||||
static void nfsd_noop(struct kref *ref)
|
||||
{
|
||||
}
|
||||
|
||||
void nfsd_put(struct net *net)
|
||||
{
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
|
||||
if (kref_put(&nn->nfsd_serv->sv_refcnt, nfsd_noop)) {
|
||||
svc_xprt_destroy_all(nn->nfsd_serv, net);
|
||||
nfsd_last_thread(nn->nfsd_serv, net);
|
||||
svc_destroy(&nn->nfsd_serv->sv_refcnt);
|
||||
spin_lock(&nfsd_notifier_lock);
|
||||
nn->nfsd_serv = NULL;
|
||||
spin_unlock(&nfsd_notifier_lock);
|
||||
}
|
||||
}
|
||||
|
||||
int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
|
||||
{
|
||||
int i = 0;
|
||||
@ -773,7 +755,7 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
nfsd_put(net);
|
||||
svc_put(nn->nfsd_serv);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -788,6 +770,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
|
||||
int error;
|
||||
bool nfsd_up_before;
|
||||
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
|
||||
struct svc_serv *serv;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
dprintk("nfsd: creating service\n");
|
||||
@ -807,22 +790,25 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred)
|
||||
goto out;
|
||||
|
||||
nfsd_up_before = nn->nfsd_net_up;
|
||||
serv = nn->nfsd_serv;
|
||||
|
||||
error = nfsd_startup_net(net, cred);
|
||||
if (error)
|
||||
goto out_put;
|
||||
error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs);
|
||||
error = svc_set_num_threads(serv, NULL, nrservs);
|
||||
if (error)
|
||||
goto out_shutdown;
|
||||
error = nn->nfsd_serv->sv_nrthreads;
|
||||
error = serv->sv_nrthreads;
|
||||
if (error == 0)
|
||||
nfsd_last_thread(net);
|
||||
out_shutdown:
|
||||
if (error < 0 && !nfsd_up_before)
|
||||
nfsd_shutdown_net(net);
|
||||
out_put:
|
||||
/* Threads now hold service active */
|
||||
if (xchg(&nn->keep_active, 0))
|
||||
nfsd_put(net);
|
||||
nfsd_put(net);
|
||||
svc_put(serv);
|
||||
svc_put(serv);
|
||||
out:
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return error;
|
||||
@ -1138,11 +1124,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file)
|
||||
|
||||
int nfsd_pool_stats_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct svc_serv *serv = seq->private;
|
||||
int ret = seq_release(inode, file);
|
||||
struct net *net = inode->i_sb->s_fs_info;
|
||||
|
||||
mutex_lock(&nfsd_mutex);
|
||||
nfsd_put(net);
|
||||
svc_put(serv);
|
||||
mutex_unlock(&nfsd_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1196,6 +1196,7 @@ struct create_posix {
|
||||
#define SMB2_LEASE_WRITE_CACHING_LE cpu_to_le32(0x04)
|
||||
|
||||
#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE cpu_to_le32(0x02)
|
||||
#define SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE cpu_to_le32(0x04)
|
||||
|
||||
#define SMB2_LEASE_KEY_SIZE 16
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
config SMB_SERVER
|
||||
tristate "SMB3 server support (EXPERIMENTAL)"
|
||||
tristate "SMB3 server support"
|
||||
depends on INET
|
||||
depends on MULTIUSER
|
||||
depends on FILE_LOCKING
|
||||
@ -33,14 +33,16 @@ config SMB_SERVER
|
||||
in ksmbd-tools, available from
|
||||
https://github.com/cifsd-team/ksmbd-tools.
|
||||
More detail about how to run the ksmbd kernel server is
|
||||
available via README file
|
||||
available via the README file
|
||||
(https://github.com/cifsd-team/ksmbd-tools/blob/master/README).
|
||||
|
||||
ksmbd kernel server includes support for auto-negotiation,
|
||||
Secure negotiate, Pre-authentication integrity, oplock/lease,
|
||||
compound requests, multi-credit, packet signing, RDMA(smbdirect),
|
||||
smb3 encryption, copy-offload, secure per-user session
|
||||
establishment via NTLM or NTLMv2.
|
||||
establishment via Kerberos or NTLMv2.
|
||||
|
||||
if SMB_SERVER
|
||||
|
||||
config SMB_SERVER_SMBDIRECT
|
||||
bool "Support for SMB Direct protocol"
|
||||
@ -54,6 +56,8 @@ config SMB_SERVER_SMBDIRECT
|
||||
SMB Direct allows transferring SMB packets over RDMA. If unsure,
|
||||
say N.
|
||||
|
||||
endif
|
||||
|
||||
config SMB_SERVER_CHECK_CAP_NET_ADMIN
|
||||
bool "Enable check network administration capability"
|
||||
depends on SMB_SERVER
|
||||
|
@ -208,32 +208,29 @@ int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksmbd_neg_token_alloc(void *context, size_t hdrlen,
|
||||
unsigned char tag, const void *value,
|
||||
size_t vlen)
|
||||
{
|
||||
struct ksmbd_conn *conn = context;
|
||||
|
||||
conn->mechToken = kmemdup_nul(value, vlen, GFP_KERNEL);
|
||||
if (!conn->mechToken)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
|
||||
unsigned char tag, const void *value,
|
||||
size_t vlen)
|
||||
{
|
||||
struct ksmbd_conn *conn = context;
|
||||
|
||||
conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
|
||||
if (!conn->mechToken)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(conn->mechToken, value, vlen);
|
||||
conn->mechToken[vlen] = '\0';
|
||||
return 0;
|
||||
return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
|
||||
}
|
||||
|
||||
int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen,
|
||||
unsigned char tag, const void *value,
|
||||
size_t vlen)
|
||||
{
|
||||
struct ksmbd_conn *conn = context;
|
||||
|
||||
conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
|
||||
if (!conn->mechToken)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(conn->mechToken, value, vlen);
|
||||
conn->mechToken[vlen] = '\0';
|
||||
return 0;
|
||||
return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
|
||||
}
|
||||
|
@ -1032,11 +1032,15 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
unsigned int assoc_data_len = sizeof(struct smb2_transform_hdr) - 20;
|
||||
int i, nr_entries[3] = {0}, total_entries = 0, sg_idx = 0;
|
||||
int i, *nr_entries, total_entries = 0, sg_idx = 0;
|
||||
|
||||
if (!nvec)
|
||||
return NULL;
|
||||
|
||||
nr_entries = kcalloc(nvec, sizeof(int), GFP_KERNEL);
|
||||
if (!nr_entries)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < nvec - 1; i++) {
|
||||
unsigned long kaddr = (unsigned long)iov[i + 1].iov_base;
|
||||
|
||||
@ -1054,8 +1058,10 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
|
||||
total_entries += 2;
|
||||
|
||||
sg = kmalloc_array(total_entries, sizeof(struct scatterlist), GFP_KERNEL);
|
||||
if (!sg)
|
||||
if (!sg) {
|
||||
kfree(nr_entries);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sg_init_table(sg, total_entries);
|
||||
smb2_sg_set_buf(&sg[sg_idx++], iov[0].iov_base + 24, assoc_data_len);
|
||||
@ -1089,6 +1095,7 @@ static struct scatterlist *ksmbd_init_sg(struct kvec *iov, unsigned int nvec,
|
||||
}
|
||||
}
|
||||
smb2_sg_set_buf(&sg[sg_idx], sign, SMB2_SIGNATURE_SIZE);
|
||||
kfree(nr_entries);
|
||||
return sg;
|
||||
}
|
||||
|
||||
|
@ -114,10 +114,8 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct list_head *requests_queue = NULL;
|
||||
|
||||
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
|
||||
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE)
|
||||
requests_queue = &conn->requests;
|
||||
work->syncronous = true;
|
||||
}
|
||||
|
||||
if (requests_queue) {
|
||||
atomic_inc(&conn->req_running);
|
||||
@ -127,28 +125,22 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
|
||||
}
|
||||
}
|
||||
|
||||
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
|
||||
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
int ret = 1;
|
||||
|
||||
if (list_empty(&work->request_entry) &&
|
||||
list_empty(&work->async_request_entry))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
if (!work->multiRsp)
|
||||
atomic_dec(&conn->req_running);
|
||||
atomic_dec(&conn->req_running);
|
||||
spin_lock(&conn->request_lock);
|
||||
if (!work->multiRsp) {
|
||||
list_del_init(&work->request_entry);
|
||||
if (work->syncronous == false)
|
||||
list_del_init(&work->async_request_entry);
|
||||
ret = 0;
|
||||
}
|
||||
list_del_init(&work->request_entry);
|
||||
spin_unlock(&conn->request_lock);
|
||||
if (work->asynchronous)
|
||||
release_async_work(work);
|
||||
|
||||
wake_up_all(&conn->req_running_q);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ksmbd_conn_lock(struct ksmbd_conn *conn)
|
||||
@ -175,63 +167,31 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
|
||||
|
||||
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
|
||||
{
|
||||
struct ksmbd_conn *bind_conn;
|
||||
|
||||
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
|
||||
|
||||
down_read(&conn_list_lock);
|
||||
list_for_each_entry(bind_conn, &conn_list, conns_list) {
|
||||
if (bind_conn == conn)
|
||||
continue;
|
||||
|
||||
if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) &&
|
||||
!ksmbd_conn_releasing(bind_conn) &&
|
||||
atomic_read(&bind_conn->req_running)) {
|
||||
wait_event(bind_conn->req_running_q,
|
||||
atomic_read(&bind_conn->req_running) == 0);
|
||||
}
|
||||
}
|
||||
up_read(&conn_list_lock);
|
||||
}
|
||||
|
||||
int ksmbd_conn_write(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
size_t len = 0;
|
||||
int sent;
|
||||
struct kvec iov[3];
|
||||
int iov_idx = 0;
|
||||
|
||||
if (!work->response_buf) {
|
||||
pr_err("NULL response header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (work->tr_buf) {
|
||||
iov[iov_idx] = (struct kvec) { work->tr_buf,
|
||||
sizeof(struct smb2_transform_hdr) + 4 };
|
||||
len += iov[iov_idx++].iov_len;
|
||||
}
|
||||
if (work->send_no_response)
|
||||
return 0;
|
||||
|
||||
if (work->aux_payload_sz) {
|
||||
iov[iov_idx] = (struct kvec) { work->response_buf, work->resp_hdr_sz };
|
||||
len += iov[iov_idx++].iov_len;
|
||||
iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz };
|
||||
len += iov[iov_idx++].iov_len;
|
||||
} else {
|
||||
if (work->tr_buf)
|
||||
iov[iov_idx].iov_len = work->resp_hdr_sz;
|
||||
else
|
||||
iov[iov_idx].iov_len = get_rfc1002_len(work->response_buf) + 4;
|
||||
iov[iov_idx].iov_base = work->response_buf;
|
||||
len += iov[iov_idx++].iov_len;
|
||||
}
|
||||
if (!work->iov_idx)
|
||||
return -EINVAL;
|
||||
|
||||
ksmbd_conn_lock(conn);
|
||||
sent = conn->transport->ops->writev(conn->transport, &iov[0],
|
||||
iov_idx, len,
|
||||
work->need_invalidate_rkey,
|
||||
work->remote_key);
|
||||
sent = conn->transport->ops->writev(conn->transport, work->iov,
|
||||
work->iov_cnt,
|
||||
get_rfc1002_len(work->iov[0].iov_base) + 4,
|
||||
work->need_invalidate_rkey,
|
||||
work->remote_key);
|
||||
ksmbd_conn_unlock(conn);
|
||||
|
||||
if (sent < 0) {
|
||||
@ -345,7 +305,7 @@ int ksmbd_conn_handler_loop(void *p)
|
||||
max_allowed_pdu_size = SMB3_MAX_MSGSIZE;
|
||||
|
||||
if (pdu_size > max_allowed_pdu_size) {
|
||||
pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n",
|
||||
pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
|
||||
pdu_size, max_allowed_pdu_size,
|
||||
READ_ONCE(conn->status));
|
||||
break;
|
||||
|
@ -159,7 +159,7 @@ int ksmbd_conn_rdma_write(struct ksmbd_conn *conn,
|
||||
struct smb2_buffer_desc_v1 *desc,
|
||||
unsigned int desc_len);
|
||||
void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
|
||||
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
|
||||
void ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
|
||||
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
|
||||
int ksmbd_conn_handler_loop(void *p);
|
||||
int ksmbd_conn_transport_init(void);
|
||||
|
@ -74,6 +74,7 @@ struct ksmbd_heartbeat {
|
||||
#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0)
|
||||
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
|
||||
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
|
||||
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
|
||||
|
||||
/*
|
||||
* IPC request for ksmbd server startup
|
||||
@ -351,7 +352,8 @@ enum KSMBD_TREE_CONN_STATUS {
|
||||
#define KSMBD_SHARE_FLAG_STREAMS BIT(11)
|
||||
#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12)
|
||||
#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13)
|
||||
#define KSMBD_SHARE_FLAG_UPDATE BIT(14)
|
||||
#define KSMBD_SHARE_FLAG_UPDATE BIT(14)
|
||||
#define KSMBD_SHARE_FLAG_CROSSMNT BIT(15)
|
||||
|
||||
/*
|
||||
* Tree connect request flags.
|
||||
|
@ -27,18 +27,38 @@ struct ksmbd_work *ksmbd_alloc_work_struct(void)
|
||||
INIT_LIST_HEAD(&work->async_request_entry);
|
||||
INIT_LIST_HEAD(&work->fp_entry);
|
||||
INIT_LIST_HEAD(&work->interim_entry);
|
||||
INIT_LIST_HEAD(&work->aux_read_list);
|
||||
work->iov_alloc_cnt = 4;
|
||||
work->iov = kcalloc(work->iov_alloc_cnt, sizeof(struct kvec),
|
||||
GFP_KERNEL);
|
||||
if (!work->iov) {
|
||||
kmem_cache_free(work_cache, work);
|
||||
work = NULL;
|
||||
}
|
||||
}
|
||||
return work;
|
||||
}
|
||||
|
||||
void ksmbd_free_work_struct(struct ksmbd_work *work)
|
||||
{
|
||||
struct aux_read *ar, *tmp;
|
||||
|
||||
WARN_ON(work->saved_cred != NULL);
|
||||
|
||||
kvfree(work->response_buf);
|
||||
kvfree(work->aux_payload_buf);
|
||||
|
||||
list_for_each_entry_safe(ar, tmp, &work->aux_read_list, entry) {
|
||||
kvfree(ar->buf);
|
||||
list_del(&ar->entry);
|
||||
kfree(ar);
|
||||
}
|
||||
|
||||
kfree(work->tr_buf);
|
||||
kvfree(work->request_buf);
|
||||
kfree(work->iov);
|
||||
if (!list_empty(&work->interim_entry))
|
||||
list_del(&work->interim_entry);
|
||||
|
||||
if (work->async_id)
|
||||
ksmbd_release_id(&work->conn->async_ida, work->async_id);
|
||||
kmem_cache_free(work_cache, work);
|
||||
@ -77,3 +97,81 @@ bool ksmbd_queue_work(struct ksmbd_work *work)
|
||||
{
|
||||
return queue_work(ksmbd_wq, &work->work);
|
||||
}
|
||||
|
||||
static inline void __ksmbd_iov_pin(struct ksmbd_work *work, void *ib,
|
||||
unsigned int ib_len)
|
||||
{
|
||||
work->iov[++work->iov_idx].iov_base = ib;
|
||||
work->iov[work->iov_idx].iov_len = ib_len;
|
||||
work->iov_cnt++;
|
||||
}
|
||||
|
||||
static int __ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len,
|
||||
void *aux_buf, unsigned int aux_size)
|
||||
{
|
||||
struct aux_read *ar = NULL;
|
||||
int need_iov_cnt = 1;
|
||||
|
||||
if (aux_size) {
|
||||
need_iov_cnt++;
|
||||
ar = kmalloc(sizeof(struct aux_read), GFP_KERNEL);
|
||||
if (!ar)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (work->iov_alloc_cnt < work->iov_cnt + need_iov_cnt) {
|
||||
struct kvec *new;
|
||||
|
||||
work->iov_alloc_cnt += 4;
|
||||
new = krealloc(work->iov,
|
||||
sizeof(struct kvec) * work->iov_alloc_cnt,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (!new) {
|
||||
kfree(ar);
|
||||
work->iov_alloc_cnt -= 4;
|
||||
return -ENOMEM;
|
||||
}
|
||||
work->iov = new;
|
||||
}
|
||||
|
||||
/* Plus rfc_length size on first iov */
|
||||
if (!work->iov_idx) {
|
||||
work->iov[work->iov_idx].iov_base = work->response_buf;
|
||||
*(__be32 *)work->iov[0].iov_base = 0;
|
||||
work->iov[work->iov_idx].iov_len = 4;
|
||||
work->iov_cnt++;
|
||||
}
|
||||
|
||||
__ksmbd_iov_pin(work, ib, len);
|
||||
inc_rfc1001_len(work->iov[0].iov_base, len);
|
||||
|
||||
if (aux_size) {
|
||||
__ksmbd_iov_pin(work, aux_buf, aux_size);
|
||||
inc_rfc1001_len(work->iov[0].iov_base, aux_size);
|
||||
|
||||
ar->buf = aux_buf;
|
||||
list_add(&ar->entry, &work->aux_read_list);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len)
|
||||
{
|
||||
return __ksmbd_iov_pin_rsp(work, ib, len, NULL, 0);
|
||||
}
|
||||
|
||||
int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
|
||||
void *aux_buf, unsigned int aux_size)
|
||||
{
|
||||
return __ksmbd_iov_pin_rsp(work, ib, len, aux_buf, aux_size);
|
||||
}
|
||||
|
||||
int allocate_interim_rsp_buf(struct ksmbd_work *work)
|
||||
{
|
||||
work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (!work->response_buf)
|
||||
return -ENOMEM;
|
||||
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
@ -19,6 +19,11 @@ enum {
|
||||
KSMBD_WORK_CLOSED,
|
||||
};
|
||||
|
||||
struct aux_read {
|
||||
void *buf;
|
||||
struct list_head entry;
|
||||
};
|
||||
|
||||
/* one of these for every pending CIFS request at the connection */
|
||||
struct ksmbd_work {
|
||||
/* Server corresponding to this mid */
|
||||
@ -31,13 +36,19 @@ struct ksmbd_work {
|
||||
/* Response buffer */
|
||||
void *response_buf;
|
||||
|
||||
/* Read data buffer */
|
||||
void *aux_payload_buf;
|
||||
struct list_head aux_read_list;
|
||||
|
||||
struct kvec *iov;
|
||||
int iov_alloc_cnt;
|
||||
int iov_cnt;
|
||||
int iov_idx;
|
||||
|
||||
/* Next cmd hdr in compound req buf*/
|
||||
int next_smb2_rcv_hdr_off;
|
||||
/* Next cmd hdr in compound rsp buf*/
|
||||
int next_smb2_rsp_hdr_off;
|
||||
/* Current cmd hdr in compound rsp buf*/
|
||||
int curr_smb2_rsp_hdr_off;
|
||||
|
||||
/*
|
||||
* Current Local FID assigned compound response if SMB2 CREATE
|
||||
@ -53,22 +64,17 @@ struct ksmbd_work {
|
||||
unsigned int credits_granted;
|
||||
|
||||
/* response smb header size */
|
||||
unsigned int resp_hdr_sz;
|
||||
unsigned int response_sz;
|
||||
/* Read data count */
|
||||
unsigned int aux_payload_sz;
|
||||
|
||||
void *tr_buf;
|
||||
|
||||
unsigned char state;
|
||||
/* Multiple responses for one request e.g. SMB ECHO */
|
||||
bool multiRsp:1;
|
||||
/* No response for cancelled request */
|
||||
bool send_no_response:1;
|
||||
/* Request is encrypted */
|
||||
bool encrypted:1;
|
||||
/* Is this SYNC or ASYNC ksmbd_work */
|
||||
bool syncronous:1;
|
||||
bool asynchronous:1;
|
||||
bool need_invalidate_rkey:1;
|
||||
|
||||
unsigned int remote_key;
|
||||
@ -95,6 +101,15 @@ static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work)
|
||||
return work->response_buf + work->next_smb2_rsp_hdr_off + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_resp_buf_curr - Get current buffer on compound response.
|
||||
* @work: smb work containing response buffer
|
||||
*/
|
||||
static inline void *ksmbd_resp_buf_curr(struct ksmbd_work *work)
|
||||
{
|
||||
return work->response_buf + work->curr_smb2_rsp_hdr_off + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_req_buf_next - Get next buffer on compound request.
|
||||
* @work: smb work containing response buffer
|
||||
@ -113,5 +128,8 @@ int ksmbd_work_pool_init(void);
|
||||
int ksmbd_workqueue_init(void);
|
||||
void ksmbd_workqueue_destroy(void);
|
||||
bool ksmbd_queue_work(struct ksmbd_work *work);
|
||||
|
||||
int ksmbd_iov_pin_rsp_read(struct ksmbd_work *work, void *ib, int len,
|
||||
void *aux_buf, unsigned int aux_size);
|
||||
int ksmbd_iov_pin_rsp(struct ksmbd_work *work, void *ib, int len);
|
||||
int allocate_interim_rsp_buf(struct ksmbd_work *work);
|
||||
#endif /* __KSMBD_WORK_H__ */
|
||||
|
@ -34,29 +34,22 @@ struct ksmbd_share_config {
|
||||
#define KSMBD_SHARE_INVALID_UID ((__u16)-1)
|
||||
#define KSMBD_SHARE_INVALID_GID ((__u16)-1)
|
||||
|
||||
static inline int share_config_create_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
static inline umode_t
|
||||
share_config_create_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
{
|
||||
if (!share->force_create_mode) {
|
||||
if (!posix_mode)
|
||||
return share->create_mask;
|
||||
else
|
||||
return posix_mode & share->create_mask;
|
||||
}
|
||||
return share->force_create_mode & share->create_mask;
|
||||
umode_t mode = (posix_mode ?: (umode_t)-1) & share->create_mask;
|
||||
|
||||
return mode | share->force_create_mode;
|
||||
}
|
||||
|
||||
static inline int share_config_directory_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
static inline umode_t
|
||||
share_config_directory_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
{
|
||||
if (!share->force_directory_mode) {
|
||||
if (!posix_mode)
|
||||
return share->directory_mask;
|
||||
else
|
||||
return posix_mode & share->directory_mask;
|
||||
}
|
||||
umode_t mode = (posix_mode ?: (umode_t)-1) & share->directory_mask;
|
||||
|
||||
return share->force_directory_mode & share->directory_mask;
|
||||
return mode | share->force_directory_mode;
|
||||
}
|
||||
|
||||
static inline int test_share_config_flag(struct ksmbd_share_config *share,
|
||||
|
@ -73,7 +73,10 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
|
||||
|
||||
tree_conn->user = sess->user;
|
||||
tree_conn->share_conf = sc;
|
||||
tree_conn->t_state = TREE_NEW;
|
||||
status.tree_conn = tree_conn;
|
||||
atomic_set(&tree_conn->refcount, 1);
|
||||
init_waitqueue_head(&tree_conn->refcount_q);
|
||||
|
||||
ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
|
||||
GFP_KERNEL));
|
||||
@ -93,14 +96,33 @@ ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
|
||||
return status;
|
||||
}
|
||||
|
||||
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon)
|
||||
{
|
||||
/*
|
||||
* Checking waitqueue to releasing tree connect on
|
||||
* tree disconnect. waitqueue_active is safe because it
|
||||
* uses atomic operation for condition.
|
||||
*/
|
||||
if (!atomic_dec_return(&tcon->refcount) &&
|
||||
waitqueue_active(&tcon->refcount_q))
|
||||
wake_up(&tcon->refcount_q);
|
||||
}
|
||||
|
||||
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *tree_conn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
write_lock(&sess->tree_conns_lock);
|
||||
xa_erase(&sess->tree_conns, tree_conn->id);
|
||||
write_unlock(&sess->tree_conns_lock);
|
||||
|
||||
if (!atomic_dec_and_test(&tree_conn->refcount))
|
||||
wait_event(tree_conn->refcount_q,
|
||||
atomic_read(&tree_conn->refcount) == 0);
|
||||
|
||||
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
|
||||
ksmbd_release_tree_conn_id(sess, tree_conn->id);
|
||||
xa_erase(&sess->tree_conns, tree_conn->id);
|
||||
ksmbd_share_config_put(tree_conn->share_conf);
|
||||
kfree(tree_conn);
|
||||
return ret;
|
||||
@ -111,26 +133,19 @@ struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
|
||||
{
|
||||
struct ksmbd_tree_connect *tcon;
|
||||
|
||||
read_lock(&sess->tree_conns_lock);
|
||||
tcon = xa_load(&sess->tree_conns, id);
|
||||
if (tcon) {
|
||||
if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
|
||||
if (tcon->t_state != TREE_CONNECTED)
|
||||
tcon = NULL;
|
||||
else if (!atomic_inc_not_zero(&tcon->refcount))
|
||||
tcon = NULL;
|
||||
}
|
||||
read_unlock(&sess->tree_conns_lock);
|
||||
|
||||
return tcon;
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
|
||||
unsigned int id)
|
||||
{
|
||||
struct ksmbd_tree_connect *tc;
|
||||
|
||||
tc = ksmbd_tree_conn_lookup(sess, id);
|
||||
if (tc)
|
||||
return tc->share_conf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -140,8 +155,18 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
|
||||
if (!sess)
|
||||
return -EINVAL;
|
||||
|
||||
xa_for_each(&sess->tree_conns, id, tc)
|
||||
xa_for_each(&sess->tree_conns, id, tc) {
|
||||
write_lock(&sess->tree_conns_lock);
|
||||
if (tc->t_state == TREE_DISCONNECTED) {
|
||||
write_unlock(&sess->tree_conns_lock);
|
||||
ret = -ENOENT;
|
||||
continue;
|
||||
}
|
||||
tc->t_state = TREE_DISCONNECTED;
|
||||
write_unlock(&sess->tree_conns_lock);
|
||||
|
||||
ret |= ksmbd_tree_conn_disconnect(sess, tc);
|
||||
}
|
||||
xa_destroy(&sess->tree_conns);
|
||||
return ret;
|
||||
}
|
||||
|
@ -14,7 +14,11 @@ struct ksmbd_share_config;
|
||||
struct ksmbd_user;
|
||||
struct ksmbd_conn;
|
||||
|
||||
#define TREE_CONN_EXPIRE 1
|
||||
enum {
|
||||
TREE_NEW = 0,
|
||||
TREE_CONNECTED,
|
||||
TREE_DISCONNECTED
|
||||
};
|
||||
|
||||
struct ksmbd_tree_connect {
|
||||
int id;
|
||||
@ -27,7 +31,9 @@ struct ksmbd_tree_connect {
|
||||
|
||||
int maximal_access;
|
||||
bool posix_extensions;
|
||||
unsigned long status;
|
||||
atomic_t refcount;
|
||||
wait_queue_head_t refcount_q;
|
||||
unsigned int t_state;
|
||||
};
|
||||
|
||||
struct ksmbd_tree_conn_status {
|
||||
@ -46,6 +52,7 @@ struct ksmbd_session;
|
||||
struct ksmbd_tree_conn_status
|
||||
ksmbd_tree_conn_connect(struct ksmbd_conn *conn, struct ksmbd_session *sess,
|
||||
const char *share_name);
|
||||
void ksmbd_tree_connect_put(struct ksmbd_tree_connect *tcon);
|
||||
|
||||
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *tree_conn);
|
||||
@ -53,9 +60,6 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
|
||||
unsigned int id);
|
||||
|
||||
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
|
||||
unsigned int id);
|
||||
|
||||
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess);
|
||||
|
||||
#endif /* __TREE_CONNECT_MANAGEMENT_H__ */
|
||||
|
@ -18,7 +18,6 @@ struct ksmbd_user {
|
||||
|
||||
size_t passkey_sz;
|
||||
char *passkey;
|
||||
unsigned int failed_login_count;
|
||||
};
|
||||
|
||||
static inline bool user_guest(struct ksmbd_user *user)
|
||||
|
@ -25,7 +25,6 @@ static DECLARE_RWSEM(sessions_table_lock);
|
||||
struct ksmbd_session_rpc {
|
||||
int id;
|
||||
unsigned int method;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static void free_channel_list(struct ksmbd_session *sess)
|
||||
@ -58,15 +57,14 @@ static void __session_rpc_close(struct ksmbd_session *sess,
|
||||
static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
long index;
|
||||
|
||||
while (!list_empty(&sess->rpc_handle_list)) {
|
||||
entry = list_entry(sess->rpc_handle_list.next,
|
||||
struct ksmbd_session_rpc,
|
||||
list);
|
||||
|
||||
list_del(&entry->list);
|
||||
xa_for_each(&sess->rpc_handle_list, index, entry) {
|
||||
xa_erase(&sess->rpc_handle_list, index);
|
||||
__session_rpc_close(sess, entry);
|
||||
}
|
||||
|
||||
xa_destroy(&sess->rpc_handle_list);
|
||||
}
|
||||
|
||||
static int __rpc_method(char *rpc_name)
|
||||
@ -102,13 +100,13 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
|
||||
|
||||
entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -EINVAL;
|
||||
return -ENOMEM;
|
||||
|
||||
list_add(&entry->list, &sess->rpc_handle_list);
|
||||
entry->method = method;
|
||||
entry->id = ksmbd_ipc_id_alloc();
|
||||
if (entry->id < 0)
|
||||
goto free_entry;
|
||||
xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL);
|
||||
|
||||
resp = ksmbd_rpc_open(sess, entry->id);
|
||||
if (!resp)
|
||||
@ -117,9 +115,9 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
|
||||
kvfree(resp);
|
||||
return entry->id;
|
||||
free_id:
|
||||
xa_erase(&sess->rpc_handle_list, entry->id);
|
||||
ksmbd_rpc_id_free(entry->id);
|
||||
free_entry:
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -128,24 +126,17 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
|
||||
if (entry->id == id) {
|
||||
list_del(&entry->list);
|
||||
__session_rpc_close(sess, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
entry = xa_erase(&sess->rpc_handle_list, id);
|
||||
if (entry)
|
||||
__session_rpc_close(sess, entry);
|
||||
}
|
||||
|
||||
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
|
||||
if (entry->id == id)
|
||||
return entry->method;
|
||||
}
|
||||
return 0;
|
||||
entry = xa_load(&sess->rpc_handle_list, id);
|
||||
return entry ? entry->method : 0;
|
||||
}
|
||||
|
||||
void ksmbd_session_destroy(struct ksmbd_session *sess)
|
||||
@ -362,8 +353,9 @@ static struct ksmbd_session *__session_create(int protocol)
|
||||
set_session_flag(sess, protocol);
|
||||
xa_init(&sess->tree_conns);
|
||||
xa_init(&sess->ksmbd_chann_list);
|
||||
INIT_LIST_HEAD(&sess->rpc_handle_list);
|
||||
xa_init(&sess->rpc_handle_list);
|
||||
sess->sequence_number = 1;
|
||||
rwlock_init(&sess->tree_conns_lock);
|
||||
|
||||
ret = __init_smb2_session(sess);
|
||||
if (ret)
|
||||
|
@ -52,7 +52,7 @@ struct ksmbd_session {
|
||||
struct xarray ksmbd_chann_list;
|
||||
struct xarray tree_conns;
|
||||
struct ida tree_conn_ida;
|
||||
struct list_head rpc_handle_list;
|
||||
struct xarray rpc_handle_list;
|
||||
|
||||
__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
|
||||
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
|
||||
@ -60,6 +60,7 @@ struct ksmbd_session {
|
||||
|
||||
struct ksmbd_file_table file_table;
|
||||
unsigned long last_active;
|
||||
rwlock_t tree_conns_lock;
|
||||
};
|
||||
|
||||
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
|
||||
|
@ -102,9 +102,10 @@ static int alloc_lease(struct oplock_info *opinfo, struct lease_ctx_info *lctx)
|
||||
lease->new_state = 0;
|
||||
lease->flags = lctx->flags;
|
||||
lease->duration = lctx->duration;
|
||||
lease->is_dir = lctx->is_dir;
|
||||
memcpy(lease->parent_lease_key, lctx->parent_lease_key, SMB2_LEASE_KEY_SIZE);
|
||||
lease->version = lctx->version;
|
||||
lease->epoch = 0;
|
||||
lease->epoch = le16_to_cpu(lctx->epoch);
|
||||
INIT_LIST_HEAD(&opinfo->lease_entry);
|
||||
opinfo->o_lease = lease;
|
||||
|
||||
@ -395,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp)
|
||||
{
|
||||
struct oplock_info *opinfo;
|
||||
|
||||
if (S_ISDIR(file_inode(fp->filp)->i_mode))
|
||||
return;
|
||||
if (fp->reserve_lease_break)
|
||||
smb_lazy_parent_lease_break_close(fp);
|
||||
|
||||
opinfo = opinfo_get(fp);
|
||||
if (!opinfo)
|
||||
@ -543,12 +544,13 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
|
||||
/* upgrading lease */
|
||||
if ((atomic_read(&ci->op_count) +
|
||||
atomic_read(&ci->sop_count)) == 1) {
|
||||
if (lease->state ==
|
||||
(lctx->req_state & lease->state)) {
|
||||
if (lease->state != SMB2_LEASE_NONE_LE &&
|
||||
lease->state == (lctx->req_state & lease->state)) {
|
||||
lease->state |= lctx->req_state;
|
||||
if (lctx->req_state &
|
||||
SMB2_LEASE_WRITE_CACHING_LE)
|
||||
lease_read_to_write(opinfo);
|
||||
|
||||
}
|
||||
} else if ((atomic_read(&ci->op_count) +
|
||||
atomic_read(&ci->sop_count)) > 1) {
|
||||
@ -616,15 +618,6 @@ static int oplock_break_pending(struct oplock_info *opinfo, int req_op_level)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int allocate_oplock_break_buf(struct ksmbd_work *work)
|
||||
{
|
||||
work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE, GFP_KERNEL);
|
||||
if (!work->response_buf)
|
||||
return -ENOMEM;
|
||||
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __smb2_oplock_break_noti() - send smb2 oplock break cmd from conn
|
||||
* to client
|
||||
@ -639,7 +632,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
|
||||
{
|
||||
struct smb2_oplock_break *rsp = NULL;
|
||||
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct oplock_break_info *br_info = work->request_buf;
|
||||
struct smb2_hdr *rsp_hdr;
|
||||
struct ksmbd_file *fp;
|
||||
@ -648,7 +640,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
|
||||
if (!fp)
|
||||
goto out;
|
||||
|
||||
if (allocate_oplock_break_buf(work)) {
|
||||
if (allocate_interim_rsp_buf(work)) {
|
||||
pr_err("smb2_allocate_rsp_buf failed! ");
|
||||
ksmbd_fd_put(work, fp);
|
||||
goto out;
|
||||
@ -656,8 +648,6 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
|
||||
|
||||
rsp_hdr = smb2_get_msg(work->response_buf);
|
||||
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
|
||||
*(__be32 *)work->response_buf =
|
||||
cpu_to_be32(conn->vals->header_size);
|
||||
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
|
||||
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
|
||||
rsp_hdr->CreditRequest = cpu_to_le16(0);
|
||||
@ -684,13 +674,15 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
|
||||
rsp->PersistentFid = fp->persistent_id;
|
||||
rsp->VolatileFid = fp->volatile_id;
|
||||
|
||||
inc_rfc1001_len(work->response_buf, 24);
|
||||
ksmbd_fd_put(work, fp);
|
||||
if (ksmbd_iov_pin_rsp(work, (void *)rsp,
|
||||
sizeof(struct smb2_oplock_break)))
|
||||
goto out;
|
||||
|
||||
ksmbd_debug(OPLOCK,
|
||||
"sending oplock break v_id %llu p_id = %llu lock level = %d\n",
|
||||
rsp->VolatileFid, rsp->PersistentFid, rsp->OplockLevel);
|
||||
|
||||
ksmbd_fd_put(work, fp);
|
||||
ksmbd_conn_write(work);
|
||||
|
||||
out:
|
||||
@ -751,18 +743,15 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
|
||||
struct smb2_lease_break *rsp = NULL;
|
||||
struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);
|
||||
struct lease_break_info *br_info = work->request_buf;
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct smb2_hdr *rsp_hdr;
|
||||
|
||||
if (allocate_oplock_break_buf(work)) {
|
||||
if (allocate_interim_rsp_buf(work)) {
|
||||
ksmbd_debug(OPLOCK, "smb2_allocate_rsp_buf failed! ");
|
||||
goto out;
|
||||
}
|
||||
|
||||
rsp_hdr = smb2_get_msg(work->response_buf);
|
||||
memset(rsp_hdr, 0, sizeof(struct smb2_hdr) + 2);
|
||||
*(__be32 *)work->response_buf =
|
||||
cpu_to_be32(conn->vals->header_size);
|
||||
rsp_hdr->ProtocolId = SMB2_PROTO_NUMBER;
|
||||
rsp_hdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
|
||||
rsp_hdr->CreditRequest = cpu_to_le16(0);
|
||||
@ -791,7 +780,9 @@ static void __smb2_lease_break_noti(struct work_struct *wk)
|
||||
rsp->AccessMaskHint = 0;
|
||||
rsp->ShareMaskHint = 0;
|
||||
|
||||
inc_rfc1001_len(work->response_buf, 44);
|
||||
if (ksmbd_iov_pin_rsp(work, (void *)rsp,
|
||||
sizeof(struct smb2_lease_break)))
|
||||
goto out;
|
||||
|
||||
ksmbd_conn_write(work);
|
||||
|
||||
@ -844,7 +835,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
|
||||
interim_entry);
|
||||
setup_async_work(in_work, NULL, NULL);
|
||||
smb2_send_interim_resp(in_work, STATUS_PENDING);
|
||||
list_del(&in_work->interim_entry);
|
||||
list_del_init(&in_work->interim_entry);
|
||||
release_async_work(in_work);
|
||||
}
|
||||
INIT_WORK(&work->work, __smb2_lease_break_noti);
|
||||
ksmbd_queue_work(work);
|
||||
@ -910,7 +902,8 @@ static int oplock_break(struct oplock_info *brk_opinfo, int req_op_level)
|
||||
lease->new_state =
|
||||
SMB2_LEASE_READ_CACHING_LE;
|
||||
} else {
|
||||
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE)
|
||||
if (lease->state & SMB2_LEASE_HANDLE_CACHING_LE &&
|
||||
!lease->is_dir)
|
||||
lease->new_state =
|
||||
SMB2_LEASE_READ_CACHING_LE;
|
||||
else
|
||||
@ -1042,6 +1035,7 @@ static void copy_lease(struct oplock_info *op1, struct oplock_info *op2)
|
||||
SMB2_LEASE_KEY_SIZE);
|
||||
lease2->duration = lease1->duration;
|
||||
lease2->flags = lease1->flags;
|
||||
lease2->epoch = lease1->epoch++;
|
||||
}
|
||||
|
||||
static int add_lease_global_list(struct oplock_info *opinfo)
|
||||
@ -1091,6 +1085,89 @@ static void set_oplock_level(struct oplock_info *opinfo, int level,
|
||||
}
|
||||
}
|
||||
|
||||
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
|
||||
struct lease_ctx_info *lctx)
|
||||
{
|
||||
struct oplock_info *opinfo;
|
||||
struct ksmbd_inode *p_ci = NULL;
|
||||
|
||||
if (lctx->version != 2)
|
||||
return;
|
||||
|
||||
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
|
||||
if (!p_ci)
|
||||
return;
|
||||
|
||||
read_lock(&p_ci->m_lock);
|
||||
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
|
||||
if (!opinfo->is_lease)
|
||||
continue;
|
||||
|
||||
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
|
||||
(!(lctx->flags & SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE) ||
|
||||
!compare_guid_key(opinfo, fp->conn->ClientGUID,
|
||||
lctx->parent_lease_key))) {
|
||||
if (!atomic_inc_not_zero(&opinfo->refcount))
|
||||
continue;
|
||||
|
||||
atomic_inc(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn)) {
|
||||
atomic_dec(&opinfo->conn->r_count);
|
||||
continue;
|
||||
}
|
||||
|
||||
read_unlock(&p_ci->m_lock);
|
||||
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
|
||||
opinfo_conn_put(opinfo);
|
||||
read_lock(&p_ci->m_lock);
|
||||
}
|
||||
}
|
||||
read_unlock(&p_ci->m_lock);
|
||||
|
||||
ksmbd_inode_put(p_ci);
|
||||
}
|
||||
|
||||
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
|
||||
{
|
||||
struct oplock_info *opinfo;
|
||||
struct ksmbd_inode *p_ci = NULL;
|
||||
|
||||
rcu_read_lock();
|
||||
opinfo = rcu_dereference(fp->f_opinfo);
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!opinfo->is_lease || opinfo->o_lease->version != 2)
|
||||
return;
|
||||
|
||||
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
|
||||
if (!p_ci)
|
||||
return;
|
||||
|
||||
read_lock(&p_ci->m_lock);
|
||||
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
|
||||
if (!opinfo->is_lease)
|
||||
continue;
|
||||
|
||||
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
|
||||
if (!atomic_inc_not_zero(&opinfo->refcount))
|
||||
continue;
|
||||
|
||||
atomic_inc(&opinfo->conn->r_count);
|
||||
if (ksmbd_conn_releasing(opinfo->conn)) {
|
||||
atomic_dec(&opinfo->conn->r_count);
|
||||
continue;
|
||||
}
|
||||
read_unlock(&p_ci->m_lock);
|
||||
oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
|
||||
opinfo_conn_put(opinfo);
|
||||
read_lock(&p_ci->m_lock);
|
||||
}
|
||||
}
|
||||
read_unlock(&p_ci->m_lock);
|
||||
|
||||
ksmbd_inode_put(p_ci);
|
||||
}
|
||||
|
||||
/**
|
||||
* smb_grant_oplock() - handle oplock/lease request on file open
|
||||
* @work: smb work
|
||||
@ -1114,10 +1191,6 @@ int smb_grant_oplock(struct ksmbd_work *work, int req_op_level, u64 pid,
|
||||
bool prev_op_has_lease;
|
||||
__le32 prev_op_state = 0;
|
||||
|
||||
/* not support directory lease */
|
||||
if (S_ISDIR(file_inode(fp->filp)->i_mode))
|
||||
return 0;
|
||||
|
||||
opinfo = alloc_opinfo(work, pid, tid);
|
||||
if (!opinfo)
|
||||
return -ENOMEM;
|
||||
@ -1374,6 +1447,7 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
|
||||
memcpy(buf->lcontext.LeaseKey, lease->lease_key,
|
||||
SMB2_LEASE_KEY_SIZE);
|
||||
buf->lcontext.LeaseFlags = lease->flags;
|
||||
buf->lcontext.Epoch = cpu_to_le16(++lease->epoch);
|
||||
buf->lcontext.LeaseState = lease->state;
|
||||
memcpy(buf->lcontext.ParentLeaseKey, lease->parent_lease_key,
|
||||
SMB2_LEASE_KEY_SIZE);
|
||||
@ -1410,10 +1484,11 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
|
||||
/**
|
||||
* parse_lease_state() - parse lease context containted in file open request
|
||||
* @open_req: buffer containing smb2 file open(create) request
|
||||
* @is_dir: whether leasing file is directory
|
||||
*
|
||||
* Return: oplock state, -ENOENT if create lease context not found
|
||||
*/
|
||||
struct lease_ctx_info *parse_lease_state(void *open_req)
|
||||
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
|
||||
{
|
||||
struct create_context *cc;
|
||||
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
|
||||
@ -1431,8 +1506,14 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
|
||||
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
|
||||
|
||||
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
|
||||
lreq->req_state = lc->lcontext.LeaseState;
|
||||
if (is_dir) {
|
||||
lreq->req_state = lc->lcontext.LeaseState &
|
||||
~SMB2_LEASE_WRITE_CACHING_LE;
|
||||
lreq->is_dir = true;
|
||||
} else
|
||||
lreq->req_state = lc->lcontext.LeaseState;
|
||||
lreq->flags = lc->lcontext.LeaseFlags;
|
||||
lreq->epoch = lc->lcontext.Epoch;
|
||||
lreq->duration = lc->lcontext.LeaseDuration;
|
||||
memcpy(lreq->parent_lease_key, lc->lcontext.ParentLeaseKey,
|
||||
SMB2_LEASE_KEY_SIZE);
|
||||
|
@ -34,7 +34,9 @@ struct lease_ctx_info {
|
||||
__le32 flags;
|
||||
__le64 duration;
|
||||
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
__le16 epoch;
|
||||
int version;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
struct lease_table {
|
||||
@ -53,6 +55,7 @@ struct lease {
|
||||
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
int version;
|
||||
unsigned short epoch;
|
||||
bool is_dir;
|
||||
struct lease_table *l_lb;
|
||||
};
|
||||
|
||||
@ -108,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);
|
||||
|
||||
/* Lease related functions */
|
||||
void create_lease_buf(u8 *rbuf, struct lease *lease);
|
||||
struct lease_ctx_info *parse_lease_state(void *open_req);
|
||||
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
|
||||
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
|
||||
int lease_read_to_write(struct oplock_info *opinfo);
|
||||
|
||||
@ -124,4 +127,7 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
|
||||
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
|
||||
struct lease_ctx_info *lctx);
|
||||
void destroy_lease_table(struct ksmbd_conn *conn);
|
||||
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
|
||||
struct lease_ctx_info *lctx);
|
||||
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
|
||||
#endif /* __KSMBD_OPLOCK_H */
|
||||
|
@ -115,8 +115,10 @@ static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
|
||||
if (check_conn_state(work))
|
||||
return SERVER_HANDLER_CONTINUE;
|
||||
|
||||
if (ksmbd_verify_smb_message(work))
|
||||
if (ksmbd_verify_smb_message(work)) {
|
||||
conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
|
||||
return SERVER_HANDLER_ABORT;
|
||||
}
|
||||
|
||||
command = conn->ops->get_cmd_val(work);
|
||||
*cmd = command;
|
||||
@ -163,6 +165,7 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
|
||||
{
|
||||
u16 command = 0;
|
||||
int rc;
|
||||
bool is_chained = false;
|
||||
|
||||
if (conn->ops->allocate_rsp_buf(work))
|
||||
return;
|
||||
@ -229,16 +232,17 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
|
||||
}
|
||||
}
|
||||
|
||||
is_chained = is_chained_smb2_message(work);
|
||||
|
||||
if (work->sess &&
|
||||
(work->sess->sign || smb3_11_final_sess_setup_resp(work) ||
|
||||
conn->ops->is_sign_req(work, command)))
|
||||
conn->ops->set_sign_rsp(work);
|
||||
} while (is_chained_smb2_message(work));
|
||||
|
||||
if (work->send_no_response)
|
||||
return;
|
||||
} while (is_chained == true);
|
||||
|
||||
send:
|
||||
if (work->tcon)
|
||||
ksmbd_tree_connect_put(work->tcon);
|
||||
smb3_preauth_hash_rsp(work);
|
||||
if (work->sess && work->sess->enc && work->encrypted &&
|
||||
conn->ops->encrypt_resp) {
|
||||
@ -442,11 +446,9 @@ static ssize_t stats_show(struct class *class, struct class_attribute *attr,
|
||||
"reset",
|
||||
"shutdown"
|
||||
};
|
||||
|
||||
ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version,
|
||||
state[server_conf.state], server_conf.tcp_port,
|
||||
server_conf.ipc_last_active / HZ);
|
||||
return sz;
|
||||
return sysfs_emit(buf, "%d %s %d %lu\n", stats_version,
|
||||
state[server_conf.state], server_conf.tcp_port,
|
||||
server_conf.ipc_last_active / HZ);
|
||||
}
|
||||
|
||||
static ssize_t kill_server_store(struct class *class,
|
||||
@ -478,19 +480,13 @@ static ssize_t debug_show(struct class *class, struct class_attribute *attr,
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) {
|
||||
if ((ksmbd_debug_types >> i) & 1) {
|
||||
pos = scnprintf(buf + sz,
|
||||
PAGE_SIZE - sz,
|
||||
"[%s] ",
|
||||
debug_type_strings[i]);
|
||||
pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]);
|
||||
} else {
|
||||
pos = scnprintf(buf + sz,
|
||||
PAGE_SIZE - sz,
|
||||
"%s ",
|
||||
debug_type_strings[i]);
|
||||
pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]);
|
||||
}
|
||||
sz += pos;
|
||||
}
|
||||
sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
|
||||
sz += sysfs_emit_at(buf, sz, "\n");
|
||||
return sz;
|
||||
}
|
||||
|
||||
@ -599,8 +595,6 @@ static int __init ksmbd_server_init(void)
|
||||
if (ret)
|
||||
goto err_crypto_destroy;
|
||||
|
||||
pr_warn_once("The ksmbd server is experimental\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_crypto_destroy:
|
||||
|
@ -106,16 +106,25 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
|
||||
break;
|
||||
case SMB2_CREATE:
|
||||
{
|
||||
unsigned short int name_off =
|
||||
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
|
||||
unsigned short int name_len =
|
||||
le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
|
||||
|
||||
if (((struct smb2_create_req *)hdr)->CreateContextsLength) {
|
||||
*off = le32_to_cpu(((struct smb2_create_req *)
|
||||
hdr)->CreateContextsOffset);
|
||||
*len = le32_to_cpu(((struct smb2_create_req *)
|
||||
hdr)->CreateContextsLength);
|
||||
break;
|
||||
if (!name_len)
|
||||
break;
|
||||
|
||||
if (name_off + name_len < (u64)*off + *len)
|
||||
break;
|
||||
}
|
||||
|
||||
*off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
|
||||
*len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
|
||||
*off = name_off;
|
||||
*len = name_len;
|
||||
break;
|
||||
}
|
||||
case SMB2_QUERY_INFO:
|
||||
@ -440,10 +449,8 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
|
||||
|
||||
validate_credit:
|
||||
if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
|
||||
smb2_validate_credit_charge(work->conn, hdr)) {
|
||||
work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
|
||||
smb2_validate_credit_charge(work->conn, hdr))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -221,7 +221,8 @@ void init_smb3_0_server(struct ksmbd_conn *conn)
|
||||
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
|
||||
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
|
||||
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
|
||||
@ -245,10 +246,12 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
|
||||
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
|
||||
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
|
||||
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
|
||||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
|
||||
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
|
||||
@ -269,7 +272,13 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
|
||||
conn->signing_algorithm = SIGNING_ALG_AES_CMAC_LE;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING |
|
||||
SMB2_GLOBAL_CAP_DIRECTORY_LEASING;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION ||
|
||||
(!(server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF) &&
|
||||
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION))
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -446,7 +446,7 @@ struct smb2_posix_info {
|
||||
/* SidBuffer contain two sids (UNIX user sid(16), UNIX group sid(16)) */
|
||||
u8 SidBuffer[32];
|
||||
__le32 name_len;
|
||||
u8 name[1];
|
||||
u8 name[];
|
||||
/*
|
||||
* var sized owner SID
|
||||
* var sized group SID
|
||||
@ -488,6 +488,7 @@ int find_matching_smb2_dialect(int start_index, __le16 *cli_dialects,
|
||||
struct file_lock *smb_flock_init(struct file *f);
|
||||
int setup_async_work(struct ksmbd_work *work, void (*fn)(void **),
|
||||
void **arg);
|
||||
void release_async_work(struct ksmbd_work *work);
|
||||
void smb2_send_interim_resp(struct ksmbd_work *work, __le32 status);
|
||||
struct channel *lookup_chann_list(struct ksmbd_session *sess,
|
||||
struct ksmbd_conn *conn);
|
||||
|
@ -266,7 +266,7 @@ static int ksmbd_negotiate_smb_dialect(void *buf)
|
||||
if (smb2_neg_size > smb_buf_length)
|
||||
goto err_out;
|
||||
|
||||
if (smb2_neg_size + le16_to_cpu(req->DialectCount) * sizeof(__le16) >
|
||||
if (struct_size(req, Dialects, le16_to_cpu(req->DialectCount)) >
|
||||
smb_buf_length)
|
||||
goto err_out;
|
||||
|
||||
@ -319,12 +319,6 @@ static int init_smb1_rsp_hdr(struct ksmbd_work *work)
|
||||
struct smb_hdr *rsp_hdr = (struct smb_hdr *)work->response_buf;
|
||||
struct smb_hdr *rcv_hdr = (struct smb_hdr *)work->request_buf;
|
||||
|
||||
/*
|
||||
* Remove 4 byte direct TCP header.
|
||||
*/
|
||||
*(__be32 *)work->response_buf =
|
||||
cpu_to_be32(sizeof(struct smb_hdr) - 4);
|
||||
|
||||
rsp_hdr->Command = SMB_COM_NEGOTIATE;
|
||||
*(__le32 *)rsp_hdr->Protocol = SMB1_PROTO_NUMBER;
|
||||
rsp_hdr->Flags = SMBFLG_RESPONSE;
|
||||
@ -359,8 +353,8 @@ static int smb1_check_user_session(struct ksmbd_work *work)
|
||||
*/
|
||||
static int smb1_allocate_rsp_buf(struct ksmbd_work *work)
|
||||
{
|
||||
work->response_buf = kmalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
work->response_buf = kzalloc(MAX_CIFS_SMALL_BUFFER_SIZE,
|
||||
GFP_KERNEL);
|
||||
work->response_sz = MAX_CIFS_SMALL_BUFFER_SIZE;
|
||||
|
||||
if (!work->response_buf) {
|
||||
@ -571,10 +565,11 @@ static int smb_handle_negotiate(struct ksmbd_work *work)
|
||||
|
||||
ksmbd_debug(SMB, "Unsupported SMB1 protocol\n");
|
||||
|
||||
/* Add 2 byte bcc and 2 byte DialectIndex. */
|
||||
inc_rfc1001_len(work->response_buf, 4);
|
||||
neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS;
|
||||
if (ksmbd_iov_pin_rsp(work, (void *)neg_rsp,
|
||||
sizeof(struct smb_negotiate_rsp) - 4))
|
||||
return -ENOMEM;
|
||||
|
||||
neg_rsp->hdr.Status.CifsError = STATUS_SUCCESS;
|
||||
neg_rsp->hdr.WordCount = 1;
|
||||
neg_rsp->DialectIndex = cpu_to_le16(work->conn->dialect);
|
||||
neg_rsp->ByteCount = 0;
|
||||
|
@ -200,7 +200,7 @@ struct smb_hdr {
|
||||
struct smb_negotiate_req {
|
||||
struct smb_hdr hdr; /* wct = 0 */
|
||||
__le16 ByteCount;
|
||||
unsigned char DialectsArray[1];
|
||||
unsigned char DialectsArray[];
|
||||
} __packed;
|
||||
|
||||
struct smb_negotiate_rsp {
|
||||
@ -263,14 +263,14 @@ struct file_directory_info {
|
||||
__le64 AllocationSize;
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
char FileName[1];
|
||||
char FileName[];
|
||||
} __packed; /* level 0x101 FF resp data */
|
||||
|
||||
struct file_names_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le32 FileNameLength;
|
||||
char FileName[1];
|
||||
char FileName[];
|
||||
} __packed; /* level 0xc FF resp data */
|
||||
|
||||
struct file_full_directory_info {
|
||||
@ -285,7 +285,7 @@ struct file_full_directory_info {
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
__le32 EaSize;
|
||||
char FileName[1];
|
||||
char FileName[];
|
||||
} __packed; /* level 0x102 FF resp */
|
||||
|
||||
struct file_both_directory_info {
|
||||
@ -303,7 +303,7 @@ struct file_both_directory_info {
|
||||
__u8 ShortNameLength;
|
||||
__u8 Reserved;
|
||||
__u8 ShortName[24];
|
||||
char FileName[1];
|
||||
char FileName[];
|
||||
} __packed; /* level 0x104 FFrsp data */
|
||||
|
||||
struct file_id_both_directory_info {
|
||||
@ -323,7 +323,7 @@ struct file_id_both_directory_info {
|
||||
__u8 ShortName[24];
|
||||
__le16 Reserved2;
|
||||
__le64 UniqueId;
|
||||
char FileName[1];
|
||||
char FileName[];
|
||||
} __packed;
|
||||
|
||||
struct file_id_full_dir_info {
|
||||
@ -340,7 +340,7 @@ struct file_id_full_dir_info {
|
||||
__le32 EaSize; /* EA size */
|
||||
__le32 Reserved;
|
||||
__le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
|
||||
char FileName[1];
|
||||
char FileName[];
|
||||
} __packed; /* level 0x105 FF rsp data */
|
||||
|
||||
struct smb_version_values {
|
||||
|
@ -97,7 +97,7 @@ int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid)
|
||||
/* compare all of the subauth values if any */
|
||||
num_sat = ctsid->num_subauth;
|
||||
num_saw = cwsid->num_subauth;
|
||||
num_subauth = num_sat < num_saw ? num_sat : num_saw;
|
||||
num_subauth = min(num_sat, num_saw);
|
||||
if (num_subauth) {
|
||||
for (i = 0; i < num_subauth; ++i) {
|
||||
if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
|
||||
@ -1185,8 +1185,7 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
|
||||
pntsd_size += sizeof(struct smb_acl) + nt_size;
|
||||
}
|
||||
|
||||
ksmbd_vfs_set_sd_xattr(conn, user_ns,
|
||||
path->dentry, pntsd, pntsd_size);
|
||||
ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, pntsd_size, false);
|
||||
kfree(pntsd);
|
||||
}
|
||||
|
||||
@ -1313,7 +1312,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
||||
|
||||
if (IS_ENABLED(CONFIG_FS_POSIX_ACL)) {
|
||||
posix_acls = get_acl(d_inode(path->dentry), ACL_TYPE_ACCESS);
|
||||
if (posix_acls && !found) {
|
||||
if (!IS_ERR_OR_NULL(posix_acls) && !found) {
|
||||
unsigned int id = -1;
|
||||
|
||||
pa_entry = posix_acls->a_entries;
|
||||
@ -1337,7 +1336,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (posix_acls)
|
||||
if (!IS_ERR_OR_NULL(posix_acls))
|
||||
posix_acl_release(posix_acls);
|
||||
}
|
||||
|
||||
@ -1378,7 +1377,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
||||
|
||||
int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
|
||||
const struct path *path, struct smb_ntsd *pntsd, int ntsd_len,
|
||||
bool type_check)
|
||||
bool type_check, bool get_write)
|
||||
{
|
||||
int rc;
|
||||
struct smb_fattr fattr = {{0}};
|
||||
@ -1406,7 +1405,7 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
|
||||
newattrs.ia_valid |= ATTR_MODE;
|
||||
newattrs.ia_mode = (inode->i_mode & ~0777) | (fattr.cf_mode & 0777);
|
||||
|
||||
ksmbd_vfs_remove_acl_xattrs(user_ns, path->dentry);
|
||||
ksmbd_vfs_remove_acl_xattrs(user_ns, path);
|
||||
/* Update posix acls */
|
||||
if (IS_ENABLED(CONFIG_FS_POSIX_ACL) && fattr.cf_dacls) {
|
||||
rc = set_posix_acl(user_ns, inode,
|
||||
@ -1437,15 +1436,14 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
|
||||
|
||||
if (test_share_config_flag(tcon->share_conf, KSMBD_SHARE_FLAG_ACL_XATTR)) {
|
||||
/* Update WinACL in xattr */
|
||||
ksmbd_vfs_remove_sd_xattrs(user_ns, path->dentry);
|
||||
ksmbd_vfs_set_sd_xattr(conn, user_ns,
|
||||
path->dentry, pntsd, ntsd_len);
|
||||
ksmbd_vfs_remove_sd_xattrs(user_ns, path);
|
||||
ksmbd_vfs_set_sd_xattr(conn, user_ns, path, pntsd, ntsd_len,
|
||||
get_write);
|
||||
}
|
||||
|
||||
out:
|
||||
posix_acl_release(fattr.cf_acls);
|
||||
posix_acl_release(fattr.cf_dacls);
|
||||
mark_inode_dirty(inode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -207,7 +207,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
|
||||
__le32 *pdaccess, int uid);
|
||||
int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
|
||||
const struct path *path, struct smb_ntsd *pntsd, int ntsd_len,
|
||||
bool type_check);
|
||||
bool type_check, bool get_write);
|
||||
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
|
||||
void ksmbd_init_domain(u32 *sub_auth);
|
||||
|
||||
|
@ -229,7 +229,7 @@ static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
|
||||
|
||||
msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO);
|
||||
msg = kvzalloc(msg_sz, GFP_KERNEL);
|
||||
if (msg)
|
||||
msg->sz = sz;
|
||||
return msg;
|
||||
@ -268,7 +268,7 @@ static int handle_response(int type, void *payload, size_t sz)
|
||||
entry->type + 1, type);
|
||||
}
|
||||
|
||||
entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO);
|
||||
entry->response = kvzalloc(sz, GFP_KERNEL);
|
||||
if (!entry->response) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
|
@ -1241,14 +1241,12 @@ static int smb_direct_writev(struct ksmbd_transport *t,
|
||||
|
||||
//FIXME: skip RFC1002 header..
|
||||
buflen -= 4;
|
||||
iov[0].iov_base += 4;
|
||||
iov[0].iov_len -= 4;
|
||||
|
||||
remaining_data_length = buflen;
|
||||
ksmbd_debug(RDMA, "Sending smb (RDMA): smb_len=%u\n", buflen);
|
||||
|
||||
smb_direct_send_ctx_init(st, &send_ctx, need_invalidate, remote_key);
|
||||
start = i = 0;
|
||||
start = i = 1;
|
||||
buflen = 0;
|
||||
while (true) {
|
||||
buflen += iov[i].iov_len;
|
||||
@ -2142,8 +2140,7 @@ static int smb_direct_ib_client_add(struct ib_device *ib_dev)
|
||||
if (ib_dev->node_type != RDMA_NODE_IB_CA)
|
||||
smb_direct_port = SMB_DIRECT_PORT_IWARP;
|
||||
|
||||
if (!ib_dev->ops.get_netdev ||
|
||||
!rdma_frwr_is_supported(&ib_dev->attrs))
|
||||
if (!rdma_frwr_is_supported(&ib_dev->attrs))
|
||||
return 0;
|
||||
|
||||
smb_dev = kzalloc(sizeof(*smb_dev), GFP_KERNEL);
|
||||
@ -2243,17 +2240,38 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
|
||||
for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) {
|
||||
struct net_device *ndev;
|
||||
|
||||
ndev = smb_dev->ib_dev->ops.get_netdev(smb_dev->ib_dev,
|
||||
i + 1);
|
||||
if (!ndev)
|
||||
continue;
|
||||
if (smb_dev->ib_dev->ops.get_netdev) {
|
||||
ndev = smb_dev->ib_dev->ops.get_netdev(
|
||||
smb_dev->ib_dev, i + 1);
|
||||
if (!ndev)
|
||||
continue;
|
||||
|
||||
if (ndev == netdev) {
|
||||
if (ndev == netdev) {
|
||||
dev_put(ndev);
|
||||
rdma_capable = true;
|
||||
goto out;
|
||||
}
|
||||
dev_put(ndev);
|
||||
rdma_capable = true;
|
||||
goto out;
|
||||
/* if ib_dev does not implement ops.get_netdev
|
||||
* check for matching infiniband GUID in hw_addr
|
||||
*/
|
||||
} else if (netdev->type == ARPHRD_INFINIBAND) {
|
||||
struct netdev_hw_addr *ha;
|
||||
union ib_gid gid;
|
||||
u32 port_num;
|
||||
int ret;
|
||||
|
||||
netdev_hw_addr_list_for_each(
|
||||
ha, &netdev->dev_addrs) {
|
||||
memcpy(&gid, ha->addr + 4, sizeof(gid));
|
||||
ret = ib_find_gid(smb_dev->ib_dev, &gid,
|
||||
&port_num, NULL);
|
||||
if (!ret) {
|
||||
rdma_capable = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
dev_put(ndev);
|
||||
}
|
||||
}
|
||||
out:
|
||||
|
@ -14,46 +14,10 @@
|
||||
#include "uniupr.h"
|
||||
#include "smb_common.h"
|
||||
|
||||
/*
|
||||
* smb_utf16_bytes() - how long will a string be after conversion?
|
||||
* @from: pointer to input string
|
||||
* @maxbytes: don't go past this many bytes of input string
|
||||
* @codepage: destination codepage
|
||||
*
|
||||
* Walk a utf16le string and return the number of bytes that the string will
|
||||
* be after being converted to the given charset, not including any null
|
||||
* termination required. Don't walk past maxbytes in the source buffer.
|
||||
*
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
static int smb_utf16_bytes(const __le16 *from, int maxbytes,
|
||||
const struct nls_table *codepage)
|
||||
{
|
||||
int i;
|
||||
int charlen, outlen = 0;
|
||||
int maxwords = maxbytes / 2;
|
||||
char tmp[NLS_MAX_CHARSET_SIZE];
|
||||
__u16 ftmp;
|
||||
|
||||
for (i = 0; i < maxwords; i++) {
|
||||
ftmp = get_unaligned_le16(&from[i]);
|
||||
if (ftmp == 0)
|
||||
break;
|
||||
|
||||
charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
|
||||
if (charlen > 0)
|
||||
outlen += charlen;
|
||||
else
|
||||
outlen++;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* cifs_mapchar() - convert a host-endian char to proper char in codepage
|
||||
* @target: where converted character should be copied
|
||||
* @src_char: 2 byte host-endian source character
|
||||
* @from: host-endian source string
|
||||
* @cp: codepage to which character should be converted
|
||||
* @mapchar: should character be mapped according to mapchars mount option?
|
||||
*
|
||||
@ -64,10 +28,13 @@ static int smb_utf16_bytes(const __le16 *from, int maxbytes,
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
static int
|
||||
cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
|
||||
cifs_mapchar(char *target, const __u16 *from, const struct nls_table *cp,
|
||||
bool mapchar)
|
||||
{
|
||||
int len = 1;
|
||||
__u16 src_char;
|
||||
|
||||
src_char = *from;
|
||||
|
||||
if (!mapchar)
|
||||
goto cp_convert;
|
||||
@ -105,30 +72,66 @@ cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
|
||||
|
||||
cp_convert:
|
||||
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
|
||||
if (len <= 0) {
|
||||
*target = '?';
|
||||
len = 1;
|
||||
}
|
||||
if (len <= 0)
|
||||
goto surrogate_pair;
|
||||
|
||||
goto out;
|
||||
|
||||
surrogate_pair:
|
||||
/* convert SURROGATE_PAIR and IVS */
|
||||
if (strcmp(cp->charset, "utf8"))
|
||||
goto unknown;
|
||||
len = utf16s_to_utf8s(from, 3, UTF16_LITTLE_ENDIAN, target, 6);
|
||||
if (len <= 0)
|
||||
goto unknown;
|
||||
return len;
|
||||
|
||||
unknown:
|
||||
*target = '?';
|
||||
len = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_char_allowed() - check for valid character
|
||||
* @ch: input character to be checked
|
||||
* smb_utf16_bytes() - compute converted string length
|
||||
* @from: pointer to input string
|
||||
* @maxbytes: input string length
|
||||
* @codepage: destination codepage
|
||||
*
|
||||
* Return: 1 if char is allowed, otherwise 0
|
||||
* Walk a utf16le string and return the number of bytes that the string will
|
||||
* be after being converted to the given charset, not including any null
|
||||
* termination required. Don't walk past maxbytes in the source buffer.
|
||||
*
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
static inline int is_char_allowed(char *ch)
|
||||
static int smb_utf16_bytes(const __le16 *from, int maxbytes,
|
||||
const struct nls_table *codepage)
|
||||
{
|
||||
/* check for control chars, wildcards etc. */
|
||||
if (!(*ch & 0x80) &&
|
||||
(*ch <= 0x1f ||
|
||||
*ch == '?' || *ch == '"' || *ch == '<' ||
|
||||
*ch == '>' || *ch == '|'))
|
||||
return 0;
|
||||
int i, j;
|
||||
int charlen, outlen = 0;
|
||||
int maxwords = maxbytes / 2;
|
||||
char tmp[NLS_MAX_CHARSET_SIZE];
|
||||
__u16 ftmp[3];
|
||||
|
||||
return 1;
|
||||
for (i = 0; i < maxwords; i++) {
|
||||
ftmp[0] = get_unaligned_le16(&from[i]);
|
||||
if (ftmp[0] == 0)
|
||||
break;
|
||||
for (j = 1; j <= 2; j++) {
|
||||
if (i + j < maxwords)
|
||||
ftmp[j] = get_unaligned_le16(&from[i + j]);
|
||||
else
|
||||
ftmp[j] = 0;
|
||||
}
|
||||
|
||||
charlen = cifs_mapchar(tmp, ftmp, codepage, 0);
|
||||
if (charlen > 0)
|
||||
outlen += charlen;
|
||||
else
|
||||
outlen++;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -158,12 +161,12 @@ static inline int is_char_allowed(char *ch)
|
||||
static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
|
||||
const struct nls_table *codepage, bool mapchar)
|
||||
{
|
||||
int i, charlen, safelen;
|
||||
int i, j, charlen, safelen;
|
||||
int outlen = 0;
|
||||
int nullsize = nls_nullsize(codepage);
|
||||
int fromwords = fromlen / 2;
|
||||
char tmp[NLS_MAX_CHARSET_SIZE];
|
||||
__u16 ftmp;
|
||||
__u16 ftmp[3]; /* ftmp[3] = 3array x 2bytes = 6bytes UTF-16 */
|
||||
|
||||
/*
|
||||
* because the chars can be of varying widths, we need to take care
|
||||
@ -174,9 +177,15 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
|
||||
safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
|
||||
|
||||
for (i = 0; i < fromwords; i++) {
|
||||
ftmp = get_unaligned_le16(&from[i]);
|
||||
if (ftmp == 0)
|
||||
ftmp[0] = get_unaligned_le16(&from[i]);
|
||||
if (ftmp[0] == 0)
|
||||
break;
|
||||
for (j = 1; j <= 2; j++) {
|
||||
if (i + j < fromwords)
|
||||
ftmp[j] = get_unaligned_le16(&from[i + j]);
|
||||
else
|
||||
ftmp[j] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check to see if converting this character might make the
|
||||
@ -191,6 +200,19 @@ static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
|
||||
/* put converted char into 'to' buffer */
|
||||
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
|
||||
outlen += charlen;
|
||||
|
||||
/*
|
||||
* charlen (=bytes of UTF-8 for 1 character)
|
||||
* 4bytes UTF-8(surrogate pair) is charlen=4
|
||||
* (4bytes UTF-16 code)
|
||||
* 7-8bytes UTF-8(IVS) is charlen=3+4 or 4+4
|
||||
* (2 UTF-8 pairs divided to 2 UTF-16 pairs)
|
||||
*/
|
||||
if (charlen == 4)
|
||||
i++;
|
||||
else if (charlen >= 5)
|
||||
/* 5-6bytes UTF-8 */
|
||||
i += 2;
|
||||
}
|
||||
|
||||
/* properly null-terminate string */
|
||||
@ -325,6 +347,9 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
|
||||
char src_char;
|
||||
__le16 dst_char;
|
||||
wchar_t tmp;
|
||||
wchar_t wchar_to[6]; /* UTF-16 */
|
||||
int ret;
|
||||
unicode_t u;
|
||||
|
||||
if (!mapchars)
|
||||
return smb_strtoUTF16(target, source, srclen, cp);
|
||||
@ -367,11 +392,57 @@ int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
|
||||
* if no match, use question mark, which at least in
|
||||
* some cases serves as wild card
|
||||
*/
|
||||
if (charlen < 1) {
|
||||
dst_char = cpu_to_le16(0x003f);
|
||||
charlen = 1;
|
||||
if (charlen > 0)
|
||||
goto ctoUTF16;
|
||||
|
||||
/* convert SURROGATE_PAIR */
|
||||
if (strcmp(cp->charset, "utf8"))
|
||||
goto unknown;
|
||||
if (*(source + i) & 0x80) {
|
||||
charlen = utf8_to_utf32(source + i, 6, &u);
|
||||
if (charlen < 0)
|
||||
goto unknown;
|
||||
} else
|
||||
goto unknown;
|
||||
ret = utf8s_to_utf16s(source + i, charlen,
|
||||
UTF16_LITTLE_ENDIAN,
|
||||
wchar_to, 6);
|
||||
if (ret < 0)
|
||||
goto unknown;
|
||||
|
||||
i += charlen;
|
||||
dst_char = cpu_to_le16(*wchar_to);
|
||||
if (charlen <= 3)
|
||||
/* 1-3bytes UTF-8 to 2bytes UTF-16 */
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
else if (charlen == 4) {
|
||||
/*
|
||||
* 4bytes UTF-8(surrogate pair) to 4bytes UTF-16
|
||||
* 7-8bytes UTF-8(IVS) divided to 2 UTF-16
|
||||
* (charlen=3+4 or 4+4)
|
||||
*/
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
dst_char = cpu_to_le16(*(wchar_to + 1));
|
||||
j++;
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
} else if (charlen >= 5) {
|
||||
/* 5-6bytes UTF-8 to 6bytes UTF-16 */
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
dst_char = cpu_to_le16(*(wchar_to + 1));
|
||||
j++;
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
dst_char = cpu_to_le16(*(wchar_to + 2));
|
||||
j++;
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
}
|
||||
continue;
|
||||
|
||||
unknown:
|
||||
dst_char = cpu_to_le16(0x003f);
|
||||
charlen = 1;
|
||||
}
|
||||
|
||||
ctoUTF16:
|
||||
/*
|
||||
* character may take more than one byte in the source string,
|
||||
* but will take exactly two bytes in the target string
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -71,25 +71,23 @@ struct ksmbd_kstat {
|
||||
__le32 file_attributes;
|
||||
};
|
||||
|
||||
int ksmbd_vfs_lock_parent(struct user_namespace *user_ns, struct dentry *parent,
|
||||
struct dentry *child);
|
||||
int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry);
|
||||
int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
|
||||
int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
|
||||
void ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, __le32 *daccess);
|
||||
int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
|
||||
int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode);
|
||||
int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
size_t count, loff_t *pos);
|
||||
int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
|
||||
loff_t *pos, char *rbuf);
|
||||
int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
char *buf, size_t count, loff_t *pos, bool sync,
|
||||
ssize_t *written);
|
||||
int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
|
||||
int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name);
|
||||
int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path);
|
||||
int ksmbd_vfs_link(struct ksmbd_work *work,
|
||||
const char *oldname, const char *newname);
|
||||
int ksmbd_vfs_getattr(const struct path *path, struct kstat *stat);
|
||||
int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
char *newname);
|
||||
int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
|
||||
char *newname, int flags);
|
||||
int ksmbd_vfs_truncate(struct ksmbd_work *work,
|
||||
struct ksmbd_file *fp, loff_t size);
|
||||
struct srv_copychunk;
|
||||
@ -110,15 +108,17 @@ ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, char *attr_name,
|
||||
int attr_name_len);
|
||||
int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, const char *attr_name,
|
||||
void *attr_value, size_t attr_size, int flags);
|
||||
const struct path *path, const char *attr_name,
|
||||
void *attr_value, size_t attr_size, int flags,
|
||||
bool get_write);
|
||||
int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
|
||||
size_t *xattr_stream_name_size, int s_type);
|
||||
int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, char *attr_name);
|
||||
int ksmbd_vfs_kern_path(struct ksmbd_work *work,
|
||||
char *name, unsigned int flags, struct path *path,
|
||||
bool caseless);
|
||||
const struct path *path, char *attr_name);
|
||||
int ksmbd_vfs_kern_path_locked(struct ksmbd_work *work, char *name,
|
||||
unsigned int flags, struct path *parent_path,
|
||||
struct path *path, bool caseless);
|
||||
void ksmbd_vfs_kern_path_unlock(struct path *parent_path, struct path *path);
|
||||
struct dentry *ksmbd_vfs_kern_path_create(struct ksmbd_work *work,
|
||||
const char *name,
|
||||
unsigned int flags,
|
||||
@ -131,8 +131,7 @@ struct file_allocated_range_buffer;
|
||||
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
|
||||
struct file_allocated_range_buffer *ranges,
|
||||
unsigned int in_count, unsigned int *out_count);
|
||||
int ksmbd_vfs_unlink(struct user_namespace *user_ns,
|
||||
struct dentry *dir, struct dentry *dentry);
|
||||
int ksmbd_vfs_unlink(struct file *filp);
|
||||
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
|
||||
int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
|
||||
struct user_namespace *user_ns,
|
||||
@ -142,26 +141,27 @@ void ksmbd_vfs_posix_lock_wait(struct file_lock *flock);
|
||||
int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout);
|
||||
void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock);
|
||||
int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
|
||||
struct dentry *dentry);
|
||||
int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns,
|
||||
struct dentry *dentry);
|
||||
const struct path *path);
|
||||
int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns, const struct path *path);
|
||||
int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
|
||||
struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct smb_ntsd *pntsd, int len);
|
||||
const struct path *path,
|
||||
struct smb_ntsd *pntsd, int len,
|
||||
bool get_write);
|
||||
int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
|
||||
struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct smb_ntsd **pntsd);
|
||||
int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct xattr_dos_attrib *da);
|
||||
const struct path *path,
|
||||
struct xattr_dos_attrib *da,
|
||||
bool get_write);
|
||||
int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct xattr_dos_attrib *da);
|
||||
int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
|
||||
struct inode *inode);
|
||||
struct path *path);
|
||||
int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
|
||||
struct inode *inode,
|
||||
struct path *path,
|
||||
struct inode *parent_inode);
|
||||
#endif /* __KSMBD_VFS_H__ */
|
||||
|
@ -65,14 +65,14 @@ static unsigned long inode_hash(struct super_block *sb, unsigned long hashval)
|
||||
return tmp & inode_hash_mask;
|
||||
}
|
||||
|
||||
static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)
|
||||
static struct ksmbd_inode *__ksmbd_inode_lookup(struct dentry *de)
|
||||
{
|
||||
struct hlist_head *head = inode_hashtable +
|
||||
inode_hash(inode->i_sb, inode->i_ino);
|
||||
inode_hash(d_inode(de)->i_sb, (unsigned long)de);
|
||||
struct ksmbd_inode *ci = NULL, *ret_ci = NULL;
|
||||
|
||||
hlist_for_each_entry(ci, head, m_hash) {
|
||||
if (ci->m_inode == inode) {
|
||||
if (ci->m_de == de) {
|
||||
if (atomic_inc_not_zero(&ci->m_count))
|
||||
ret_ci = ci;
|
||||
break;
|
||||
@ -83,26 +83,27 @@ static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)
|
||||
|
||||
static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
|
||||
{
|
||||
return __ksmbd_inode_lookup(file_inode(fp->filp));
|
||||
return __ksmbd_inode_lookup(fp->filp->f_path.dentry);
|
||||
}
|
||||
|
||||
static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode)
|
||||
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d)
|
||||
{
|
||||
struct ksmbd_inode *ci;
|
||||
|
||||
read_lock(&inode_hash_lock);
|
||||
ci = __ksmbd_inode_lookup(inode);
|
||||
ci = __ksmbd_inode_lookup(d);
|
||||
read_unlock(&inode_hash_lock);
|
||||
|
||||
return ci;
|
||||
}
|
||||
|
||||
int ksmbd_query_inode_status(struct inode *inode)
|
||||
int ksmbd_query_inode_status(struct dentry *dentry)
|
||||
{
|
||||
struct ksmbd_inode *ci;
|
||||
int ret = KSMBD_INODE_STATUS_UNKNOWN;
|
||||
|
||||
read_lock(&inode_hash_lock);
|
||||
ci = __ksmbd_inode_lookup(inode);
|
||||
ci = __ksmbd_inode_lookup(dentry);
|
||||
if (ci) {
|
||||
ret = KSMBD_INODE_STATUS_OK;
|
||||
if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
|
||||
@ -142,7 +143,7 @@ void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
|
||||
static void ksmbd_inode_hash(struct ksmbd_inode *ci)
|
||||
{
|
||||
struct hlist_head *b = inode_hashtable +
|
||||
inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino);
|
||||
inode_hash(d_inode(ci->m_de)->i_sb, (unsigned long)ci->m_de);
|
||||
|
||||
hlist_add_head(&ci->m_hash, b);
|
||||
}
|
||||
@ -156,7 +157,6 @@ static void ksmbd_inode_unhash(struct ksmbd_inode *ci)
|
||||
|
||||
static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp)
|
||||
{
|
||||
ci->m_inode = file_inode(fp->filp);
|
||||
atomic_set(&ci->m_count, 1);
|
||||
atomic_set(&ci->op_count, 0);
|
||||
atomic_set(&ci->sop_count, 0);
|
||||
@ -165,6 +165,7 @@ static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp)
|
||||
INIT_LIST_HEAD(&ci->m_fp_list);
|
||||
INIT_LIST_HEAD(&ci->m_op_list);
|
||||
rwlock_init(&ci->m_lock);
|
||||
ci->m_de = fp->filp->f_path.dentry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -208,7 +209,7 @@ static void ksmbd_inode_free(struct ksmbd_inode *ci)
|
||||
kfree(ci);
|
||||
}
|
||||
|
||||
static void ksmbd_inode_put(struct ksmbd_inode *ci)
|
||||
void ksmbd_inode_put(struct ksmbd_inode *ci)
|
||||
{
|
||||
if (atomic_dec_and_test(&ci->m_count))
|
||||
ksmbd_inode_free(ci);
|
||||
@ -243,7 +244,6 @@ void ksmbd_release_inode_hash(void)
|
||||
|
||||
static void __ksmbd_inode_close(struct ksmbd_file *fp)
|
||||
{
|
||||
struct dentry *dir, *dentry;
|
||||
struct ksmbd_inode *ci = fp->f_ci;
|
||||
int err;
|
||||
struct file *filp;
|
||||
@ -252,7 +252,7 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
|
||||
if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
|
||||
ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
|
||||
err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp),
|
||||
filp->f_path.dentry,
|
||||
&filp->f_path,
|
||||
fp->stream.name);
|
||||
if (err)
|
||||
pr_err("remove xattr failed : %s\n",
|
||||
@ -262,11 +262,9 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
|
||||
if (atomic_dec_and_test(&ci->m_count)) {
|
||||
write_lock(&ci->m_lock);
|
||||
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
|
||||
dentry = filp->f_path.dentry;
|
||||
dir = dentry->d_parent;
|
||||
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
|
||||
write_unlock(&ci->m_lock);
|
||||
ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry);
|
||||
ksmbd_vfs_unlink(filp);
|
||||
write_lock(&ci->m_lock);
|
||||
}
|
||||
write_unlock(&ci->m_lock);
|
||||
@ -335,6 +333,9 @@ static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
|
||||
|
||||
static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
|
||||
{
|
||||
if (fp->f_state != FP_INITED)
|
||||
return NULL;
|
||||
|
||||
if (!atomic_inc_not_zero(&fp->refcount))
|
||||
return NULL;
|
||||
return fp;
|
||||
@ -384,15 +385,20 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
|
||||
return 0;
|
||||
|
||||
ft = &work->sess->file_table;
|
||||
read_lock(&ft->lock);
|
||||
write_lock(&ft->lock);
|
||||
fp = idr_find(ft->idr, id);
|
||||
if (fp) {
|
||||
set_close_state_blocked_works(fp);
|
||||
|
||||
if (!atomic_dec_and_test(&fp->refcount))
|
||||
if (fp->f_state != FP_INITED)
|
||||
fp = NULL;
|
||||
else {
|
||||
fp->f_state = FP_CLOSED;
|
||||
if (!atomic_dec_and_test(&fp->refcount))
|
||||
fp = NULL;
|
||||
}
|
||||
}
|
||||
read_unlock(&ft->lock);
|
||||
write_unlock(&ft->lock);
|
||||
|
||||
if (!fp)
|
||||
return -EINVAL;
|
||||
@ -482,12 +488,15 @@ struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
|
||||
return fp;
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode)
|
||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry)
|
||||
{
|
||||
struct ksmbd_file *lfp;
|
||||
struct ksmbd_inode *ci;
|
||||
struct inode *inode = d_inode(dentry);
|
||||
|
||||
ci = ksmbd_inode_lookup_by_vfsinode(inode);
|
||||
read_lock(&inode_hash_lock);
|
||||
ci = __ksmbd_inode_lookup(dentry);
|
||||
read_unlock(&inode_hash_lock);
|
||||
if (!ci)
|
||||
return NULL;
|
||||
|
||||
@ -572,6 +581,7 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
|
||||
fp->tcon = work->tcon;
|
||||
fp->volatile_id = KSMBD_NO_FID;
|
||||
fp->persistent_id = KSMBD_NO_FID;
|
||||
fp->f_state = FP_NEW;
|
||||
fp->f_ci = ksmbd_inode_get(fp);
|
||||
|
||||
if (!fp->f_ci) {
|
||||
@ -593,6 +603,17 @@ struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
|
||||
unsigned int state)
|
||||
{
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
write_lock(&ft->lock);
|
||||
fp->f_state = state;
|
||||
write_unlock(&ft->lock);
|
||||
}
|
||||
|
||||
static int
|
||||
__close_file_table_ids(struct ksmbd_file_table *ft,
|
||||
struct ksmbd_tree_connect *tcon,
|
||||
|
@ -51,7 +51,7 @@ struct ksmbd_inode {
|
||||
atomic_t op_count;
|
||||
/* opinfo count for streams */
|
||||
atomic_t sop_count;
|
||||
struct inode *m_inode;
|
||||
struct dentry *m_de;
|
||||
unsigned int m_flags;
|
||||
struct hlist_node m_hash;
|
||||
struct list_head m_fp_list;
|
||||
@ -60,6 +60,12 @@ struct ksmbd_inode {
|
||||
__le32 m_fattr;
|
||||
};
|
||||
|
||||
enum {
|
||||
FP_NEW = 0,
|
||||
FP_INITED,
|
||||
FP_CLOSED
|
||||
};
|
||||
|
||||
struct ksmbd_file {
|
||||
struct file *filp;
|
||||
u64 persistent_id;
|
||||
@ -98,6 +104,8 @@ struct ksmbd_file {
|
||||
/* if ls is happening on directory, below is valid*/
|
||||
struct ksmbd_readdir_data readdir_data;
|
||||
int dot_dotdot[2];
|
||||
unsigned int f_state;
|
||||
bool reserve_lease_break;
|
||||
};
|
||||
|
||||
static inline void set_ctx_actor(struct dir_context *ctx,
|
||||
@ -131,9 +139,11 @@ struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
|
||||
u64 pid);
|
||||
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
|
||||
struct ksmbd_inode *ksmbd_inode_lookup_lock(struct dentry *d);
|
||||
void ksmbd_inode_put(struct ksmbd_inode *ci);
|
||||
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct dentry *dentry);
|
||||
unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
|
||||
struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp);
|
||||
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
|
||||
@ -142,6 +152,8 @@ int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
|
||||
int ksmbd_init_global_file_table(void);
|
||||
void ksmbd_free_global_file_table(void);
|
||||
void ksmbd_set_fd_limit(unsigned long limit);
|
||||
void ksmbd_update_fstate(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
|
||||
unsigned int state);
|
||||
|
||||
/*
|
||||
* INODE hash
|
||||
@ -155,7 +167,7 @@ enum KSMBD_INODE_STATUS {
|
||||
KSMBD_INODE_STATUS_PENDING_DELETE,
|
||||
};
|
||||
|
||||
int ksmbd_query_inode_status(struct inode *inode);
|
||||
int ksmbd_query_inode_status(struct dentry *dentry);
|
||||
bool ksmbd_inode_pending_delete(struct ksmbd_file *fp);
|
||||
void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
|
||||
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
|
||||
|
@ -583,7 +583,7 @@ struct request_queue {
|
||||
#define QUEUE_FLAG_NOXMERGES 9 /* No extended merges */
|
||||
#define QUEUE_FLAG_ADD_RANDOM 10 /* Contributes to random pool */
|
||||
#define QUEUE_FLAG_SAME_FORCE 12 /* force complete on same CPU */
|
||||
#define QUEUE_FLAG_HW_WC 18 /* Write back caching supported */
|
||||
#define QUEUE_FLAG_HW_WC 13 /* Write back caching supported */
|
||||
#define QUEUE_FLAG_INIT_DONE 14 /* queue is initialized */
|
||||
#define QUEUE_FLAG_STABLE_WRITES 15 /* don't modify blks until WB is done */
|
||||
#define QUEUE_FLAG_POLL 16 /* IO polling enabled if set */
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#define SYMBOL_CRC(sym, crc, sec) \
|
||||
asm(".section \"___kcrctab" sec "+" #sym "\",\"a\"" "\n" \
|
||||
".balign 4" "\n" \
|
||||
"__crc_" #sym ":" "\n" \
|
||||
".long " #crc "\n" \
|
||||
".previous" "\n")
|
||||
|
@ -887,8 +887,17 @@ static inline bool module_sig_ok(struct module *module)
|
||||
}
|
||||
#endif /* CONFIG_MODULE_SIG */
|
||||
|
||||
#if defined(CONFIG_MODULES) && defined(CONFIG_KALLSYMS)
|
||||
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
|
||||
struct module *, unsigned long),
|
||||
void *data);
|
||||
#else
|
||||
static inline int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
|
||||
struct module *, unsigned long),
|
||||
void *data)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* CONFIG_MODULES && CONFIG_KALLSYMS */
|
||||
|
||||
#endif /* _LINUX_MODULE_H */
|
||||
|
@ -57,12 +57,18 @@ static inline int user_path_at(int dfd, const char __user *name, unsigned flags,
|
||||
return user_path_at_empty(dfd, name, flags, path, NULL);
|
||||
}
|
||||
|
||||
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
|
||||
struct dentry *base,
|
||||
unsigned int flags);
|
||||
extern int kern_path(const char *, unsigned, struct path *);
|
||||
|
||||
extern struct dentry *kern_path_create(int, const char *, struct path *, unsigned int);
|
||||
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
|
||||
extern void done_path_create(struct path *, struct dentry *);
|
||||
extern struct dentry *kern_path_locked(const char *, struct path *);
|
||||
int vfs_path_parent_lookup(struct filename *filename, unsigned int flags,
|
||||
struct path *parent, struct qstr *last, int *type,
|
||||
const struct path *root);
|
||||
|
||||
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
|
||||
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
|
||||
@ -81,6 +87,7 @@ extern int follow_down(struct path *);
|
||||
extern int follow_up(struct path *);
|
||||
|
||||
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
|
||||
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
|
||||
extern void unlock_rename(struct dentry *, struct dentry *);
|
||||
|
||||
extern int __must_check nd_jump_link(const struct path *path);
|
||||
|
@ -32,7 +32,12 @@ enum dev_dma_attr {
|
||||
DEV_DMA_COHERENT,
|
||||
};
|
||||
|
||||
struct fwnode_handle *dev_fwnode(const struct device *dev);
|
||||
const struct fwnode_handle *__dev_fwnode_const(const struct device *dev);
|
||||
struct fwnode_handle *__dev_fwnode(struct device *dev);
|
||||
#define dev_fwnode(dev) \
|
||||
_Generic((dev), \
|
||||
const struct device *: __dev_fwnode_const, \
|
||||
struct device *: __dev_fwnode)(dev)
|
||||
|
||||
bool device_property_present(struct device *dev, const char *propname);
|
||||
int device_property_read_u8_array(struct device *dev, const char *propname,
|
||||
|
@ -267,6 +267,26 @@ static inline void *spi_get_drvdata(struct spi_device *spi)
|
||||
return dev_get_drvdata(&spi->dev);
|
||||
}
|
||||
|
||||
static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
|
||||
{
|
||||
return spi->chip_select;
|
||||
}
|
||||
|
||||
static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect)
|
||||
{
|
||||
spi->chip_select = chipselect;
|
||||
}
|
||||
|
||||
static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi, u8 idx)
|
||||
{
|
||||
return spi->cs_gpiod;
|
||||
}
|
||||
|
||||
static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod)
|
||||
{
|
||||
spi->cs_gpiod = csgpiod;
|
||||
}
|
||||
|
||||
struct spi_message;
|
||||
|
||||
/**
|
||||
@ -1530,6 +1550,9 @@ extern void spi_unregister_device(struct spi_device *spi);
|
||||
extern const struct spi_device_id *
|
||||
spi_get_device_id(const struct spi_device *sdev);
|
||||
|
||||
extern const void *
|
||||
spi_get_device_match_data(const struct spi_device *sdev);
|
||||
|
||||
static inline bool
|
||||
spi_transfer_is_last(struct spi_controller *ctlr, struct spi_transfer *xfer)
|
||||
{
|
||||
|
@ -494,7 +494,6 @@ unsigned long module_kallsyms_lookup_name(const char *name)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIVEPATCH
|
||||
int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
|
||||
struct module *, unsigned long),
|
||||
void *data)
|
||||
@ -531,4 +530,3 @@ int module_kallsyms_on_each_symbol(int (*fn)(void *, const char *,
|
||||
mutex_unlock(&module_mutex);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_LIVEPATCH */
|
||||
|
@ -666,48 +666,6 @@ rb_time_read_cmpxchg(local_t *l, unsigned long expect, unsigned long set)
|
||||
return ret == expect;
|
||||
}
|
||||
|
||||
static int rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
|
||||
{
|
||||
unsigned long cnt, top, bottom, msb;
|
||||
unsigned long cnt2, top2, bottom2, msb2;
|
||||
u64 val;
|
||||
|
||||
/* Any interruptions in this function should cause a failure */
|
||||
cnt = local_read(&t->cnt);
|
||||
|
||||
/* The cmpxchg always fails if it interrupted an update */
|
||||
if (!__rb_time_read(t, &val, &cnt2))
|
||||
return false;
|
||||
|
||||
if (val != expect)
|
||||
return false;
|
||||
|
||||
if ((cnt & 3) != cnt2)
|
||||
return false;
|
||||
|
||||
cnt2 = cnt + 1;
|
||||
|
||||
rb_time_split(val, &top, &bottom, &msb);
|
||||
msb = rb_time_val_cnt(msb, cnt);
|
||||
top = rb_time_val_cnt(top, cnt);
|
||||
bottom = rb_time_val_cnt(bottom, cnt);
|
||||
|
||||
rb_time_split(set, &top2, &bottom2, &msb2);
|
||||
msb2 = rb_time_val_cnt(msb2, cnt);
|
||||
top2 = rb_time_val_cnt(top2, cnt2);
|
||||
bottom2 = rb_time_val_cnt(bottom2, cnt2);
|
||||
|
||||
if (!rb_time_read_cmpxchg(&t->cnt, cnt, cnt2))
|
||||
return false;
|
||||
if (!rb_time_read_cmpxchg(&t->msb, msb, msb2))
|
||||
return false;
|
||||
if (!rb_time_read_cmpxchg(&t->top, top, top2))
|
||||
return false;
|
||||
if (!rb_time_read_cmpxchg(&t->bottom, bottom, bottom2))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else /* 64 bits */
|
||||
|
||||
/* local64_t always succeeds */
|
||||
@ -721,13 +679,6 @@ static void rb_time_set(rb_time_t *t, u64 val)
|
||||
{
|
||||
local64_set(&t->time, val);
|
||||
}
|
||||
|
||||
static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
|
||||
{
|
||||
u64 val;
|
||||
val = local64_cmpxchg(&t->time, expect, set);
|
||||
return val == expect;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline bool has_ext_writer(struct trace_buffer *buffer)
|
||||
@ -906,9 +857,14 @@ static __always_inline bool full_hit(struct trace_buffer *buffer, int cpu, int f
|
||||
if (!nr_pages || !full)
|
||||
return true;
|
||||
|
||||
dirty = ring_buffer_nr_dirty_pages(buffer, cpu);
|
||||
/*
|
||||
* Add one as dirty will never equal nr_pages, as the sub-buffer
|
||||
* that the writer is on is not counted as dirty.
|
||||
* This is needed if "buffer_percent" is set to 100.
|
||||
*/
|
||||
dirty = ring_buffer_nr_dirty_pages(buffer, cpu) + 1;
|
||||
|
||||
return (dirty * 100) > (full * nr_pages);
|
||||
return (dirty * 100) >= (full * nr_pages);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -968,7 +924,8 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, int cpu)
|
||||
/* make sure the waiters see the new index */
|
||||
smp_wmb();
|
||||
|
||||
rb_wake_up_waiters(&rbwork->work);
|
||||
/* This can be called in any context */
|
||||
irq_work_queue(&rbwork->work);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2980,25 +2937,6 @@ static unsigned rb_calculate_event_length(unsigned length)
|
||||
return length;
|
||||
}
|
||||
|
||||
static u64 rb_time_delta(struct ring_buffer_event *event)
|
||||
{
|
||||
switch (event->type_len) {
|
||||
case RINGBUF_TYPE_PADDING:
|
||||
return 0;
|
||||
|
||||
case RINGBUF_TYPE_TIME_EXTEND:
|
||||
return rb_event_time_stamp(event);
|
||||
|
||||
case RINGBUF_TYPE_TIME_STAMP:
|
||||
return 0;
|
||||
|
||||
case RINGBUF_TYPE_DATA:
|
||||
return event->time_delta;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
struct ring_buffer_event *event)
|
||||
@ -3007,8 +2945,6 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
struct buffer_page *bpage;
|
||||
unsigned long index;
|
||||
unsigned long addr;
|
||||
u64 write_stamp;
|
||||
u64 delta;
|
||||
|
||||
new_index = rb_event_index(event);
|
||||
old_index = new_index + rb_event_ts_length(event);
|
||||
@ -3017,14 +2953,10 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
|
||||
bpage = READ_ONCE(cpu_buffer->tail_page);
|
||||
|
||||
delta = rb_time_delta(event);
|
||||
|
||||
if (!rb_time_read(&cpu_buffer->write_stamp, &write_stamp))
|
||||
return 0;
|
||||
|
||||
/* Make sure the write stamp is read before testing the location */
|
||||
barrier();
|
||||
|
||||
/*
|
||||
* Make sure the tail_page is still the same and
|
||||
* the next write location is the end of this event
|
||||
*/
|
||||
if (bpage->page == (void *)addr && rb_page_write(bpage) == old_index) {
|
||||
unsigned long write_mask =
|
||||
local_read(&bpage->write) & ~RB_WRITE_MASK;
|
||||
@ -3035,20 +2967,20 @@ rb_try_to_discard(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
* to make sure that the next event adds an absolute
|
||||
* value and does not rely on the saved write stamp, which
|
||||
* is now going to be bogus.
|
||||
*
|
||||
* By setting the before_stamp to zero, the next event
|
||||
* is not going to use the write_stamp and will instead
|
||||
* create an absolute timestamp. This means there's no
|
||||
* reason to update the wirte_stamp!
|
||||
*/
|
||||
rb_time_set(&cpu_buffer->before_stamp, 0);
|
||||
|
||||
/* Something came in, can't discard */
|
||||
if (!rb_time_cmpxchg(&cpu_buffer->write_stamp,
|
||||
write_stamp, write_stamp - delta))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If an event were to come in now, it would see that the
|
||||
* write_stamp and the before_stamp are different, and assume
|
||||
* that this event just added itself before updating
|
||||
* the write stamp. The interrupting event will fix the
|
||||
* write stamp for us, and use the before stamp as its delta.
|
||||
* write stamp for us, and use an absolute timestamp.
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -3631,20 +3563,36 @@ __rb_reserve_next(struct ring_buffer_per_cpu *cpu_buffer,
|
||||
} else {
|
||||
u64 ts;
|
||||
/* SLOW PATH - Interrupted between A and C */
|
||||
a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after);
|
||||
|
||||
/* Save the old before_stamp */
|
||||
a_ok = rb_time_read(&cpu_buffer->before_stamp, &info->before);
|
||||
RB_WARN_ON(cpu_buffer, !a_ok);
|
||||
|
||||
/*
|
||||
* Read a new timestamp and update the before_stamp to make
|
||||
* the next event after this one force using an absolute
|
||||
* timestamp. This is in case an interrupt were to come in
|
||||
* between E and F.
|
||||
*/
|
||||
ts = rb_time_stamp(cpu_buffer->buffer);
|
||||
rb_time_set(&cpu_buffer->before_stamp, ts);
|
||||
|
||||
barrier();
|
||||
/*E*/ a_ok = rb_time_read(&cpu_buffer->write_stamp, &info->after);
|
||||
/* Was interrupted before here, write_stamp must be valid */
|
||||
RB_WARN_ON(cpu_buffer, !a_ok);
|
||||
ts = rb_time_stamp(cpu_buffer->buffer);
|
||||
barrier();
|
||||
/*E*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) &&
|
||||
info->after < ts &&
|
||||
rb_time_cmpxchg(&cpu_buffer->write_stamp,
|
||||
info->after, ts)) {
|
||||
/* Nothing came after this event between C and E */
|
||||
/*F*/ if (write == (local_read(&tail_page->write) & RB_WRITE_MASK) &&
|
||||
info->after == info->before && info->after < ts) {
|
||||
/*
|
||||
* Nothing came after this event between C and F, it is
|
||||
* safe to use info->after for the delta as it
|
||||
* matched info->before and is still valid.
|
||||
*/
|
||||
info->delta = ts - info->after;
|
||||
} else {
|
||||
/*
|
||||
* Interrupted between C and E:
|
||||
* Interrupted between C and F:
|
||||
* Lost the previous events time stamp. Just set the
|
||||
* delta to zero, and this will be the same time as
|
||||
* the event this event interrupted. And the events that
|
||||
|
@ -1851,6 +1851,9 @@ update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu,
|
||||
__update_max_tr(tr, tsk, cpu);
|
||||
|
||||
arch_spin_unlock(&tr->max_lock);
|
||||
|
||||
/* Any waiters on the old snapshot buffer need to wake up */
|
||||
ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1902,12 +1905,23 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
|
||||
|
||||
static int wait_on_pipe(struct trace_iterator *iter, int full)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Iterators are static, they should be filled or empty */
|
||||
if (trace_buffer_iter(iter, iter->cpu_file))
|
||||
return 0;
|
||||
|
||||
return ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file,
|
||||
full);
|
||||
ret = ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, full);
|
||||
|
||||
#ifdef CONFIG_TRACER_MAX_TRACE
|
||||
/*
|
||||
* Make sure this is still the snapshot buffer, as if a snapshot were
|
||||
* to happen, this would now be the main buffer.
|
||||
*/
|
||||
if (iter->snapshot)
|
||||
iter->array_buffer = &iter->tr->max_buffer;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FTRACE_STARTUP_TEST
|
||||
@ -8387,7 +8401,7 @@ tracing_buffers_splice_read(struct file *file, loff_t *ppos,
|
||||
|
||||
wait_index = READ_ONCE(iter->wait_index);
|
||||
|
||||
ret = wait_on_pipe(iter, iter->tr->buffer_percent);
|
||||
ret = wait_on_pipe(iter, iter->snapshot ? 0 : iter->tr->buffer_percent);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -714,14 +714,31 @@ static int count_symbols(void *data, unsigned long unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sym_count_ctx {
|
||||
unsigned int count;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static int count_mod_symbols(void *data, const char *name,
|
||||
struct module *module, unsigned long unused)
|
||||
{
|
||||
struct sym_count_ctx *ctx = data;
|
||||
|
||||
if (strcmp(name, ctx->name) == 0)
|
||||
ctx->count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int number_of_same_symbols(char *func_name)
|
||||
{
|
||||
unsigned int count;
|
||||
struct sym_count_ctx ctx = { .count = 0, .name = func_name };
|
||||
|
||||
count = 0;
|
||||
kallsyms_on_each_match_symbol(count_symbols, func_name, &count);
|
||||
kallsyms_on_each_match_symbol(count_symbols, func_name, &ctx.count);
|
||||
|
||||
return count;
|
||||
module_kallsyms_on_each_symbol(count_mod_symbols, &ctx);
|
||||
|
||||
return ctx.count;
|
||||
}
|
||||
|
||||
static int __trace_kprobe_create(int argc, const char *argv[])
|
||||
|
@ -2762,6 +2762,15 @@ ssize_t filemap_read(struct kiocb *iocb, struct iov_iter *iter,
|
||||
goto put_folios;
|
||||
end_offset = min_t(loff_t, isize, iocb->ki_pos + iter->count);
|
||||
|
||||
/*
|
||||
* Pairs with a barrier in
|
||||
* block_write_end()->mark_buffer_dirty() or other page
|
||||
* dirtying routines like iomap_write_end() to ensure
|
||||
* changes to page contents are visible before we see
|
||||
* increased inode size.
|
||||
*/
|
||||
smp_rmb();
|
||||
|
||||
/*
|
||||
* Once we start copying data, we don't want to be touching any
|
||||
* cachelines that might be contended:
|
||||
|
@ -1422,7 +1422,7 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
|
||||
* This check implies we don't kill processes if their pages
|
||||
* are in the swap cache early. Those are always late kills.
|
||||
*/
|
||||
if (!page_mapped(hpage))
|
||||
if (!page_mapped(p))
|
||||
return true;
|
||||
|
||||
if (PageKsm(p)) {
|
||||
@ -1478,10 +1478,10 @@ static bool hwpoison_user_mappings(struct page *p, unsigned long pfn,
|
||||
try_to_unmap(folio, ttu);
|
||||
}
|
||||
|
||||
unmap_success = !page_mapped(hpage);
|
||||
unmap_success = !page_mapped(p);
|
||||
if (!unmap_success)
|
||||
pr_err("%#lx: failed to unmap page (mapcount=%d)\n",
|
||||
pfn, page_mapcount(hpage));
|
||||
pfn, page_mapcount(p));
|
||||
|
||||
/*
|
||||
* try_to_unmap() might put mlocked page in lru cache, so call
|
||||
@ -1561,7 +1561,7 @@ static void unmap_and_kill(struct list_head *to_kill, unsigned long pfn,
|
||||
* mapping being torn down is communicated in siginfo, see
|
||||
* kill_proc()
|
||||
*/
|
||||
loff_t start = (index << PAGE_SHIFT) & ~(size - 1);
|
||||
loff_t start = ((loff_t)index << PAGE_SHIFT) & ~(size - 1);
|
||||
|
||||
unmap_mapping_range(mapping, start, size, 0);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user