Import of kernel-5.14.0-687.15.1.el9_8

This commit is contained in:
almalinux-bot-kernel 2026-06-12 05:40:20 +00:00
parent a19517f55f
commit 9bb58011ba
59 changed files with 1828 additions and 896 deletions

View File

@ -250,6 +250,24 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
=============================== ========================
Frequency monitor
=================
Some DPLL devices may offer the capability to measure the actual
frequency of all available input pins. The attribute and current feature state
shall be included in the response message of the ``DPLL_CMD_DEVICE_GET``
command for supported DPLL devices. In such cases, users can also control
the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the
``enum dpll_feature_state`` values for the attribute.
Once enabled the measured input frequency for each input pin shall be
returned in the ``DPLL_A_PIN_MEASURED_FREQUENCY`` attribute. The value
is in millihertz (mHz), using ``DPLL_PIN_MEASURED_FREQUENCY_DIVIDER``
as the divider.
=============================== ========================
``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature
=============================== ========================
Embedded SYNC
=============
@ -411,6 +429,8 @@ according to attribute purpose.
``DPLL_A_PIN_STATE`` attr state of pin on the parent
pin
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
``DPLL_A_PIN_MEASURED_FREQUENCY`` attr measured frequency of
an input pin in mHz
==================================== ==================================
==================================== =================================

View File

@ -240,6 +240,20 @@ definitions:
integer part of a measured phase offset value.
Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
fractional part of a measured phase offset value.
-
type: const
name: pin-measured-frequency-divider
value: 1000
doc: |
pin measured frequency divider allows userspace to calculate
a value of measured input frequency as a fractional value with
three digit decimal precision (millihertz).
Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part of
a measured frequency value.
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of
a measured frequency value.
-
type: enum
name: feature-state
@ -319,6 +333,13 @@ attribute-sets:
name: phase-offset-avg-factor
type: u32
doc: Averaging factor applied to calculation of reported phase offset.
-
name: frequency-monitor
type: u32
enum: feature-state
doc: Current or desired state of the frequency monitor feature.
If enabled, dpll device shall measure all currently available
inputs for their actual input frequency.
-
name: pin
enum-name: dpll_a_pin
@ -456,6 +477,17 @@ attribute-sets:
Value is in PPT (parts per trillion, 10^-12).
Note: This attribute provides higher resolution than the standard
fractional-frequency-offset (which is in PPM).
-
name: measured-frequency
type: u64
doc: |
The measured frequency of the input pin in millihertz (mHz).
Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part (Hz)
of a measured frequency value.
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part
of a measured frequency value.
-
name: pin-parent-device
@ -544,6 +576,7 @@ operations:
- type
- phase-offset-monitor
- phase-offset-avg-factor
- frequency-monitor
dump:
reply: *dev-attrs
@ -563,6 +596,7 @@ operations:
- mode
- phase-offset-monitor
- phase-offset-avg-factor
- frequency-monitor
-
name: device-create-ntf
doc: Notification about device appearing
@ -643,6 +677,7 @@ operations:
- esync-frequency-supported
- esync-pulse
- reference-sync
- measured-frequency
dump:
request:

View File

@ -86,6 +86,7 @@ struct kvm_task_sleep_node {
struct swait_queue_head wq;
u32 token;
int cpu;
bool dummy;
};
static struct kvm_task_sleep_head {
@ -117,15 +118,26 @@ static bool kvm_async_pf_queue_task(u32 token, struct kvm_task_sleep_node *n)
raw_spin_lock(&b->lock);
e = _find_apf_task(b, token);
if (e) {
/* dummy entry exist -> wake up was delivered ahead of PF */
hlist_del(&e->link);
struct kvm_task_sleep_node *dummy = NULL;
/*
* The entry can either be a 'dummy' entry (which is put on the
* list when wake-up happens ahead of APF handling completion)
* or a token from another task which should not be touched.
*/
if (e->dummy) {
hlist_del(&e->link);
dummy = e;
}
raw_spin_unlock(&b->lock);
kfree(e);
kfree(dummy);
return false;
}
n->token = token;
n->cpu = smp_processor_id();
n->dummy = false;
init_swait_queue_head(&n->wq);
hlist_add_head(&n->link, &b->list);
raw_spin_unlock(&b->lock);
@ -228,6 +240,7 @@ again:
}
dummy->token = token;
dummy->cpu = smp_processor_id();
dummy->dummy = true;
init_swait_queue_head(&dummy->wq);
hlist_add_head(&dummy->link, &b->list);
dummy = NULL;

View File

@ -39,7 +39,7 @@ struct authenc_request_ctx {
static void authenc_request_complete(struct aead_request *req, int err)
{
if (err != -EINPROGRESS)
if (err != -EINPROGRESS && err != -EBUSY)
aead_request_complete(req, err);
}
@ -109,27 +109,42 @@ out:
return err;
}
static void authenc_geniv_ahash_done(struct crypto_async_request *areq, int err)
static void authenc_geniv_ahash_finish(struct aead_request *req)
{
struct aead_request *req = areq->data;
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(authenc);
struct authenc_instance_ctx *ictx = aead_instance_ctx(inst);
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
if (err)
goto out;
scatterwalk_map_and_copy(ahreq->result, req->dst,
req->assoclen + req->cryptlen,
crypto_aead_authsize(authenc), 1);
}
out:
static void authenc_geniv_ahash_done(struct crypto_async_request *areq, int err)
{
struct aead_request *req = areq->data;
if (!err)
authenc_geniv_ahash_finish(req);
aead_request_complete(req, err);
}
static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
/*
* Used when the ahash request was invoked in the async callback context
* of the previous skcipher request. Eat any EINPROGRESS notifications.
*/
static void authenc_geniv_ahash_done2(struct crypto_async_request *areq, int err)
{
struct aead_request *req = areq->data;
if (!err)
authenc_geniv_ahash_finish(req);
authenc_request_complete(req, err);
}
static int crypto_authenc_genicv(struct aead_request *req, unsigned int mask)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(authenc);
@ -138,6 +153,7 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
struct crypto_ahash *auth = ctx->auth;
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
unsigned int flags = aead_request_flags(req) & ~mask;
u8 *hash = areq_ctx->tail;
int err;
@ -148,7 +164,8 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
ahash_request_set_crypt(ahreq, req->dst, hash,
req->assoclen + req->cryptlen);
ahash_request_set_callback(ahreq, flags,
authenc_geniv_ahash_done, req);
mask ? authenc_geniv_ahash_done2 :
authenc_geniv_ahash_done, req);
err = crypto_ahash_digest(ahreq);
if (err)
@ -165,12 +182,11 @@ static void crypto_authenc_encrypt_done(struct crypto_async_request *req,
{
struct aead_request *areq = req->data;
if (err)
goto out;
err = crypto_authenc_genicv(areq, 0);
out:
if (err) {
aead_request_complete(areq, err);
return;
}
err = crypto_authenc_genicv(areq, CRYPTO_TFM_REQ_MAY_SLEEP);
authenc_request_complete(areq, err);
}
@ -223,11 +239,18 @@ static int crypto_authenc_encrypt(struct aead_request *req)
if (err)
return err;
return crypto_authenc_genicv(req, aead_request_flags(req));
return crypto_authenc_genicv(req, 0);
}
static void authenc_decrypt_tail_done(struct crypto_async_request *areq, int err)
{
struct aead_request *req = areq->data;
authenc_request_complete(req, err);
}
static int crypto_authenc_decrypt_tail(struct aead_request *req,
unsigned int flags)
unsigned int mask)
{
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
struct aead_instance *inst = aead_alg_instance(authenc);
@ -238,6 +261,7 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
struct skcipher_request *skreq = (void *)(areq_ctx->tail +
ictx->reqoff);
unsigned int authsize = crypto_aead_authsize(authenc);
unsigned int flags = aead_request_flags(req) & ~mask;
u8 *ihash = ahreq->result + authsize;
struct scatterlist *src, *dst;
@ -253,8 +277,10 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
dst = scatterwalk_ffwd(areq_ctx->dst, req->dst, req->assoclen);
skcipher_request_set_tfm(skreq, ctx->enc);
skcipher_request_set_callback(skreq, aead_request_flags(req),
req->base.complete, req->base.data);
skcipher_request_set_callback(skreq, flags,
mask ? authenc_decrypt_tail_done :
req->base.complete,
mask ? req : req->base.data);
skcipher_request_set_crypt(skreq, src, dst,
req->cryptlen - authsize, req->iv);
@ -266,12 +292,11 @@ static void authenc_verify_ahash_done(struct crypto_async_request *areq,
{
struct aead_request *req = areq->data;
if (err)
goto out;
err = crypto_authenc_decrypt_tail(req, 0);
out:
if (err) {
aead_request_complete(req, err);
return;
}
err = crypto_authenc_decrypt_tail(req, CRYPTO_TFM_REQ_MAY_SLEEP);
authenc_request_complete(req, err);
}
@ -301,7 +326,7 @@ static int crypto_authenc_decrypt(struct aead_request *req)
if (err)
return err;
return crypto_authenc_decrypt_tail(req, aead_request_flags(req));
return crypto_authenc_decrypt_tail(req, 0);
}
static int crypto_authenc_init_tfm(struct crypto_aead *tfm)

View File

@ -880,7 +880,10 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
if (WARN_ON(!ops) ||
WARN_ON(!ops->state_on_dpll_get) ||
WARN_ON(!ops->direction_get))
WARN_ON(!ops->direction_get) ||
WARN_ON(ops->measured_freq_get &&
(!dpll_device_ops(dpll)->freq_monitor_get ||
!dpll_device_ops(dpll)->freq_monitor_set)))
return -EINVAL;
mutex_lock(&dpll_lock);

View File

@ -175,6 +175,26 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
return 0;
}
static int
dpll_msg_add_freq_monitor(struct sk_buff *msg, struct dpll_device *dpll,
struct netlink_ext_ack *extack)
{
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
enum dpll_feature_state state;
int ret;
if (ops->freq_monitor_set && ops->freq_monitor_get) {
ret = ops->freq_monitor_get(dpll, dpll_priv(dpll),
&state, extack);
if (ret)
return ret;
if (nla_put_u32(msg, DPLL_A_FREQUENCY_MONITOR, state))
return -EMSGSIZE;
}
return 0;
}
static int
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
struct dpll_device *dpll,
@ -400,6 +420,38 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
ffo);
}
static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
struct dpll_pin_ref *ref,
struct netlink_ext_ack *extack)
{
const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
struct dpll_device *dpll = ref->dpll;
enum dpll_feature_state state;
u64 measured_freq;
int ret;
if (!ops->measured_freq_get)
return 0;
ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
&state, extack);
if (ret)
return ret;
if (state == DPLL_FEATURE_STATE_DISABLE)
return 0;
ret = ops->measured_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
dpll, dpll_priv(dpll), &measured_freq,
extack);
if (ret)
return ret;
if (nla_put_64bit(msg, DPLL_A_PIN_MEASURED_FREQUENCY,
sizeof(measured_freq), &measured_freq,
DPLL_A_PIN_PAD))
return -EMSGSIZE;
return 0;
}
static int
dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
@ -670,6 +722,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
if (ret)
return ret;
ret = dpll_msg_add_ffo(msg, pin, ref, extack);
if (ret)
return ret;
ret = dpll_msg_add_measured_freq(msg, pin, ref, extack);
if (ret)
return ret;
ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
@ -722,6 +777,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
if (ret)
return ret;
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
if (ret)
return ret;
ret = dpll_msg_add_freq_monitor(msg, dpll, extack);
if (ret)
return ret;
@ -958,6 +1016,32 @@ dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
extack);
}
static int
dpll_freq_monitor_set(struct dpll_device *dpll, struct nlattr *a,
struct netlink_ext_ack *extack)
{
const struct dpll_device_ops *ops = dpll_device_ops(dpll);
enum dpll_feature_state state = nla_get_u32(a), old_state;
int ret;
if (!(ops->freq_monitor_set && ops->freq_monitor_get)) {
NL_SET_ERR_MSG_ATTR(extack, a,
"dpll device not capable of frequency monitor");
return -EOPNOTSUPP;
}
ret = ops->freq_monitor_get(dpll, dpll_priv(dpll), &old_state,
extack);
if (ret) {
NL_SET_ERR_MSG(extack,
"unable to get current state of frequency monitor");
return ret;
}
if (state == old_state)
return 0;
return ops->freq_monitor_set(dpll, dpll_priv(dpll), state, extack);
}
static int
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
struct netlink_ext_ack *extack)
@ -1888,6 +1972,12 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
if (ret)
return ret;
break;
case DPLL_A_FREQUENCY_MONITOR:
ret = dpll_freq_monitor_set(dpll, a,
info->extack);
if (ret)
return ret;
break;
}
}

View File

@ -42,11 +42,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
};
/* DPLL_CMD_DEVICE_SET - do */
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = {
[DPLL_A_ID] = { .type = NLA_U32, },
[DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
[DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
};
/* DPLL_CMD_PIN_ID_GET - do */
@ -114,7 +115,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
.doit = dpll_nl_device_set_doit,
.post_doit = dpll_post_doit,
.policy = dpll_device_set_nl_policy,
.maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
.maxattr = DPLL_A_FREQUENCY_MONITOR,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{

View File

@ -1,8 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_ZL3073X) += zl3073x.o
zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \
out.o prop.o ref.o synth.o
zl3073x-objs := chan.o core.o devlink.o dpll.o \
flash.o fw.o out.o prop.o ref.o synth.o
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
zl3073x_i2c-objs := i2c.o

165
drivers/dpll/zl3073x/chan.c Normal file
View File

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/cleanup.h>
#include <linux/dev_printk.h>
#include <linux/string.h>
#include <linux/types.h>
#include "chan.h"
#include "core.h"
/**
* zl3073x_chan_state_update - update DPLL channel status from HW
* @zldev: pointer to zl3073x_dev structure
* @index: DPLL channel index
*
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_chan *chan = &zldev->chan[index];
int rc;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),
&chan->mon_status);
if (rc)
return rc;
return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
&chan->refsel_status);
}
/**
* zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
* @zldev: pointer to zl3073x_dev structure
* @index: DPLL channel index to fetch state for
*
* Reads the mode_refsel register and reference priority registers for
* the given DPLL channel and stores the raw values for later use.
*
* Return: 0 on success, <0 on error
*/
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_chan *chan = &zldev->chan[index];
int rc, i;
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
&chan->mode_refsel);
if (rc)
return rc;
dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index,
zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));
rc = zl3073x_chan_state_update(zldev, index);
if (rc)
return rc;
dev_dbg(zldev->dev,
"DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n",
index, zl3073x_chan_lock_state_get(chan),
zl3073x_chan_is_ho_ready(chan) ? 1 : 0,
zl3073x_chan_refsel_state_get(chan),
zl3073x_chan_refsel_ref_get(chan));
guard(mutex)(&zldev->multiop_lock);
/* Read DPLL configuration from mailbox */
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
ZL_REG_DPLL_MB_MASK, BIT(index));
if (rc)
return rc;
/* Read reference priority registers */
for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i),
&chan->ref_prio[i]);
if (rc)
return rc;
}
return 0;
}
/**
* zl3073x_chan_state_get - get current DPLL channel state
* @zldev: pointer to zl3073x_dev structure
* @index: DPLL channel index to get state for
*
* Return: pointer to given DPLL channel state
*/
const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
u8 index)
{
return &zldev->chan[index];
}
/**
* zl3073x_chan_state_set - commit DPLL channel state changes to hardware
* @zldev: pointer to zl3073x_dev structure
* @index: DPLL channel index to set state for
* @chan: desired channel state
*
* Skips the HW write if the configuration is unchanged, and otherwise
* writes only the changed registers to hardware. The mode_refsel register
* is written directly, while the reference priority registers are written
* via the DPLL mailbox interface.
*
* Return: 0 on success, <0 on HW error
*/
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_chan *chan)
{
struct zl3073x_chan *dchan = &zldev->chan[index];
int rc, i;
/* Skip HW write if configuration hasn't changed */
if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg)))
return 0;
/* Direct register write for mode_refsel */
if (dchan->mode_refsel != chan->mode_refsel) {
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
chan->mode_refsel);
if (rc)
return rc;
dchan->mode_refsel = chan->mode_refsel;
}
/* Mailbox write for ref_prio if changed */
if (!memcmp(dchan->ref_prio, chan->ref_prio, sizeof(chan->ref_prio))) {
dchan->cfg = chan->cfg;
return 0;
}
guard(mutex)(&zldev->multiop_lock);
/* Read DPLL configuration into mailbox */
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
ZL_REG_DPLL_MB_MASK, BIT(index));
if (rc)
return rc;
/* Update changed ref_prio registers */
for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
if (dchan->ref_prio[i] != chan->ref_prio[i]) {
rc = zl3073x_write_u8(zldev,
ZL_REG_DPLL_REF_PRIO(i),
chan->ref_prio[i]);
if (rc)
return rc;
}
}
/* Commit DPLL configuration */
rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
ZL_REG_DPLL_MB_MASK, BIT(index));
if (rc)
return rc;
/* After successful write store new state */
dchan->cfg = chan->cfg;
return 0;
}

174
drivers/dpll/zl3073x/chan.h Normal file
View File

@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _ZL3073X_CHAN_H
#define _ZL3073X_CHAN_H
#include <linux/bitfield.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include "regs.h"
struct zl3073x_dev;
/**
* struct zl3073x_chan - DPLL channel state
* @mode_refsel: mode and reference selection register value
* @ref_prio: reference priority registers (4 bits per ref, P/N packed)
* @mon_status: monitor status register value
* @refsel_status: reference selection status register value
*/
struct zl3073x_chan {
struct_group(cfg,
u8 mode_refsel;
u8 ref_prio[ZL3073X_NUM_REFS / 2];
);
struct_group(stat,
u8 mon_status;
u8 refsel_status;
);
};
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
u8 index);
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_chan *chan);
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
/**
* zl3073x_chan_mode_get - get DPLL channel operating mode
* @chan: pointer to channel state
*
* Return: reference selection mode of the given DPLL channel
*/
static inline u8 zl3073x_chan_mode_get(const struct zl3073x_chan *chan)
{
return FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, chan->mode_refsel);
}
/**
* zl3073x_chan_ref_get - get manually selected reference
* @chan: pointer to channel state
*
* Return: reference selected in forced reference lock mode
*/
static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)
{
return FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, chan->mode_refsel);
}
/**
* zl3073x_chan_mode_set - set DPLL channel operating mode
* @chan: pointer to channel state
* @mode: mode to set
*/
static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
{
FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_MODE, &chan->mode_refsel, mode);
}
/**
* zl3073x_chan_ref_set - set manually selected reference
* @chan: pointer to channel state
* @ref: reference to set
*/
static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
{
FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_REF, &chan->mode_refsel, ref);
}
/**
* zl3073x_chan_ref_prio_get - get reference priority
* @chan: pointer to channel state
* @ref: input reference index
*
* Return: priority of the given reference <0, 15>
*/
static inline u8
zl3073x_chan_ref_prio_get(const struct zl3073x_chan *chan, u8 ref)
{
u8 val = chan->ref_prio[ref / 2];
if (!(ref & 1))
return FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, val);
else
return FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, val);
}
/**
* zl3073x_chan_ref_prio_set - set reference priority
* @chan: pointer to channel state
* @ref: input reference index
* @prio: priority to set
*/
static inline void
zl3073x_chan_ref_prio_set(struct zl3073x_chan *chan, u8 ref, u8 prio)
{
u8 *val = &chan->ref_prio[ref / 2];
if (!(ref & 1))
FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_P, val, prio);
else
FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_N, val, prio);
}
/**
* zl3073x_chan_ref_is_selectable - check if reference is selectable
* @chan: pointer to channel state
* @ref: input reference index
*
* Return: true if the reference priority is not NONE, false otherwise
*/
static inline bool
zl3073x_chan_ref_is_selectable(const struct zl3073x_chan *chan, u8 ref)
{
return zl3073x_chan_ref_prio_get(chan, ref) != ZL_DPLL_REF_PRIO_NONE;
}
/**
* zl3073x_chan_lock_state_get - get DPLL channel lock state
* @chan: pointer to channel state
*
* Return: lock state of the given DPLL channel
*/
static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
{
return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
}
/**
* zl3073x_chan_is_ho_ready - check if holdover is ready
* @chan: pointer to channel state
*
* Return: true if holdover is ready, false otherwise
*/
static inline bool zl3073x_chan_is_ho_ready(const struct zl3073x_chan *chan)
{
return !!FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, chan->mon_status);
}
/**
* zl3073x_chan_refsel_state_get - get reference selection state
* @chan: pointer to channel state
*
* Return: reference selection state of the given DPLL channel
*/
static inline u8 zl3073x_chan_refsel_state_get(const struct zl3073x_chan *chan)
{
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, chan->refsel_status);
}
/**
* zl3073x_chan_refsel_ref_get - get currently selected reference in auto mode
* @chan: pointer to channel state
*
* Return: reference selected by the DPLL in automatic mode
*/
static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
{
return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
}
#endif /* _ZL3073X_CHAN_H */

View File

@ -20,78 +20,29 @@
#include "dpll.h"
#include "regs.h"
/* Chip IDs for zl30731 */
static const u16 zl30731_ids[] = {
0x0E93,
0x1E93,
0x2E93,
};
#define ZL_CHIP_INFO(_id, _nchannels, _flags) \
{ .id = (_id), .num_channels = (_nchannels), .flags = (_flags) }
const struct zl3073x_chip_info zl30731_chip_info = {
.ids = zl30731_ids,
.num_ids = ARRAY_SIZE(zl30731_ids),
.num_channels = 1,
static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
ZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x1E93, 1, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x1E94, 2, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x1E95, 3, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x1E96, 4, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x1E97, 5, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
ZL_CHIP_INFO(0x2E93, 1, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x2E94, 2, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x2E95, 3, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x2E96, 4, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x2E97, 5, ZL3073X_FLAG_DIE_TEMP),
ZL_CHIP_INFO(0x3FC4, 2, ZL3073X_FLAG_DIE_TEMP),
};
EXPORT_SYMBOL_NS_GPL(zl30731_chip_info, ZL3073X);
/* Chip IDs for zl30732 */
static const u16 zl30732_ids[] = {
0x0E30,
0x0E94,
0x1E94,
0x1F60,
0x2E94,
0x3FC4,
};
const struct zl3073x_chip_info zl30732_chip_info = {
.ids = zl30732_ids,
.num_ids = ARRAY_SIZE(zl30732_ids),
.num_channels = 2,
};
EXPORT_SYMBOL_NS_GPL(zl30732_chip_info, ZL3073X);
/* Chip IDs for zl30733 */
static const u16 zl30733_ids[] = {
0x0E95,
0x1E95,
0x2E95,
};
const struct zl3073x_chip_info zl30733_chip_info = {
.ids = zl30733_ids,
.num_ids = ARRAY_SIZE(zl30733_ids),
.num_channels = 3,
};
EXPORT_SYMBOL_NS_GPL(zl30733_chip_info, ZL3073X);
/* Chip IDs for zl30734 */
static const u16 zl30734_ids[] = {
0x0E96,
0x1E96,
0x2E96,
};
const struct zl3073x_chip_info zl30734_chip_info = {
.ids = zl30734_ids,
.num_ids = ARRAY_SIZE(zl30734_ids),
.num_channels = 4,
};
EXPORT_SYMBOL_NS_GPL(zl30734_chip_info, ZL3073X);
/* Chip IDs for zl30735 */
static const u16 zl30735_ids[] = {
0x0E97,
0x1E97,
0x2E97,
};
const struct zl3073x_chip_info zl30735_chip_info = {
.ids = zl30735_ids,
.num_ids = ARRAY_SIZE(zl30735_ids),
.num_channels = 5,
};
EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, ZL3073X);
#define ZL_RANGE_OFFSET 0x80
#define ZL_PAGE_SIZE 0x80
@ -588,17 +539,26 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
}
}
for (i = 0; i < zldev->info->num_channels; i++) {
rc = zl3073x_chan_state_fetch(zldev, i);
if (rc) {
dev_err(zldev->dev,
"Failed to fetch channel state: %pe\n",
ERR_PTR(rc));
return rc;
}
}
return rc;
}
static void
zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
{
int i, rc;
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
&zldev->ref[i].mon_status);
rc = zl3073x_ref_state_update(zldev, i);
if (rc)
dev_warn(zldev->dev,
"Failed to get REF%u status: %pe\n", i,
@ -606,6 +566,20 @@ zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
}
}
static void
zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
{
int i, rc;
for (i = 0; i < zldev->info->num_channels; i++) {
rc = zl3073x_chan_state_update(zldev, i);
if (rc)
dev_warn(zldev->dev,
"Failed to get DPLL%u state: %pe\n", i,
ERR_PTR(rc));
}
}
/**
* zl3073x_ref_phase_offsets_update - update reference phase offsets
* @zldev: pointer to zl3073x_dev structure
@ -658,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
}
/**
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
* zl3073x_ref_freq_meas_latch - latch reference frequency measurements
* @zldev: pointer to zl3073x_dev structure
* @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
*
* The function asks device to update fractional frequency offsets latch
* registers the latest measured values, reads and stores them into
* The function waits for the previous measurement to finish, selects all
* references and requests a new measurement of the given type.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
{
int i, rc;
int rc;
/* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
* to ensure that the measured data are coherent.
*/
/* Wait for previous measurement to finish */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
if (rc)
@ -689,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
if (rc)
return rc;
/* Request frequency offset measurement */
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
/* Request measurement */
rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
if (rc)
return rc;
/* Wait for finish */
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
ZL_REF_FREQ_MEAS_CTRL);
}
/**
* zl3073x_ref_freq_meas_update - update measured input reference frequencies
* @zldev: pointer to zl3073x_dev structure
*
* The function asks device to latch measured input reference frequencies
* and stores the results in the ref state.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
{
int i, rc;
rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
if (rc)
return rc;
/* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
u32 value;
rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
if (rc)
return rc;
zldev->ref[i].meas_freq = value;
}
return 0;
}
/**
* zl3073x_ref_ffo_update - update reference fractional frequency offsets
* @zldev: pointer to zl3073x_dev structure
*
* The function asks device to latch the latest measured fractional
* frequency offset values, reads and stores them into the ref state.
*
* Return: 0 on success, <0 on error
*/
static int
zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
{
int i, rc;
rc = zl3073x_ref_freq_meas_latch(zldev,
ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
if (rc)
return rc;
@ -728,8 +750,11 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
struct zl3073x_dpll *zldpll;
int rc;
/* Update input references status */
zl3073x_dev_ref_status_update(zldev);
/* Update input references' states */
zl3073x_dev_ref_states_update(zldev);
/* Update DPLL channels' states */
zl3073x_dev_chan_states_update(zldev);
/* Update DPLL-to-connected-ref phase offsets registers */
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
@ -737,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
ERR_PTR(rc));
/* Update measured input reference frequencies if any DPLL has
* frequency monitoring enabled.
*/
list_for_each_entry(zldpll, &zldev->dplls, list) {
if (zldpll->freq_monitor) {
rc = zl3073x_ref_freq_meas_update(zldev);
if (rc)
dev_warn(zldev->dev,
"Failed to update measured frequencies: %pe\n",
ERR_PTR(rc));
break;
}
}
/* Update references' fractional frequency offsets */
rc = zl3073x_ref_ffo_update(zldev);
if (rc)
@ -766,8 +805,7 @@ int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
value = (factor + 1) & 0x0f;
/* Update phase measurement control register */
dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
FIELD_MODIFY(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, &dpll_meas_ctrl, value);
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
if (rc)
return rc;
@ -942,7 +980,7 @@ static void zl3073x_dev_dpll_fini(void *ptr)
}
static int
zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
zl3073x_devm_dpll_init(struct zl3073x_dev *zldev)
{
struct kthread_worker *kworker;
struct zl3073x_dpll *zldpll;
@ -952,7 +990,7 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
INIT_LIST_HEAD(&zldev->dplls);
/* Allocate all DPLLs */
for (i = 0; i < num_dplls; i++) {
for (i = 0; i < zldev->info->num_channels; i++) {
zldpll = zl3073x_dpll_alloc(zldev, i);
if (IS_ERR(zldpll)) {
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
@ -994,14 +1032,12 @@ error:
/**
* zl3073x_dev_probe - initialize zl3073x device
* @zldev: pointer to zl3073x device
* @chip_info: chip info based on compatible
*
* Common initialization of zl3073x device structure.
*
* Returns: 0 on success, <0 on error
*/
int zl3073x_dev_probe(struct zl3073x_dev *zldev,
const struct zl3073x_chip_info *chip_info)
int zl3073x_dev_probe(struct zl3073x_dev *zldev)
{
u16 id, revision, fw_ver;
unsigned int i;
@ -1013,18 +1049,17 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
if (rc)
return rc;
/* Check it matches */
for (i = 0; i < chip_info->num_ids; i++) {
if (id == chip_info->ids[i])
/* Detect chip variant */
for (i = 0; i < ARRAY_SIZE(zl3073x_chip_ids); i++) {
if (zl3073x_chip_ids[i].id == id)
break;
}
if (i == chip_info->num_ids) {
if (i == ARRAY_SIZE(zl3073x_chip_ids))
return dev_err_probe(zldev->dev, -ENODEV,
"Unknown or non-match chip ID: 0x%0x\n",
id);
}
zldev->chip_id = id;
"Unknown chip ID: 0x%04x\n", id);
zldev->info = &zl3073x_chip_ids[i];
/* Read revision, firmware version and custom config version */
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
@ -1063,7 +1098,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
"Failed to initialize mutex\n");
/* Register DPLL channels */
rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
rc = zl3073x_devm_dpll_init(zldev);
if (rc)
return rc;

View File

@ -9,6 +9,7 @@
#include <linux/mutex.h>
#include <linux/types.h>
#include "chan.h"
#include "out.h"
#include "ref.h"
#include "regs.h"
@ -18,27 +19,39 @@ struct device;
struct regmap;
struct zl3073x_dpll;
/*
* Hardware limits for ZL3073x chip family
enum zl3073x_flags {
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
ZL3073X_FLAG_DIE_TEMP_BIT,
ZL3073X_FLAGS_NBITS /* must be last */
};
#define __ZL3073X_FLAG(name) BIT(ZL3073X_FLAG_ ## name ## _BIT)
#define ZL3073X_FLAG_REF_PHASE_COMP_32 __ZL3073X_FLAG(REF_PHASE_COMP_32)
#define ZL3073X_FLAG_DIE_TEMP __ZL3073X_FLAG(DIE_TEMP)
/**
* struct zl3073x_chip_info - chip variant identification
* @id: chip ID
* @num_channels: number of DPLL channels supported by this variant
* @flags: chip variant flags
*/
#define ZL3073X_MAX_CHANNELS 5
#define ZL3073X_NUM_REFS 10
#define ZL3073X_NUM_OUTS 10
#define ZL3073X_NUM_SYNTHS 5
#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
ZL3073X_NUM_OUTPUT_PINS)
struct zl3073x_chip_info {
u16 id;
u8 num_channels;
unsigned long flags;
};
/**
* struct zl3073x_dev - zl3073x device
* @dev: pointer to device
* @regmap: regmap to access device registers
* @info: detected chip info
* @multiop_lock: to serialize multiple register operations
* @chip_id: chip ID read from hardware
* @ref: array of input references' invariants
* @out: array of outs' invariants
* @synth: array of synths' invariants
* @chan: array of DPLL channels' state
* @dplls: list of DPLLs
* @kworker: thread for periodic work
* @work: periodic work
@ -46,15 +59,16 @@ struct zl3073x_dpll;
* @phase_avg_factor: phase offset measurement averaging factor
*/
struct zl3073x_dev {
struct device *dev;
struct regmap *regmap;
struct mutex multiop_lock;
u16 chip_id;
struct device *dev;
struct regmap *regmap;
const struct zl3073x_chip_info *info;
struct mutex multiop_lock;
/* Invariants */
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
struct zl3073x_out out[ZL3073X_NUM_OUTS];
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
struct zl3073x_chan chan[ZL3073X_MAX_CHANNELS];
/* DPLL channels */
struct list_head dplls;
@ -68,22 +82,10 @@ struct zl3073x_dev {
u8 phase_avg_factor;
};
struct zl3073x_chip_info {
const u16 *ids;
size_t num_ids;
int num_channels;
};
extern const struct zl3073x_chip_info zl30731_chip_info;
extern const struct zl3073x_chip_info zl30732_chip_info;
extern const struct zl3073x_chip_info zl30733_chip_info;
extern const struct zl3073x_chip_info zl30734_chip_info;
extern const struct zl3073x_chip_info zl30735_chip_info;
extern const struct regmap_config zl3073x_regmap_config;
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);
int zl3073x_dev_probe(struct zl3073x_dev *zldev,
const struct zl3073x_chip_info *chip_info);
int zl3073x_dev_probe(struct zl3073x_dev *zldev);
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
@ -158,18 +160,7 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
static inline bool
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
{
switch (zldev->chip_id) {
case 0x0E30:
case 0x0E93:
case 0x0E94:
case 0x0E95:
case 0x0E96:
case 0x0E97:
case 0x1F60:
return true;
default:
return false;
}
return zldev->info->flags & ZL3073X_FLAG_REF_PHASE_COMP_32;
}
static inline bool

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,10 @@
* @list: this DPLL list entry
* @dev: pointer to multi-function parent device
* @id: DPLL index
* @refsel_mode: reference selection mode
* @forced_ref: selected reference in forced reference lock mode
* @check_count: periodic check counter
* @phase_monitor: is phase offset monitor enabled
* @freq_monitor: is frequency monitor enabled
* @ops: DPLL device operations for this instance
* @dpll_dev: pointer to registered DPLL device
* @tracker: tracking object for the acquired reference
* @lock_status: last saved DPLL lock status
@ -27,10 +27,10 @@ struct zl3073x_dpll {
struct list_head list;
struct zl3073x_dev *dev;
u8 id;
u8 refsel_mode;
u8 forced_ref;
u8 check_count;
bool phase_monitor;
bool freq_monitor;
struct dpll_device_ops ops;
struct dpll_device *dpll_dev;
dpll_tracker tracker;
enum dpll_lock_status lock_status;

View File

@ -194,8 +194,7 @@ zl3073x_flash_cmd_wait(struct zl3073x_dev *zldev, u32 operation,
if (rc)
return rc;
value &= ~ZL_WRITE_FLASH_OP;
value |= FIELD_PREP(ZL_WRITE_FLASH_OP, operation);
FIELD_MODIFY(ZL_WRITE_FLASH_OP, &value, operation);
rc = zl3073x_write_u8(zldev, ZL_REG_WRITE_FLASH, value);
if (rc)

View File

@ -22,40 +22,25 @@ static int zl3073x_i2c_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
"Failed to initialize regmap\n");
return zl3073x_dev_probe(zldev, i2c_get_match_data(client));
return zl3073x_dev_probe(zldev);
}
static const struct i2c_device_id zl3073x_i2c_id[] = {
{
.name = "zl30731",
.driver_data = (kernel_ulong_t)&zl30731_chip_info,
},
{
.name = "zl30732",
.driver_data = (kernel_ulong_t)&zl30732_chip_info,
},
{
.name = "zl30733",
.driver_data = (kernel_ulong_t)&zl30733_chip_info,
},
{
.name = "zl30734",
.driver_data = (kernel_ulong_t)&zl30734_chip_info,
},
{
.name = "zl30735",
.driver_data = (kernel_ulong_t)&zl30735_chip_info,
},
{ "zl30731" },
{ "zl30732" },
{ "zl30733" },
{ "zl30734" },
{ "zl30735" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id);
static const struct of_device_id zl3073x_i2c_of_match[] = {
{ .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
{ .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
{ .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
{ .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
{ .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
{ .compatible = "microchip,zl30731" },
{ .compatible = "microchip,zl30732" },
{ .compatible = "microchip,zl30733" },
{ .compatible = "microchip,zl30734" },
{ .compatible = "microchip,zl30735" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match);

View File

@ -106,12 +106,32 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
return &zldev->out[index];
}
/**
* zl3073x_out_state_set - commit output state changes to hardware
* @zldev: pointer to zl3073x_dev structure
* @index: output index to set state for
* @out: desired output state
*
* Validates that invariant fields have not been modified, skips the HW
* write if the mutable configuration is unchanged, and otherwise writes
* only the changed cfg fields to hardware via the mailbox interface.
*
* Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
*/
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_out *out)
{
struct zl3073x_out *dout = &zldev->out[index];
int rc;
/* Reject attempts to change invariant fields (set at fetch only) */
if (WARN_ON(memcmp(&dout->inv, &out->inv, sizeof(out->inv))))
return -EINVAL;
/* Skip HW write if configuration hasn't changed */
if (!memcmp(&dout->cfg, &out->cfg, sizeof(out->cfg)))
return 0;
guard(mutex)(&zldev->multiop_lock);
/* Read output configuration into mailbox */
@ -146,12 +166,7 @@ int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
return rc;
/* After successful commit store new state */
dout->div = out->div;
dout->width = out->width;
dout->esync_n_period = out->esync_n_period;
dout->esync_n_width = out->esync_n_width;
dout->mode = out->mode;
dout->phase_comp = out->phase_comp;
dout->cfg = out->cfg;
return 0;
}

View File

@ -4,6 +4,7 @@
#define _ZL3073X_OUT_H
#include <linux/bitfield.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include "regs.h"
@ -17,17 +18,21 @@ struct zl3073x_dev;
* @esync_n_period: embedded sync or n-pin period (for n-div formats)
* @esync_n_width: embedded sync or n-pin pulse width
* @phase_comp: phase compensation
* @ctrl: output control
* @mode: output mode
* @ctrl: output control
*/
struct zl3073x_out {
u32 div;
u32 width;
u32 esync_n_period;
u32 esync_n_width;
s32 phase_comp;
u8 ctrl;
u8 mode;
struct_group(cfg, /* Config */
u32 div;
u32 width;
u32 esync_n_period;
u32 esync_n_width;
s32 phase_comp;
u8 mode;
);
struct_group(inv, /* Invariants */
u8 ctrl;
);
};
int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);
@ -37,6 +42,28 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_out *out);
/**
* zl3073x_out_clock_type_get - get output clock type
* @out: pointer to out state
*
* Return: clock type of given output (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
*/
static inline u8 zl3073x_out_clock_type_get(const struct zl3073x_out *out)
{
return FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
}
/**
* zl3073x_out_clock_type_set - set output clock type
* @out: pointer to out state
* @type: clock type (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
*/
static inline void
zl3073x_out_clock_type_set(struct zl3073x_out *out, u8 type)
{
FIELD_MODIFY(ZL_OUTPUT_MODE_CLOCK_TYPE, &out->mode, type);
}
/**
* zl3073x_out_signal_format_get - get output signal format
* @out: pointer to out state

View File

@ -51,6 +51,21 @@ zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
return -EINVAL;
}
/**
* zl3073x_ref_state_update - update input reference status from HW
* @zldev: pointer to zl3073x_dev structure
* @index: input reference index
*
* Return: 0 on success, <0 on error
*/
int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index)
{
struct zl3073x_ref *ref = &zldev->ref[index];
return zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(index),
&ref->mon_status);
}
/**
* zl3073x_ref_state_fetch - fetch input reference state from hardware
* @zldev: pointer to zl3073x_dev structure
@ -73,18 +88,17 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
/* Copy the shared items from the P-pin */
ref->config = p_ref->config;
ref->esync_n_div = p_ref->esync_n_div;
ref->freq_base = p_ref->freq_base;
ref->freq_mult = p_ref->freq_mult;
ref->freq_ratio_m = p_ref->freq_ratio_m;
ref->freq_ratio_n = p_ref->freq_ratio_n;
ref->phase_comp = p_ref->phase_comp;
ref->sync_ctrl = p_ref->sync_ctrl;
ref->cfg = p_ref->cfg;
ref->inv = p_ref->inv;
return 0; /* Finish - no non-shared items for now */
}
/* Read reference status */
rc = zl3073x_ref_state_update(zldev, index);
if (rc)
return rc;
guard(mutex)(&zldev->multiop_lock);
/* Read reference configuration */
@ -154,12 +168,32 @@ zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
return &zldev->ref[index];
}
/**
* zl3073x_ref_state_set - commit input reference state changes to hardware
* @zldev: pointer to zl3073x_dev structure
* @index: input reference index to set state for
* @ref: desired reference state
*
* Validates that invariant fields have not been modified, skips the HW
* write if the mutable configuration is unchanged, and otherwise writes
* only the changed cfg fields to hardware via the mailbox interface.
*
* Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
*/
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_ref *ref)
{
struct zl3073x_ref *dref = &zldev->ref[index];
int rc;
/* Reject attempts to change invariant fields (set at init only) */
if (WARN_ON(memcmp(&dref->inv, &ref->inv, sizeof(ref->inv))))
return -EINVAL;
/* Skip HW write if configuration hasn't changed */
if (!memcmp(&dref->cfg, &ref->cfg, sizeof(ref->cfg)))
return 0;
guard(mutex)(&zldev->multiop_lock);
/* Read reference configuration into mailbox */
@ -207,13 +241,7 @@ int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
return rc;
/* After successful commit store new state */
dref->freq_base = ref->freq_base;
dref->freq_mult = ref->freq_mult;
dref->freq_ratio_m = ref->freq_ratio_m;
dref->freq_ratio_n = ref->freq_ratio_n;
dref->esync_n_div = ref->esync_n_div;
dref->sync_ctrl = ref->sync_ctrl;
dref->phase_comp = ref->phase_comp;
dref->cfg = ref->cfg;
return 0;
}

View File

@ -5,6 +5,7 @@
#include <linux/bitfield.h>
#include <linux/math64.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include "regs.h"
@ -13,28 +14,36 @@ struct zl3073x_dev;
/**
* struct zl3073x_ref - input reference state
* @ffo: current fractional frequency offset
* @phase_comp: phase compensation
* @esync_n_div: divisor for embedded sync or n-divided signal formats
* @freq_base: frequency base
* @freq_mult: frequnecy multiplier
* @freq_ratio_m: FEC mode multiplier
* @freq_ratio_n: FEC mode divisor
* @config: reference config
* @sync_ctrl: reference sync control
* @config: reference config
* @ffo: current fractional frequency offset
* @meas_freq: measured input frequency in Hz
* @mon_status: reference monitor status
*/
struct zl3073x_ref {
s64 ffo;
u64 phase_comp;
u32 esync_n_div;
u16 freq_base;
u16 freq_mult;
u16 freq_ratio_m;
u16 freq_ratio_n;
u8 config;
u8 sync_ctrl;
u8 mon_status;
struct_group(cfg, /* Configuration */
u64 phase_comp;
u32 esync_n_div;
u16 freq_base;
u16 freq_mult;
u16 freq_ratio_m;
u16 freq_ratio_n;
u8 sync_ctrl;
);
struct_group(inv, /* Invariants */
u8 config;
);
struct_group(stat, /* Status */
s64 ffo;
u32 meas_freq;
u8 mon_status;
);
};
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
@ -45,6 +54,8 @@ const struct zl3073x_ref *zl3073x_ref_state_get(struct zl3073x_dev *zldev,
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
const struct zl3073x_ref *ref);
int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index);
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
/**
@ -59,6 +70,18 @@ zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
return ref->ffo;
}
/**
* zl3073x_ref_meas_freq_get - get measured input frequency
* @ref: pointer to ref state
*
* Return: measured input frequency in Hz
*/
static inline u32
zl3073x_ref_meas_freq_get(const struct zl3073x_ref *ref)
{
return ref->meas_freq;
}
/**
* zl3073x_ref_freq_get - get given input reference frequency
* @ref: pointer to ref state
@ -97,6 +120,52 @@ zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
return 0;
}
/**
* zl3073x_ref_sync_mode_get - get sync control mode
* @ref: pointer to ref state
*
* Return: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
*/
static inline u8
zl3073x_ref_sync_mode_get(const struct zl3073x_ref *ref)
{
return FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl);
}
/**
* zl3073x_ref_sync_mode_set - set sync control mode
* @ref: pointer to ref state
* @mode: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
*/
static inline void
zl3073x_ref_sync_mode_set(struct zl3073x_ref *ref, u8 mode)
{
FIELD_MODIFY(ZL_REF_SYNC_CTRL_MODE, &ref->sync_ctrl, mode);
}
/**
* zl3073x_ref_sync_pair_get - get sync pair reference index
* @ref: pointer to ref state
*
* Return: paired reference index
*/
static inline u8
zl3073x_ref_sync_pair_get(const struct zl3073x_ref *ref)
{
return FIELD_GET(ZL_REF_SYNC_CTRL_PAIR, ref->sync_ctrl);
}
/**
* zl3073x_ref_sync_pair_set - set sync pair reference index
* @ref: pointer to ref state
* @pair: paired reference index
*/
static inline void
zl3073x_ref_sync_pair_set(struct zl3073x_ref *ref, u8 pair)
{
FIELD_MODIFY(ZL_REF_SYNC_CTRL_PAIR, &ref->sync_ctrl, pair);
}
/**
* zl3073x_ref_is_diff - check if the given input reference is differential
* @ref: pointer to ref state

View File

@ -6,6 +6,18 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
/*
* Hardware limits for ZL3073x chip family
*/
#define ZL3073X_MAX_CHANNELS 5
#define ZL3073X_NUM_REFS 10
#define ZL3073X_NUM_OUTS 10
#define ZL3073X_NUM_SYNTHS 5
#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
ZL3073X_NUM_OUTPUT_PINS)
/*
* Register address structure:
* ===========================
@ -78,6 +90,8 @@
#define ZL_REG_RESET_STATUS ZL_REG(0, 0x18, 1)
#define ZL_REG_RESET_STATUS_RESET BIT(0)
#define ZL_REG_DIE_TEMP_STATUS ZL_REG(0, 0x44, 2)
/*************************
* Register Page 2, Status
*************************/
@ -199,7 +213,9 @@
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR 1
#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
#define ZL_REF_SYNC_CTRL_PAIR GENMASK(7, 4)
#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
#define ZL_REF_ESYNC_DIV_1HZ 0

View File

@ -22,40 +22,25 @@ static int zl3073x_spi_probe(struct spi_device *spi)
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
"Failed to initialize regmap\n");
return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi));
return zl3073x_dev_probe(zldev);
}
static const struct spi_device_id zl3073x_spi_id[] = {
{
.name = "zl30731",
.driver_data = (kernel_ulong_t)&zl30731_chip_info
},
{
.name = "zl30732",
.driver_data = (kernel_ulong_t)&zl30732_chip_info,
},
{
.name = "zl30733",
.driver_data = (kernel_ulong_t)&zl30733_chip_info,
},
{
.name = "zl30734",
.driver_data = (kernel_ulong_t)&zl30734_chip_info,
},
{
.name = "zl30735",
.driver_data = (kernel_ulong_t)&zl30735_chip_info,
},
{ "zl30731" },
{ "zl30732" },
{ "zl30733" },
{ "zl30734" },
{ "zl30735" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(spi, zl3073x_spi_id);
static const struct of_device_id zl3073x_spi_of_match[] = {
{ .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
{ .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
{ .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
{ .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
{ .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
{ .compatible = "microchip,zl30731" },
{ .compatible = "microchip,zl30732" },
{ .compatible = "microchip,zl30733" },
{ .compatible = "microchip,zl30734" },
{ .compatible = "microchip,zl30735" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match);

View File

@ -5,6 +5,7 @@
#include <linux/bitfield.h>
#include <linux/math64.h>
#include <linux/stddef.h>
#include <linux/types.h>
#include "regs.h"
@ -20,11 +21,13 @@ struct zl3073x_dev;
* @ctrl: synth control
*/
struct zl3073x_synth {
u32 freq_mult;
u16 freq_base;
u16 freq_m;
u16 freq_n;
u8 ctrl;
struct_group(inv, /* Invariants */
u32 freq_mult;
u16 freq_base;
u16 freq_m;
u16 freq_n;
u8 ctrl;
);
};
int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
@ -32,9 +35,6 @@ int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
u8 synth_id);
int zl3073x_synth_state_set(struct zl3073x_dev *zldev, u8 synth_id,
const struct zl3073x_synth *synth);
/**
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
* @synth: pointer to synth state

View File

@ -77,9 +77,6 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
goto err_free;
}
srq->rq.queue = q;
init->attr.max_wr = srq->rq.max_wr;
if (uresp) {
if (copy_to_user(&uresp->srq_num, &srq->srq_num,
sizeof(uresp->srq_num))) {
@ -88,6 +85,9 @@ int rxe_srq_from_init(struct rxe_dev *rxe, struct rxe_srq *srq,
}
}
srq->rq.queue = q;
init->attr.max_wr = srq->rq.max_wr;
return 0;
err_free:

View File

@ -5398,7 +5398,7 @@ static netdev_tx_t bond_xmit_broadcast(struct sk_buff *skb,
if (!(bond_slave_is_up(slave) && slave->link == BOND_LINK_UP))
continue;
if (bond_is_last_slave(bond, slave)) {
if (i + 1 == slaves_count) {
skb2 = skb;
skb_used = true;
} else {

View File

@ -43,7 +43,8 @@ ice-y := ice_main.o \
ice_repr.o \
ice_tc_lib.o \
ice_debugfs.o \
ice_adapter.o
ice_adapter.o \
ice_rh.o
ice-$(CONFIG_PCI_IOV) += \
ice_sriov.o \
virt/allowlist.o \

View File

@ -837,6 +837,28 @@ static inline void ice_tx_xsk_pool(struct ice_vsi *vsi, u16 qid)
WRITE_ONCE(ring->xsk_pool, ice_get_xp_from_qid(vsi, qid));
}
/**
* ice_get_max_txq - return the maximum number of Tx queues for in a PF
* @pf: PF structure
*
* Return: maximum number of Tx queues
*/
static inline int ice_get_max_txq(struct ice_pf *pf)
{
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_txq);
}
/**
* ice_get_max_rxq - return the maximum number of Rx queues for in a PF
* @pf: PF structure
*
* Return: maximum number of Rx queues
*/
static inline int ice_get_max_rxq(struct ice_pf *pf)
{
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_rxq);
}
/**
* ice_get_main_vsi - Get the PF VSI
* @pf: PF instance

View File

@ -3744,24 +3744,6 @@ ice_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info)
return 0;
}
/**
* ice_get_max_txq - return the maximum number of Tx queues for in a PF
* @pf: PF structure
*/
static int ice_get_max_txq(struct ice_pf *pf)
{
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_txq);
}
/**
* ice_get_max_rxq - return the maximum number of Rx queues for in a PF
* @pf: PF structure
*/
static int ice_get_max_rxq(struct ice_pf *pf)
{
return min(num_online_cpus(), pf->hw.func_caps.common_cap.num_rxq);
}
/**
* ice_get_combined_cnt - return the current number of combined channels
* @vsi: PF VSI pointer

View File

@ -4,6 +4,7 @@
#include "ice.h"
#include "ice_lib.h"
#include "ice_irq.h"
#include "ice_rh.h"
/**
* ice_init_irq_tracker - initialize interrupt tracker
@ -106,9 +107,10 @@ static struct ice_irq_entry *ice_get_irq_res(struct ice_pf *pf,
#define ICE_RDMA_AEQ_MSIX 1
static int ice_get_default_msix_amount(struct ice_pf *pf)
{
return ICE_MIN_LAN_OICR_MSIX + num_online_cpus() +
return ICE_MIN_LAN_OICR_MSIX + ice_rh_get_num_default_rss_queues() +
(test_bit(ICE_FLAG_FD_ENA, pf->flags) ? ICE_FDIR_MSIX : 0) +
(ice_is_rdma_ena(pf) ? num_online_cpus() + ICE_RDMA_AEQ_MSIX : 0);
(ice_is_rdma_ena(pf) ? ice_rh_get_num_default_rss_queues() +
ICE_RDMA_AEQ_MSIX : 0);
}
/**

View File

@ -9,6 +9,7 @@
#include "ice_dcb_lib.h"
#include "ice_type.h"
#include "ice_vsi_vlan_ops.h"
#include "ice_rh.h"
/**
* ice_vsi_type_str - maps VSI type enum to string equivalents
@ -159,12 +160,14 @@ static void ice_vsi_set_num_desc(struct ice_vsi *vsi)
static u16 ice_get_rxq_count(struct ice_pf *pf)
{
return min(ice_get_avail_rxq_count(pf), num_online_cpus());
return min(ice_get_avail_rxq_count(pf),
ice_rh_get_num_default_rss_queues());
}
static u16 ice_get_txq_count(struct ice_pf *pf)
{
return min(ice_get_avail_txq_count(pf), num_online_cpus());
return min(ice_get_avail_txq_count(pf),
ice_rh_get_num_default_rss_queues());
}
/**
@ -911,13 +914,15 @@ static void ice_vsi_set_rss_params(struct ice_vsi *vsi)
if (vsi->type == ICE_VSI_CHNL)
vsi->rss_size = min_t(u16, vsi->num_rxq, max_rss_size);
else
vsi->rss_size = min_t(u16, num_online_cpus(),
vsi->rss_size = min_t(u16,
ice_rh_get_num_default_rss_queues(),
max_rss_size);
vsi->rss_lut_type = ICE_LUT_PF;
break;
case ICE_VSI_SF:
vsi->rss_table_size = ICE_LUT_VSI_SIZE;
vsi->rss_size = min_t(u16, num_online_cpus(), max_rss_size);
vsi->rss_size = min_t(u16, ice_rh_get_num_default_rss_queues(),
max_rss_size);
vsi->rss_lut_type = ICE_LUT_VSI;
break;
case ICE_VSI_VF:
@ -3007,7 +3012,7 @@ ice_vsi_rebuild_set_coalesce(struct ice_vsi *vsi,
* ice_vsi_realloc_stat_arrays - Frees unused stat structures or alloc new ones
* @vsi: VSI pointer
*/
static int
int
ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi)
{
u16 req_txq = vsi->req_txq ? vsi->req_txq : vsi->alloc_txq;

View File

@ -66,6 +66,7 @@ int ice_ena_vsi(struct ice_vsi *vsi, bool locked);
void ice_vsi_decfg(struct ice_vsi *vsi);
void ice_dis_vsi(struct ice_vsi *vsi, bool locked);
int ice_vsi_realloc_stat_arrays(struct ice_vsi *vsi);
int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags);
int ice_vsi_cfg(struct ice_vsi *vsi);
struct ice_vsi *ice_vsi_alloc(struct ice_pf *pf);

View File

@ -4730,8 +4730,8 @@ static int ice_cfg_netdev(struct ice_vsi *vsi)
struct net_device *netdev;
u8 mac_addr[ETH_ALEN];
netdev = alloc_etherdev_mqs(sizeof(*np), vsi->alloc_txq,
vsi->alloc_rxq);
netdev = alloc_etherdev_mqs(sizeof(*np), ice_get_max_txq(vsi->back),
ice_get_max_rxq(vsi->back));
if (!netdev)
return -ENOMEM;

View File

@ -0,0 +1,33 @@
#include <linux/cpumask.h>
#include <linux/crash_dump.h>
#include <linux/gfp_types.h>
#include <linux/math.h>
#include <linux/topology.h>
#include "ice_rh.h"
/**
* ice_rh_get_num_default_rss_queues - default number of RSS queues
*
* Default value is the number of physical cores if there are only 1 or 2, or
* divided by 2 if there are more.
*
* This is a renamed copy of netif_get_num_default_rss_queues() from upstream.
*/
int ice_rh_get_num_default_rss_queues(void)
{
cpumask_var_t cpus;
int cpu, count = 0;
if (unlikely(is_kdump_kernel() || !zalloc_cpumask_var(&cpus, GFP_KERNEL)))
return 1;
cpumask_copy(cpus, cpu_online_mask);
for_each_cpu(cpu, cpus) {
++count;
cpumask_andnot(cpus, cpus, topology_sibling_cpumask(cpu));
}
free_cpumask_var(cpus);
return count > 2 ? DIV_ROUND_UP(count, 2) : count;
}

View File

@ -0,0 +1,6 @@
#ifndef __ICE_RH_H
#define __ICE_RH_H
int ice_rh_get_num_default_rss_queues(void);
#endif

View File

@ -268,6 +268,13 @@ static int ice_vf_reconfig_vsi(struct ice_vf *vf)
vsi->flags = ICE_VSI_FLAG_NO_INIT;
vsi->req_txq = vf->num_req_qs;
vsi->req_rxq = vf->num_req_qs;
err = ice_vsi_realloc_stat_arrays(vsi);
if (err)
return err;
ice_vsi_decfg(vsi);
ice_fltr_remove_all(vsi);

View File

@ -44,13 +44,14 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type)
{
struct mlx4_srq_table *srq_table = &mlx4_priv(dev)->srq_table;
struct mlx4_srq *srq;
unsigned long flags;
rcu_read_lock();
spin_lock_irqsave(&srq_table->lock, flags);
srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1));
rcu_read_unlock();
if (srq)
refcount_inc(&srq->refcount);
else {
if (!srq || !refcount_inc_not_zero(&srq->refcount))
srq = NULL;
spin_unlock_irqrestore(&srq_table->lock, flags);
if (!srq) {
mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn);
return;
}
@ -203,8 +204,8 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd,
if (err)
goto err_radix;
refcount_set(&srq->refcount, 1);
init_completion(&srq->free);
atomic_set_release(&srq->refcount.refs, 1);
return 0;

View File

@ -3196,6 +3196,7 @@ static int add_adev(struct gdma_dev *gd, const char *name)
struct auxiliary_device *adev;
struct mana_adev *madev;
int ret;
int id;
madev = kzalloc(sizeof(*madev), GFP_KERNEL);
if (!madev)
@ -3205,7 +3206,8 @@ static int add_adev(struct gdma_dev *gd, const char *name)
ret = mana_adev_idx_alloc();
if (ret < 0)
goto idx_fail;
adev->id = ret;
id = ret;
adev->id = id;
adev->name = name;
adev->dev.parent = gd->gdma_context->dev;
@ -3231,7 +3233,7 @@ add_fail:
auxiliary_device_uninit(adev);
init_fail:
mana_adev_idx_free(adev->id);
mana_adev_idx_free(id);
idx_fail:
kfree(madev);

View File

@ -2354,17 +2354,11 @@ nvme_fc_ctrl_free(struct kref *ref)
container_of(ref, struct nvme_fc_ctrl, ref);
unsigned long flags;
if (ctrl->ctrl.tagset)
nvme_remove_io_tag_set(&ctrl->ctrl);
/* remove from rport list */
spin_lock_irqsave(&ctrl->rport->lock, flags);
list_del(&ctrl->ctrl_list);
spin_unlock_irqrestore(&ctrl->rport->lock, flags);
nvme_unquiesce_admin_queue(&ctrl->ctrl);
nvme_remove_admin_tag_set(&ctrl->ctrl);
kfree(ctrl->queues);
put_device(ctrl->dev);
@ -3250,13 +3244,20 @@ nvme_fc_delete_ctrl(struct nvme_ctrl *nctrl)
{
struct nvme_fc_ctrl *ctrl = to_fc_ctrl(nctrl);
cancel_work_sync(&ctrl->ioerr_work);
cancel_delayed_work_sync(&ctrl->connect_work);
/*
* kill the association on the link side. this will block
* waiting for io to terminate
*/
nvme_fc_delete_association(ctrl);
cancel_work_sync(&ctrl->ioerr_work);
if (ctrl->ctrl.tagset)
nvme_remove_io_tag_set(&ctrl->ctrl);
nvme_unquiesce_admin_queue(&ctrl->ctrl);
nvme_remove_admin_tag_set(&ctrl->ctrl);
}
static void

View File

@ -6144,10 +6144,12 @@ static void copy_pair_set_active(struct dasd_copy_relation *copy, char *new_busi
static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid,
char *sec_busid)
{
struct dasd_eckd_private *prim_priv, *sec_priv;
struct dasd_device *primary, *secondary;
struct dasd_copy_relation *copy;
struct dasd_block *block;
struct gendisk *gdp;
int rc;
copy = device->copy;
if (!copy)
@ -6163,6 +6165,9 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid
if (!secondary)
return DASD_COPYPAIRSWAP_SECONDARY;
prim_priv = primary->private;
sec_priv = secondary->private;
/*
* usually the device should be quiesced for swap
* for paranoia stop device and requeue requests again
@ -6182,6 +6187,25 @@ static int dasd_eckd_copy_pair_swap(struct dasd_device *device, char *prim_busid
/* swap blocklayer device link */
gdp = block->gdp;
dasd_add_link_to_gendisk(gdp, secondary);
rc = device_move(disk_to_dev(gdp), &secondary->cdev->dev, DPM_ORDER_NONE);
if (rc) {
dev_err(&primary->cdev->dev,
"copy_pair_swap: moving blockdevice parent %s->%s failed (%d)\n",
dev_name(&primary->cdev->dev),
dev_name(&secondary->cdev->dev), rc);
}
if (primary->stopped & DASD_STOPPED_QUIESCE) {
dasd_device_set_stop_bits(secondary, DASD_STOPPED_QUIESCE);
dasd_device_remove_stop_bits(primary, DASD_STOPPED_QUIESCE);
}
/*
* The secondary device never got through format detection, but since it
* is a copy of the primary device, the format is exactly the same;
* therefore, the detected layout can simply be copied.
*/
sec_priv->uses_cdl = prim_priv->uses_cdl;
/* re-enable device */
dasd_device_remove_stop_bits(primary, DASD_STOPPED_PPRC);

View File

@ -857,8 +857,11 @@ void iscsit_dec_conn_usage_count(struct iscsit_conn *conn)
spin_lock_bh(&conn->conn_usage_lock);
conn->conn_usage_count--;
if (!conn->conn_usage_count && conn->conn_waiting_on_uc)
if (!conn->conn_usage_count && conn->conn_waiting_on_uc) {
spin_unlock_bh(&conn->conn_usage_lock);
complete(&conn->conn_waiting_on_uc_comp);
return;
}
spin_unlock_bh(&conn->conn_usage_lock);
}

View File

@ -364,6 +364,8 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
ret = sprintf(buffer, "<denied>");
return ret;
}
if (ret > PAGE_SIZE)
return -ENOSPC;
buildid = kmalloc(sizeof(*buildid) + ret, GFP_KERNEL);
if (!buildid)
@ -371,8 +373,10 @@ static ssize_t buildid_show(struct hyp_sysfs_attr *attr, char *buffer)
buildid->len = ret;
ret = HYPERVISOR_xen_version(XENVER_build_id, buildid);
if (ret > 0)
ret = sprintf(buffer, "%s", buildid->buf);
if (ret > 0) {
/* Build id is binary, not a string. */
memcpy(buffer, buildid->buf, ret);
}
kfree(buildid);
return ret;

View File

@ -442,13 +442,16 @@ static int rsb_cmp(struct dlm_rsb *r, const char *name, int nlen)
return memcmp(r->res_name, maxname, DLM_RESNAME_MAXLEN);
}
int dlm_search_rsb_tree(struct rb_root *tree, char *name, int len,
struct dlm_rsb **r_ret)
int dlm_search_rsb_tree(struct rb_root *tree, char *name,
unsigned int len, struct dlm_rsb **r_ret)
{
struct rb_node *node = tree->rb_node;
struct dlm_rsb *r;
int rc;
if (len > DLM_RESNAME_MAXLEN)
return -EINVAL;
while (node) {
r = rb_entry(node, struct dlm_rsb, res_hashnode);
rc = rsb_cmp(r, name, len);

View File

@ -30,8 +30,8 @@ void dlm_adjust_timeouts(struct dlm_ls *ls);
int dlm_master_lookup(struct dlm_ls *ls, int nodeid, char *name, int len,
unsigned int flags, int *r_nodeid, int *result);
int dlm_search_rsb_tree(struct rb_root *tree, char *name, int len,
struct dlm_rsb **r_ret);
int dlm_search_rsb_tree(struct rb_root *tree, char *name,
unsigned int len, struct dlm_rsb **r_ret);
void dlm_recover_purge(struct dlm_ls *ls);
void dlm_purge_mstcpy_locks(struct dlm_rsb *r);

View File

@ -27,10 +27,11 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
{
struct smb2_err_rsp *err = iov->iov_base;
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
u8 *end = (u8 *)err + iov->iov_len;
u32 len;
if (err->ErrorContextCount) {
struct smb2_error_context_rsp *p, *end;
struct smb2_error_context_rsp *p;
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
ErrorContextData) +
@ -39,8 +40,7 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
return ERR_PTR(-EINVAL);
p = (struct smb2_error_context_rsp *)err->ErrorData;
end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
do {
while ((u8 *)p + sizeof(*p) <= end) {
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
break;
@ -50,14 +50,16 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
} while (p < end);
}
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
}
if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
if (!IS_ERR(sym) &&
((u8 *)sym + sizeof(*sym) > end ||
le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
sym = ERR_PTR(-EINVAL);
return sym;
@ -128,8 +130,10 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
print_len = le16_to_cpu(sym->PrintNameLength);
print_offs = le16_to_cpu(sym->PrintNameOffset);
if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
if ((char *)sym->PathBuffer + sub_offs + sub_len >
(char *)iov->iov_base + iov->iov_len ||
(char *)sym->PathBuffer + print_offs + print_len >
(char *)iov->iov_base + iov->iov_len)
return -EINVAL;
return smb2_parse_native_symlink(path,

View File

@ -55,8 +55,12 @@ struct dpll_device_ops {
void *dpll_priv, u32 *factor,
struct netlink_ext_ack *extack);
RH_KABI_RESERVE(1)
RH_KABI_RESERVE(2)
RH_KABI_USE(1, int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
enum dpll_feature_state state,
struct netlink_ext_ack *extack))
RH_KABI_USE(2, int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
enum dpll_feature_state *state,
struct netlink_ext_ack *extack))
RH_KABI_RESERVE(3)
RH_KABI_RESERVE(4)
RH_KABI_RESERVE(5)
@ -141,7 +145,10 @@ struct dpll_pin_ops {
enum dpll_pin_state *state,
struct netlink_ext_ack *extack);
RH_KABI_RESERVE(1)
RH_KABI_USE(1, int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
const struct dpll_device *dpll,
void *dpll_priv, u64 *measured_freq,
struct netlink_ext_ack *extack))
RH_KABI_RESERVE(2)
RH_KABI_RESERVE(3)
RH_KABI_RESERVE(4)

View File

@ -348,9 +348,15 @@ extern gfp_t gfp_allowed_mask;
/* Returns true if the gfp_mask allows use of ALLOC_NO_WATERMARK */
bool gfp_pfmemalloc_allowed(gfp_t gfp_mask);
/* A helper for checking if gfp includes all the specified flags */
static inline bool gfp_has_flags(gfp_t gfp, gfp_t flags)
{
return (gfp & flags) == flags;
}
static inline bool gfp_has_io_fs(gfp_t gfp)
{
return (gfp & (__GFP_IO | __GFP_FS)) == (__GFP_IO | __GFP_FS);
return gfp_has_flags(gfp, __GFP_IO | __GFP_FS);
}
/*

View File

@ -168,7 +168,8 @@ typedef unsigned int __bitwise gfp_t;
* the caller still has to check for failures) while costly requests try to be
* not disruptive and back off even without invoking the OOM killer.
* The following three modifiers might be used to override some of these
* implicit rules
* implicit rules. Please note that all of them must be used along with
* %__GFP_DIRECT_RECLAIM flag.
*
* %__GFP_NORETRY: The VM implementation will try only very lightweight
* memory direct reclaim to get some memory under memory pressure (thus
@ -199,11 +200,14 @@ typedef unsigned int __bitwise gfp_t;
* cannot handle allocation failures. The allocation could block
* indefinitely but will never return with failure. Testing for
* failure is pointless.
* It _must_ be blockable and used together with __GFP_DIRECT_RECLAIM.
* It should _never_ be used in non-sleepable contexts.
* New users should be evaluated carefully (and the flag should be
* used only when there is no reasonable failure policy) but it is
* definitely preferable to use the flag rather than opencode endless
* loop around allocator.
* Using this flag for costly allocations is _highly_ discouraged.
* Allocating pages from the buddy with __GFP_NOFAIL and order > 1 is
* not supported. Please consider using kvmalloc() instead.
*/
#define __GFP_IO ((__force gfp_t)___GFP_IO)
#define __GFP_FS ((__force gfp_t)___GFP_FS)

View File

@ -84,6 +84,11 @@ void nf_conntrack_lock(spinlock_t *lock);
extern spinlock_t nf_conntrack_expect_lock;
static inline void lockdep_nfct_expect_lock_held(void)
{
lockdep_assert_held(&nf_conntrack_expect_lock);
}
/* ctnetlink code shared by both ctnetlink and nf_conntrack_bpf */
#if (IS_BUILTIN(CONFIG_NF_CONNTRACK) && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) || \

View File

@ -190,7 +190,8 @@ enum dpll_pin_capabilities {
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4,
};
#define DPLL_PHASE_OFFSET_DIVIDER 1000
#define DPLL_PHASE_OFFSET_DIVIDER 1000
#define DPLL_PIN_MEASURED_FREQUENCY_DIVIDER 1000
/**
* enum dpll_feature_state - Allow control (enable/disable) and status checking
@ -217,6 +218,7 @@ enum dpll_a {
DPLL_A_CLOCK_QUALITY_LEVEL,
DPLL_A_PHASE_OFFSET_MONITOR,
DPLL_A_PHASE_OFFSET_AVG_FACTOR,
DPLL_A_FREQUENCY_MONITOR,
__DPLL_A_MAX,
DPLL_A_MAX = (__DPLL_A_MAX - 1)
@ -253,6 +255,7 @@ enum dpll_a_pin {
DPLL_A_PIN_REFERENCE_SYNC,
DPLL_A_PIN_PHASE_ADJUST_GRAN,
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
DPLL_A_PIN_MEASURED_FREQUENCY,
__DPLL_A_PIN_MAX,
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)

View File

@ -1,3 +1,3 @@
sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md
kernel.rhel,1,Red Hat,kernel-core,5.14.0-687.12.1.el9.x86_64,mailto:secalert@redhat.com
kernel.almalinux,1,AlmaLinux,kernel-core,5.14.0-687.12.1.el9.x86_64,mailto:security@almalinux.org
kernel.rhel,1,Red Hat,kernel-core,5.14.0-687.15.1.el9.x86_64,mailto:secalert@redhat.com
kernel.almalinux,1,AlmaLinux,kernel-core,5.14.0-687.15.1.el9.x86_64,mailto:security@almalinux.org

View File

@ -290,6 +290,14 @@ int user_min_free_kbytes = -1;
static int watermark_boost_factor __read_mostly = 15000;
static int watermark_scale_factor = 10;
/*
* RHEL-ONLY: When set to 1, allows reclaim for __GFP_THISNODE THP allocations,
* restoring the behavior prior to the fix that prevents zone_reclaim_mode-like
* excessive reclaim on a single NUMA node when other nodes have plenty of free
* memory.
*/
static int thp_thisnode_reclaim __read_mostly = 1;
/* movable_zone is the "real" zone pages in ZONE_MOVABLE are taken from */
int movable_zone;
EXPORT_SYMBOL(movable_zone);
@ -2915,12 +2923,6 @@ struct page *rmqueue(struct zone *preferred_zone,
{
struct page *page;
/*
* We most definitely don't want callers attempting to
* allocate greater than order-1 page units with __GFP_NOFAIL.
*/
WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
if (likely(pcp_allowed_order(order))) {
page = rmqueue_pcplist(preferred_zone, zone, order,
migratetype, alloc_flags);
@ -4062,7 +4064,8 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
struct alloc_context *ac)
{
bool can_direct_reclaim = gfp_mask & __GFP_DIRECT_RECLAIM;
bool can_compact = gfp_compaction_allowed(gfp_mask);
bool can_compact = can_direct_reclaim && gfp_compaction_allowed(gfp_mask);
bool nofail = gfp_mask & __GFP_NOFAIL;
const bool costly_order = order > PAGE_ALLOC_COSTLY_ORDER;
struct page *page = NULL;
unsigned int alloc_flags;
@ -4074,6 +4077,27 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order,
unsigned int cpuset_mems_cookie;
unsigned int zonelist_iter_cookie;
int reserve_flags;
bool compact_first = false;
bool can_retry_reserves = true;
if (unlikely(nofail)) {
/*
* We most definitely don't want callers attempting to
* allocate greater than order-1 page units with __GFP_NOFAIL.
*/
WARN_ON_ONCE(order > 1);
/*
* Also we don't support __GFP_NOFAIL without __GFP_DIRECT_RECLAIM,
* otherwise, we may result in lockup.
*/
WARN_ON_ONCE(!can_direct_reclaim);
/*
* PF_MEMALLOC request from this context is rather bizarre
* because we cannot reclaim anything and only can loop waiting
* for somebody to do a work for us.
*/
WARN_ON_ONCE(current->flags & PF_MEMALLOC);
}
restart:
compaction_retries = 0;
@ -4082,6 +4106,19 @@ restart:
cpuset_mems_cookie = read_mems_allowed_begin();
zonelist_iter_cookie = zonelist_iter_begin();
/*
* For costly allocations, try direct compaction first, as it's likely
* that we have enough base pages and don't need to reclaim. For non-
* movable high-order allocations, do that as well, as compaction will
* try prevent permanent fragmentation by migrating from blocks of the
* same migratetype.
*/
if (can_compact && (costly_order || (order > 0 &&
ac->migratetype != MIGRATE_MOVABLE))) {
compact_first = true;
compact_priority = INIT_COMPACT_PRIORITY;
}
/*
* The fast path uses conservative alloc_flags to succeed only until
* kswapd needs to be woken up, and to avoid the cost of setting up
@ -4113,6 +4150,8 @@ restart:
goto nopage;
}
retry:
/* Ensure kswapd doesn't accidentally go to sleep as long as we loop */
if (alloc_flags & ALLOC_KSWAPD)
wake_all_kswapds(order, gfp_mask, ac);
@ -4124,66 +4163,6 @@ restart:
if (page)
goto got_pg;
/*
* For costly allocations, try direct compaction first, as it's likely
* that we have enough base pages and don't need to reclaim. For non-
* movable high-order allocations, do that as well, as compaction will
* try prevent permanent fragmentation by migrating from blocks of the
* same migratetype.
* Don't try this for allocations that are allowed to ignore
* watermarks, as the ALLOC_NO_WATERMARKS attempt didn't yet happen.
*/
if (can_direct_reclaim && can_compact &&
(costly_order ||
(order > 0 && ac->migratetype != MIGRATE_MOVABLE))
&& !gfp_pfmemalloc_allowed(gfp_mask)) {
page = __alloc_pages_direct_compact(gfp_mask, order,
alloc_flags, ac,
INIT_COMPACT_PRIORITY,
&compact_result);
if (page)
goto got_pg;
/*
* Checks for costly allocations with __GFP_NORETRY, which
* includes some THP page fault allocations
*/
if (costly_order && (gfp_mask & __GFP_NORETRY)) {
/*
* If allocating entire pageblock(s) and compaction
* failed because all zones are below low watermarks
* or is prohibited because it recently failed at this
* order, fail immediately unless the allocator has
* requested compaction and reclaim retry.
*
* Reclaim is
* - potentially very expensive because zones are far
* below their low watermarks or this is part of very
* bursty high order allocations,
* - not guaranteed to help because isolate_freepages()
* may not iterate over freed pages as part of its
* linear scan, and
* - unlikely to make entire pageblocks free on its
* own.
*/
if (compact_result == COMPACT_SKIPPED ||
compact_result == COMPACT_DEFERRED)
goto nopage;
/*
* Looks like reclaim/compaction is worth trying, but
* sync compaction could be very expensive, so keep
* using async compaction.
*/
compact_priority = INIT_COMPACT_PRIORITY;
}
}
retry:
/* Ensure kswapd doesn't accidentally go to sleep as long as we loop */
if (alloc_flags & ALLOC_KSWAPD)
wake_all_kswapds(order, gfp_mask, ac);
reserve_flags = __gfp_pfmemalloc_flags(gfp_mask);
if (reserve_flags)
alloc_flags = gfp_to_alloc_flags_cma(gfp_mask, reserve_flags) |
@ -4198,12 +4177,18 @@ retry:
ac->nodemask = NULL;
ac->preferred_zoneref = first_zones_zonelist(ac->zonelist,
ac->highest_zoneidx, ac->nodemask);
}
/* Attempt with potentially adjusted zonelist and alloc_flags */
page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);
if (page)
goto got_pg;
/*
* The first time we adjust anything due to being allowed to
* ignore memory policies or watermarks, retry immediately. This
* allows us to keep the first allocation attempt optimistic so
* it can succeed in a zone that is still above watermarks.
*/
if (can_retry_reserves) {
can_retry_reserves = false;
goto retry;
}
}
/* Caller is not willing to reclaim, we can't balance anything */
if (!can_direct_reclaim)
@ -4214,10 +4199,12 @@ retry:
goto nopage;
/* Try direct reclaim and then allocating */
page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac,
&did_some_progress);
if (page)
goto got_pg;
if (!compact_first) {
page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags,
ac, &did_some_progress);
if (page)
goto got_pg;
}
/* Try direct compaction and then allocating */
page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
@ -4225,6 +4212,44 @@ retry:
if (page)
goto got_pg;
if (compact_first) {
/*
* THP page faults may attempt local node only first, but are
* then allowed to only compact, not reclaim, see
* alloc_pages_mpol().
*
* Compaction has failed above and we don't want such THP
* allocations to put reclaim pressure on a single node in a
* situation where other nodes might have plenty of available
* memory.
*
* RHEL-ONLY: vm.thp_thisnode_reclaim can override this to
* restore the pre-fix behavior: allow reclaim for THISNODE THP
* allocations, but still fail immediately when compaction was
* skipped (insufficient free base pages) or deferred (recent
* compaction failures at this order).
*/
if (gfp_has_flags(gfp_mask, __GFP_NORETRY | __GFP_THISNODE)) {
if (!thp_thisnode_reclaim)
goto nopage;
if (compact_result == COMPACT_SKIPPED ||
compact_result == COMPACT_DEFERRED)
goto nopage;
}
/*
* For the initial compaction attempt we have lowered its
* priority. Restore it for further retries, if those are
* allowed. With __GFP_NORETRY there will be a single round of
* reclaim and compaction with the lowered priority.
*/
if (!(gfp_mask & __GFP_NORETRY))
compact_priority = DEF_COMPACT_PRIORITY;
compact_first = false;
goto retry;
}
/* Do not loop if specifically requested */
if (gfp_mask & __GFP_NORETRY)
goto nopage;
@ -4237,6 +4262,15 @@ retry:
!(gfp_mask & __GFP_RETRY_MAYFAIL)))
goto nopage;
/*
* Deal with possible cpuset update races or zonelist updates to avoid
* infinite retries. No "goto retry;" can be placed above this check
* unless it can execute just once.
*/
if (check_retry_cpuset(cpuset_mems_cookie, ac) ||
check_retry_zonelist(zonelist_iter_cookie))
goto restart;
if (should_reclaim_retry(gfp_mask, order, ac, alloc_flags,
did_some_progress > 0, &no_progress_loops))
goto retry;
@ -4292,29 +4326,15 @@ nopage:
* Make sure that __GFP_NOFAIL request doesn't leak out and make sure
* we always retry
*/
if (gfp_mask & __GFP_NOFAIL) {
if (unlikely(nofail)) {
/*
* All existing users of the __GFP_NOFAIL are blockable, so warn
* of any new users that actually require GFP_NOWAIT
* Lacking direct_reclaim we can't do anything to reclaim memory,
* we disregard these unreasonable nofail requests and still
* return NULL
*/
if (WARN_ON_ONCE_GFP(!can_direct_reclaim, gfp_mask))
if (!can_direct_reclaim)
goto fail;
/*
* PF_MEMALLOC request from this context is rather bizarre
* because we cannot reclaim anything and only can loop waiting
* for somebody to do a work for us
*/
WARN_ON_ONCE_GFP(current->flags & PF_MEMALLOC, gfp_mask);
/*
* non failing costly orders are a hard requirement which we
* are not prepared for much so let's warn about these users
* so that we can identify them and convert them to something
* else.
*/
WARN_ON_ONCE_GFP(costly_order, gfp_mask);
/*
* Help non-failing allocations by giving some access to memory
* reserves normally used for high priority non-blocking
@ -6213,6 +6233,15 @@ static struct ctl_table page_alloc_sysctl_table[] = {
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE_HUNDRED,
},
{
.procname = "thp_thisnode_reclaim", //RHEL-ONLY
.data = &thp_thisnode_reclaim,
.maxlen = sizeof(thp_thisnode_reclaim),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
#endif
{}
};

View File

@ -479,6 +479,7 @@ static int ipv6_rpl_srh_rcv(struct sk_buff *skb)
struct net *net = dev_net(skb->dev);
struct inet6_dev *idev;
struct ipv6hdr *oldhdr;
unsigned int chdr_len;
struct in6_addr addr;
unsigned char *buf;
int accept_rpl_seg;
@ -601,8 +602,10 @@ looped_back:
skb_pull(skb, ((hdr->hdrlen + 1) << 3));
skb_postpull_rcsum(skb, oldhdr,
sizeof(struct ipv6hdr) + ((hdr->hdrlen + 1) << 3));
if (unlikely(!hdr->segments_left)) {
if (pskb_expand_head(skb, sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3), 0,
chdr_len = sizeof(struct ipv6hdr) + ((chdr->hdrlen + 1) << 3);
if (unlikely(!hdr->segments_left ||
skb_headroom(skb) < chdr_len + skb->mac_len)) {
if (pskb_expand_head(skb, chdr_len + skb->mac_len, 0,
GFP_ATOMIC)) {
__IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_OUTDISCARDS);
kfree_skb(skb);
@ -612,7 +615,7 @@ looped_back:
oldhdr = ipv6_hdr(skb);
}
skb_push(skb, ((chdr->hdrlen + 1) << 3) + sizeof(struct ipv6hdr));
skb_push(skb, chdr_len);
skb_reset_network_header(skb);
skb_mac_header_rebuild(skb);
skb_set_transport_header(skb, sizeof(struct ipv6hdr));

View File

@ -603,11 +603,16 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (!skb2)
return 0;
/* Remove debris left by IPv6 stack. */
memset(IPCB(skb2), 0, sizeof(*IPCB(skb2)));
skb_dst_drop(skb2);
skb_pull(skb2, offset);
skb_reset_network_header(skb2);
eiph = ip_hdr(skb2);
if (eiph->version != 4 || eiph->ihl < 5)
goto out;
/* Try to guess incoming interface */
rt = ip_route_output_ports(dev_net(skb->dev), &fl4, NULL, eiph->saddr,

View File

@ -237,6 +237,8 @@ void nf_ct_expect_event_report(enum ip_conntrack_expect_events event,
struct nf_ct_event_notifier *notify;
struct nf_conntrack_ecache *e;
lockdep_nfct_expect_lock_held();
rcu_read_lock();
notify = rcu_dereference(net->ct.nf_conntrack_event_cb);
if (!notify)

View File

@ -51,6 +51,7 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp,
struct net *net = nf_ct_exp_net(exp);
struct nf_conntrack_net *cnet;
lockdep_nfct_expect_lock_held();
WARN_ON(!master_help);
WARN_ON(timer_pending(&exp->timeout));
@ -118,6 +119,8 @@ nf_ct_exp_equal(const struct nf_conntrack_tuple *tuple,
bool nf_ct_remove_expect(struct nf_conntrack_expect *exp)
{
lockdep_nfct_expect_lock_held();
if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect(exp);
nf_ct_expect_put(exp);
@ -177,6 +180,8 @@ nf_ct_find_expectation(struct net *net,
struct nf_conntrack_expect *i, *exp = NULL;
unsigned int h;
lockdep_nfct_expect_lock_held();
if (!cnet->expect_count)
return NULL;
@ -442,6 +447,8 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect,
unsigned int h;
int ret = 0;
lockdep_nfct_expect_lock_held();
if (!master_help) {
ret = -ESHUTDOWN;
goto out;
@ -498,8 +505,9 @@ int nf_ct_expect_related_report(struct nf_conntrack_expect *expect,
nf_ct_expect_insert(expect);
spin_unlock_bh(&nf_conntrack_expect_lock);
nf_ct_expect_event_report(IPEXP_NEW, expect, portid, report);
spin_unlock_bh(&nf_conntrack_expect_lock);
return 0;
out:
spin_unlock_bh(&nf_conntrack_expect_lock);

View File

@ -3329,31 +3329,37 @@ static int ctnetlink_get_expect(struct sk_buff *skb,
if (err < 0)
return err;
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb2)
return -ENOMEM;
spin_lock_bh(&nf_conntrack_expect_lock);
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
if (!exp)
if (!exp) {
spin_unlock_bh(&nf_conntrack_expect_lock);
kfree_skb(skb2);
return -ENOENT;
}
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
spin_unlock_bh(&nf_conntrack_expect_lock);
kfree_skb(skb2);
return -ENOENT;
}
}
skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb2) {
nf_ct_expect_put(exp);
return -ENOMEM;
}
rcu_read_lock();
err = ctnetlink_exp_fill_info(skb2, NETLINK_CB(skb).portid,
info->nlh->nlmsg_seq, IPCTNL_MSG_EXP_NEW,
exp);
rcu_read_unlock();
nf_ct_expect_put(exp);
spin_unlock_bh(&nf_conntrack_expect_lock);
if (err <= 0) {
kfree_skb(skb2);
return -ENOMEM;
@ -3403,22 +3409,26 @@ static int ctnetlink_del_expect(struct sk_buff *skb,
if (err < 0)
return err;
spin_lock_bh(&nf_conntrack_expect_lock);
/* bump usage count to 2 */
exp = nf_ct_expect_find_get(info->net, &zone, &tuple);
if (!exp)
if (!exp) {
spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
}
if (cda[CTA_EXPECT_ID]) {
__be32 id = nla_get_be32(cda[CTA_EXPECT_ID]);
if (id != nf_expect_get_id(exp)) {
nf_ct_expect_put(exp);
spin_unlock_bh(&nf_conntrack_expect_lock);
return -ENOENT;
}
}
/* after list removal, usage count == 1 */
spin_lock_bh(&nf_conntrack_expect_lock);
if (del_timer(&exp->timeout)) {
nf_ct_unlink_expect_report(exp, NETLINK_CB(skb).portid,
nlmsg_report(info->nlh));

View File

@ -144,11 +144,15 @@ static void vport_netdev_free(struct rcu_head *rcu)
void ovs_netdev_detach_dev(struct vport *vport)
{
ASSERT_RTNL();
vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
netdev_rx_handler_unregister(vport->dev);
netdev_upper_dev_unlink(vport->dev,
netdev_master_upper_dev_get(vport->dev));
dev_set_promiscuity(vport->dev, -1);
/* paired with smp_mb() in netdev_destroy() */
smp_wmb();
vport->dev->priv_flags &= ~IFF_OVS_DATAPATH;
}
static void netdev_destroy(struct vport *vport)
@ -167,6 +171,9 @@ static void netdev_destroy(struct vport *vport)
rtnl_unlock();
}
/* paired with smp_wmb() in ovs_netdev_detach_dev() */
smp_mb();
call_rcu(&vport->rcu, vport_netdev_free);
}

View File

@ -53,11 +53,6 @@ static void usb6fire_chip_abort(struct sfire_chip *chip)
usb6fire_comm_abort(chip);
if (chip->control)
usb6fire_control_abort(chip);
if (chip->card) {
snd_card_disconnect(chip->card);
snd_card_free_when_closed(chip->card);
chip->card = NULL;
}
}
}
@ -168,6 +163,7 @@ destroy_chip:
static void usb6fire_chip_disconnect(struct usb_interface *intf)
{
struct sfire_chip *chip;
struct snd_card *card;
chip = usb_get_intfdata(intf);
if (chip) { /* if !chip, fw upload has been performed */
@ -178,8 +174,19 @@ static void usb6fire_chip_disconnect(struct usb_interface *intf)
chips[chip->regidx] = NULL;
}
/*
* Save card pointer before teardown.
* snd_card_free_when_closed() may free card (and
* the embedded chip) immediately, so it must be
* called last and chip must not be accessed after.
*/
card = chip->card;
chip->shutdown = true;
if (card)
snd_card_disconnect(card);
usb6fire_chip_abort(chip);
if (card)
snd_card_free_when_closed(card);
}
}
}