From a306a0e4a5326269b6c78d136407f08433ab5ece Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Fri, 27 Jul 2018 14:44:12 +0530 Subject: soundwire: Initialize completion for defer messages Deferred messages are async messages used to synchronize transitions mostly while doing a bank switch on multi links. On successful transitions these messages are marked complete and thereby confirming that all the buses performed bank switch successfully. So, initialize the completion structure for the same. Signed-off-by: Sanyog Kale Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index dcc0ff9f0c22..dbabd5e69343 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -175,6 +175,7 @@ static inline int do_transfer_defer(struct sdw_bus *bus, defer->msg = msg; defer->length = msg->len; + init_completion(&defer->complete); for (i = 0; i <= retry; i++) { resp = bus->ops->xfer_msg_defer(bus, msg, defer); -- cgit v1.2.3-58-ga151 From 0c4a1049cf298721eaec4553d3d5039798086e12 Mon Sep 17 00:00:00 2001 From: Sanyog Kale Date: Fri, 27 Jul 2018 14:44:13 +0530 Subject: soundwire: Add support to lock across bus instances Currently, the stream concept is limited to single Master and one or more Codecs. This patch extends the concept to support multiple Master(s) sharing the same reference clock and synchronized in the hardware. Modify sdw_stream_runtime to support a list of sdw_master_runtime for the same. The existing reference to a single m_rt is removed in the next patch. Typically to lock, one would acquire a global lock and then lock bus instances. In this case, the caller framework(ASoC DPCM) guarantees that stream operations on a card are always serialized. So, there is no race condition and hence no need for global lock. Bus lock(s) are acquired to reconfigure the bus while the stream is set-up. So, we add sdw_acquire_bus_lock()/sdw_release_bus_lock() APIs which are used only to reconfigure the bus. Signed-off-by: Sanyog Kale Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/bus.h | 2 ++ drivers/soundwire/stream.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/soundwire/sdw.h | 4 ++++ 3 files changed, 49 insertions(+) (limited to 'drivers') diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index 3b15c4e25a3a..b6cfbdfc47d5 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -99,6 +99,7 @@ struct sdw_slave_runtime { * this stream, can be zero. * @slave_rt_list: Slave runtime list * @port_list: List of Master Ports configured for this stream, can be zero. + * @stream_node: sdw_stream_runtime master_list node * @bus_node: sdw_bus m_rt_list node */ struct sdw_master_runtime { @@ -108,6 +109,7 @@ struct sdw_master_runtime { unsigned int ch_count; struct list_head slave_rt_list; struct list_head port_list; + struct list_head stream_node; struct list_head bus_node; }; diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index e5c7e1ef6318..b52903fe6f31 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -747,6 +747,7 @@ struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name) return NULL; stream->name = stream_name; + INIT_LIST_HEAD(&stream->master_list); stream->state = SDW_STREAM_ALLOCATED; return stream; @@ -1245,6 +1246,48 @@ struct sdw_dpn_prop *sdw_get_slave_dpn_prop(struct sdw_slave *slave, return NULL; } +/** + * sdw_acquire_bus_lock: Acquire bus lock for all Master runtime(s) + * + * @stream: SoundWire stream + * + * Acquire bus_lock for each of the master runtime(m_rt) part of this + * stream to reconfigure the bus. + * NOTE: This function is called from SoundWire stream ops and is + * expected that a global lock is held before acquiring bus_lock. + */ +static void sdw_acquire_bus_lock(struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt = NULL; + struct sdw_bus *bus = NULL; + + /* Iterate for all Master(s) in Master list */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + + mutex_lock(&bus->bus_lock); + } +} + +/** + * sdw_release_bus_lock: Release bus lock for all Master runtime(s) + * + * @stream: SoundWire stream + * + * Release the previously held bus_lock after reconfiguring the bus. + */ +static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt = NULL; + struct sdw_bus *bus = NULL; + + /* Iterate for all Master(s) in Master list */ + list_for_each_entry_reverse(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + mutex_unlock(&bus->bus_lock); + } +} + static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt = stream->m_rt; diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 962971e6a9c7..ccd8dcdf06ab 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -769,6 +769,9 @@ struct sdw_stream_params { * @state: Current state of the stream * @type: Stream type PCM or PDM * @m_rt: Master runtime + * @master_list: List of Master runtime(s) in this stream. + * master_list can contain only one m_rt per Master instance + * for a stream */ struct sdw_stream_runtime { char *name; @@ -776,6 +779,7 @@ struct sdw_stream_runtime { enum sdw_stream_state state; enum sdw_stream_type type; struct sdw_master_runtime *m_rt; + struct list_head master_list; }; struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name); -- cgit v1.2.3-58-ga151 From 48949722ced4daacfa32f13c221f173b87231ead Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Fri, 27 Jul 2018 14:44:14 +0530 Subject: soundwire: Handle multiple master instances in a stream For each SoundWire stream operation, we need to parse master list and operate upon all master runtime. This is a preparatory patch to do the boilerplate conversion of stream handling from single master runtime to handle a list of master runtime. The code to support bank switch for multiple master instances is added in the next patch. Signed-off-by: Sanyog Kale Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 311 +++++++++++++++++++++++++----------------- include/linux/soundwire/sdw.h | 2 - 2 files changed, 188 insertions(+), 125 deletions(-) (limited to 'drivers') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index b52903fe6f31..ee024d72dd7b 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -681,35 +681,45 @@ error_1: static int do_bank_switch(struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt = stream->m_rt; + struct sdw_master_runtime *m_rt = NULL; const struct sdw_master_ops *ops; - struct sdw_bus *bus = m_rt->bus; + struct sdw_bus *bus = NULL; int ret = 0; - ops = bus->ops; - /* Pre-bank switch */ - if (ops->pre_bank_switch) { - ret = ops->pre_bank_switch(bus); + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + ops = bus->ops; + + /* Pre-bank switch */ + if (ops->pre_bank_switch) { + ret = ops->pre_bank_switch(bus); + if (ret < 0) { + dev_err(bus->dev, + "Pre bank switch op failed: %d", ret); + return ret; + } + } + + /* Bank switch */ + ret = sdw_bank_switch(bus); if (ret < 0) { - dev_err(bus->dev, "Pre bank switch op failed: %d", ret); + dev_err(bus->dev, "Bank switch failed: %d", ret); return ret; } } - /* Bank switch */ - ret = sdw_bank_switch(bus); - if (ret < 0) { - dev_err(bus->dev, "Bank switch failed: %d", ret); - return ret; - } + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + ops = bus->ops; - /* Post-bank switch */ - if (ops->post_bank_switch) { - ret = ops->post_bank_switch(bus); - if (ret < 0) { - dev_err(bus->dev, + /* Post-bank switch */ + if (ops->post_bank_switch) { + ret = ops->post_bank_switch(bus); + if (ret < 0) { + dev_err(bus->dev, "Post bank switch op failed: %d", ret); + } } } @@ -754,6 +764,21 @@ struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name) } EXPORT_SYMBOL(sdw_alloc_stream); +static struct sdw_master_runtime +*sdw_find_master_rt(struct sdw_bus *bus, + struct sdw_stream_runtime *stream) +{ + struct sdw_master_runtime *m_rt = NULL; + + /* Retrieve Bus handle if already available */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + if (m_rt->bus == bus) + return m_rt; + } + + return NULL; +} + /** * sdw_alloc_master_rt() - Allocates and initialize Master runtime handle * @@ -770,12 +795,11 @@ static struct sdw_master_runtime { struct sdw_master_runtime *m_rt; - m_rt = stream->m_rt; - /* * check if Master is already allocated (as a result of Slave adding * it first), if so skip allocation and go to configure */ + m_rt = sdw_find_master_rt(bus, stream); if (m_rt) goto stream_config; @@ -786,7 +810,7 @@ static struct sdw_master_runtime /* Initialization of Master runtime handle */ INIT_LIST_HEAD(&m_rt->port_list); INIT_LIST_HEAD(&m_rt->slave_rt_list); - stream->m_rt = m_rt; + list_add_tail(&m_rt->stream_node, &stream->master_list); list_add_tail(&m_rt->bus_node, &bus->m_rt_list); @@ -844,17 +868,21 @@ static void sdw_slave_port_release(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { struct sdw_port_runtime *p_rt, *_p_rt; - struct sdw_master_runtime *m_rt = stream->m_rt; + struct sdw_master_runtime *m_rt; struct sdw_slave_runtime *s_rt; - list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - if (s_rt->slave != slave) - continue; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + list_for_each_entry(s_rt, &m_rt->slave_rt_list, m_rt_node) { - list_for_each_entry_safe(p_rt, _p_rt, - &s_rt->port_list, port_node) { - list_del(&p_rt->port_node); - kfree(p_rt); + if (s_rt->slave != slave) + continue; + + list_for_each_entry_safe(p_rt, _p_rt, + &s_rt->port_list, port_node) { + + list_del(&p_rt->port_node); + kfree(p_rt); + } } } } @@ -871,16 +899,18 @@ static void sdw_release_slave_stream(struct sdw_slave *slave, struct sdw_stream_runtime *stream) { struct sdw_slave_runtime *s_rt, *_s_rt; - struct sdw_master_runtime *m_rt = stream->m_rt; - - /* Retrieve Slave runtime handle */ - list_for_each_entry_safe(s_rt, _s_rt, - &m_rt->slave_rt_list, m_rt_node) { + struct sdw_master_runtime *m_rt; - if (s_rt->slave == slave) { - list_del(&s_rt->m_rt_node); - kfree(s_rt); - return; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + /* Retrieve Slave runtime handle */ + list_for_each_entry_safe(s_rt, _s_rt, + &m_rt->slave_rt_list, m_rt_node) { + + if (s_rt->slave == slave) { + list_del(&s_rt->m_rt_node); + kfree(s_rt); + return; + } } } } @@ -888,6 +918,7 @@ static void sdw_release_slave_stream(struct sdw_slave *slave, /** * sdw_release_master_stream() - Free Master runtime handle * + * @m_rt: Master runtime node * @stream: Stream runtime handle. * * This function is to be called with bus_lock held @@ -895,9 +926,9 @@ static void sdw_release_slave_stream(struct sdw_slave *slave, * handle. If this is called first then sdw_release_slave_stream() will have * no effect as Slave(s) runtime handle would already be freed up. */ -static void sdw_release_master_stream(struct sdw_stream_runtime *stream) +static void sdw_release_master_stream(struct sdw_master_runtime *m_rt, + struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt = stream->m_rt; struct sdw_slave_runtime *s_rt, *_s_rt; list_for_each_entry_safe(s_rt, _s_rt, &m_rt->slave_rt_list, m_rt_node) { @@ -905,7 +936,9 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream) sdw_release_slave_stream(s_rt->slave, stream); } + list_del(&m_rt->stream_node); list_del(&m_rt->bus_node); + kfree(m_rt); } /** @@ -919,13 +952,22 @@ static void sdw_release_master_stream(struct sdw_stream_runtime *stream) int sdw_stream_remove_master(struct sdw_bus *bus, struct sdw_stream_runtime *stream) { + struct sdw_master_runtime *m_rt, *_m_rt; + mutex_lock(&bus->bus_lock); - sdw_release_master_stream(stream); - sdw_master_port_release(bus, stream->m_rt); - stream->state = SDW_STREAM_RELEASED; - kfree(stream->m_rt); - stream->m_rt = NULL; + list_for_each_entry_safe(m_rt, _m_rt, + &stream->master_list, stream_node) { + + if (m_rt->bus != bus) + continue; + + sdw_master_port_release(bus, m_rt); + sdw_release_master_stream(m_rt, stream); + } + + if (list_empty(&stream->master_list)) + stream->state = SDW_STREAM_RELEASED; mutex_unlock(&bus->bus_lock); @@ -1128,7 +1170,7 @@ int sdw_stream_add_master(struct sdw_bus *bus, goto unlock; stream_error: - sdw_release_master_stream(stream); + sdw_release_master_stream(m_rt, stream); unlock: mutex_unlock(&bus->bus_lock); return ret; @@ -1206,7 +1248,7 @@ stream_error: * we hit error so cleanup the stream, release all Slave(s) and * Master runtime */ - sdw_release_master_stream(stream); + sdw_release_master_stream(m_rt, stream); error: mutex_unlock(&slave->bus->bus_lock); return ret; @@ -1275,6 +1317,8 @@ static void sdw_acquire_bus_lock(struct sdw_stream_runtime *stream) * @stream: SoundWire stream * * Release the previously held bus_lock after reconfiguring the bus. + * NOTE: This function is called from SoundWire stream ops and is + * expected that a global lock is held before releasing bus_lock. */ static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) { @@ -1290,31 +1334,36 @@ static void sdw_release_bus_lock(struct sdw_stream_runtime *stream) static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; + struct sdw_master_runtime *m_rt = NULL; + struct sdw_bus *bus = NULL; struct sdw_master_prop *prop = NULL; struct sdw_bus_params params; int ret; - prop = &bus->prop; - memcpy(¶ms, &bus->params, sizeof(params)); + /* Prepare Master(s) and Slave(s) port(s) associated with stream */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + prop = &bus->prop; + memcpy(¶ms, &bus->params, sizeof(params)); - /* TODO: Support Asynchronous mode */ - if ((prop->max_freq % stream->params.rate) != 0) { - dev_err(bus->dev, "Async mode not supported"); - return -EINVAL; - } + /* TODO: Support Asynchronous mode */ + if ((prop->max_freq % stream->params.rate) != 0) { + dev_err(bus->dev, "Async mode not supported"); + return -EINVAL; + } - /* Increment cumulative bus bandwidth */ - /* TODO: Update this during Device-Device support */ - bus->params.bandwidth += m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; + /* Increment cumulative bus bandwidth */ + /* TODO: Update this during Device-Device support */ + bus->params.bandwidth += m_rt->stream->params.rate * + m_rt->ch_count * m_rt->stream->params.bps; + + /* Program params */ + ret = sdw_program_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Program params failed: %d", ret); + goto restore_params; + } - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - goto restore_params; } ret = do_bank_switch(stream); @@ -1323,12 +1372,16 @@ static int _sdw_prepare_stream(struct sdw_stream_runtime *stream) goto restore_params; } - /* Prepare port(s) on the new clock configuration */ - ret = sdw_prep_deprep_ports(m_rt, true); - if (ret < 0) { - dev_err(bus->dev, "Prepare port(s) failed ret = %d", - ret); - return ret; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + + /* Prepare port(s) on the new clock configuration */ + ret = sdw_prep_deprep_ports(m_rt, true); + if (ret < 0) { + dev_err(bus->dev, "Prepare port(s) failed ret = %d", + ret); + return ret; + } } stream->state = SDW_STREAM_PREPARED; @@ -1356,35 +1409,40 @@ int sdw_prepare_stream(struct sdw_stream_runtime *stream) return -EINVAL; } - mutex_lock(&stream->m_rt->bus->bus_lock); + sdw_acquire_bus_lock(stream); ret = _sdw_prepare_stream(stream); if (ret < 0) pr_err("Prepare for stream:%s failed: %d", stream->name, ret); - mutex_unlock(&stream->m_rt->bus->bus_lock); + sdw_release_bus_lock(stream); return ret; } EXPORT_SYMBOL(sdw_prepare_stream); static int _sdw_enable_stream(struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; + struct sdw_master_runtime *m_rt = NULL; + struct sdw_bus *bus = NULL; int ret; - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - return ret; - } + /* Enable Master(s) and Slave(s) port(s) associated with stream */ + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; - /* Enable port(s) */ - ret = sdw_enable_disable_ports(m_rt, true); - if (ret < 0) { - dev_err(bus->dev, "Enable port(s) failed ret: %d", ret); - return ret; + /* Program params */ + ret = sdw_program_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Program params failed: %d", ret); + return ret; + } + + /* Enable port(s) */ + ret = sdw_enable_disable_ports(m_rt, true); + if (ret < 0) { + dev_err(bus->dev, "Enable port(s) failed ret: %d", ret); + return ret; + } } ret = do_bank_switch(stream); @@ -1413,37 +1471,42 @@ int sdw_enable_stream(struct sdw_stream_runtime *stream) return -EINVAL; } - mutex_lock(&stream->m_rt->bus->bus_lock); + sdw_acquire_bus_lock(stream); ret = _sdw_enable_stream(stream); if (ret < 0) pr_err("Enable for stream:%s failed: %d", stream->name, ret); - mutex_unlock(&stream->m_rt->bus->bus_lock); + sdw_release_bus_lock(stream); return ret; } EXPORT_SYMBOL(sdw_enable_stream); static int _sdw_disable_stream(struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; + struct sdw_master_runtime *m_rt = NULL; + struct sdw_bus *bus = NULL; int ret; - /* Disable port(s) */ - ret = sdw_enable_disable_ports(m_rt, false); - if (ret < 0) { - dev_err(bus->dev, "Disable port(s) failed: %d", ret); - return ret; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + /* Disable port(s) */ + ret = sdw_enable_disable_ports(m_rt, false); + if (ret < 0) { + dev_err(bus->dev, "Disable port(s) failed: %d", ret); + return ret; + } } - stream->state = SDW_STREAM_DISABLED; - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - return ret; + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + /* Program params */ + ret = sdw_program_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Program params failed: %d", ret); + return ret; + } } return do_bank_switch(stream); @@ -1465,43 +1528,46 @@ int sdw_disable_stream(struct sdw_stream_runtime *stream) return -EINVAL; } - mutex_lock(&stream->m_rt->bus->bus_lock); + sdw_acquire_bus_lock(stream); ret = _sdw_disable_stream(stream); if (ret < 0) pr_err("Disable for stream:%s failed: %d", stream->name, ret); - mutex_unlock(&stream->m_rt->bus->bus_lock); + sdw_release_bus_lock(stream); return ret; } EXPORT_SYMBOL(sdw_disable_stream); static int _sdw_deprepare_stream(struct sdw_stream_runtime *stream) { - struct sdw_master_runtime *m_rt = stream->m_rt; - struct sdw_bus *bus = m_rt->bus; + struct sdw_master_runtime *m_rt = NULL; + struct sdw_bus *bus = NULL; int ret = 0; - /* De-prepare port(s) */ - ret = sdw_prep_deprep_ports(m_rt, false); - if (ret < 0) { - dev_err(bus->dev, "De-prepare port(s) failed: %d", ret); - return ret; - } + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + /* De-prepare port(s) */ + ret = sdw_prep_deprep_ports(m_rt, false); + if (ret < 0) { + dev_err(bus->dev, "De-prepare port(s) failed: %d", ret); + return ret; + } - stream->state = SDW_STREAM_DEPREPARED; + /* TODO: Update this during Device-Device support */ + bus->params.bandwidth -= m_rt->stream->params.rate * + m_rt->ch_count * m_rt->stream->params.bps; - /* TODO: Update this during Device-Device support */ - bus->params.bandwidth -= m_rt->stream->params.rate * - m_rt->ch_count * m_rt->stream->params.bps; + /* Program params */ + ret = sdw_program_params(bus); + if (ret < 0) { + dev_err(bus->dev, "Program params failed: %d", ret); + return ret; + } - /* Program params */ - ret = sdw_program_params(bus); - if (ret < 0) { - dev_err(bus->dev, "Program params failed: %d", ret); - return ret; } + stream->state = SDW_STREAM_DEPREPARED; return do_bank_switch(stream); } @@ -1521,13 +1587,12 @@ int sdw_deprepare_stream(struct sdw_stream_runtime *stream) return -EINVAL; } - mutex_lock(&stream->m_rt->bus->bus_lock); - + sdw_acquire_bus_lock(stream); ret = _sdw_deprepare_stream(stream); if (ret < 0) pr_err("De-prepare for stream:%d failed: %d", ret, ret); - mutex_unlock(&stream->m_rt->bus->bus_lock); + sdw_release_bus_lock(stream); return ret; } EXPORT_SYMBOL(sdw_deprepare_stream); diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index ccd8dcdf06ab..03df709fb8ef 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -768,7 +768,6 @@ struct sdw_stream_params { * @params: Stream parameters * @state: Current state of the stream * @type: Stream type PCM or PDM - * @m_rt: Master runtime * @master_list: List of Master runtime(s) in this stream. * master_list can contain only one m_rt per Master instance * for a stream @@ -778,7 +777,6 @@ struct sdw_stream_runtime { struct sdw_stream_params params; enum sdw_stream_state state; enum sdw_stream_type type; - struct sdw_master_runtime *m_rt; struct list_head master_list; }; -- cgit v1.2.3-58-ga151 From ce6e74d008ff5c8b43e3bafaa7343bf7eb69593e Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Fri, 27 Jul 2018 14:44:16 +0530 Subject: soundwire: Add support for multi link bank switch In cases of multiple Masters in a stream, synchronization between multiple Master(s) is achieved by performing bank switch together and using Master methods. Add sdw_ml_bank_switch() to wait for completion of bank switch. Signed-off-by: Sanyog Kale Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/bus.c | 5 ++ drivers/soundwire/bus.h | 2 + drivers/soundwire/stream.c | 145 ++++++++++++++++++++++++++++++++++++++---- include/linux/soundwire/sdw.h | 4 ++ 4 files changed, 144 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c index dbabd5e69343..1cbfedfc20ef 100644 --- a/drivers/soundwire/bus.c +++ b/drivers/soundwire/bus.c @@ -35,6 +35,11 @@ int sdw_add_bus_master(struct sdw_bus *bus) INIT_LIST_HEAD(&bus->slaves); INIT_LIST_HEAD(&bus->m_rt_list); + /* + * Initialize multi_link flag + * TODO: populate this flag by reading property from FW node + */ + bus->multi_link = false; if (bus->ops->read_prop) { ret = bus->ops->read_prop(bus); if (ret < 0) { diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h index b6cfbdfc47d5..c77de05b8100 100644 --- a/drivers/soundwire/bus.h +++ b/drivers/soundwire/bus.h @@ -4,6 +4,8 @@ #ifndef __SDW_BUS_H #define __SDW_BUS_H +#define DEFAULT_BANK_SWITCH_TIMEOUT 3000 + #if IS_ENABLED(CONFIG_ACPI) int sdw_acpi_find_slaves(struct sdw_bus *bus); #else diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index ee024d72dd7b..3d98f20cbd6a 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -626,9 +626,10 @@ static int sdw_program_params(struct sdw_bus *bus) return ret; } -static int sdw_bank_switch(struct sdw_bus *bus) +static int sdw_bank_switch(struct sdw_bus *bus, int m_rt_count) { int col_index, row_index; + bool multi_link; struct sdw_msg *wr_msg; u8 *wbuf = NULL; int ret = 0; @@ -638,6 +639,8 @@ static int sdw_bank_switch(struct sdw_bus *bus) if (!wr_msg) return -ENOMEM; + bus->defer_msg.msg = wr_msg; + wbuf = kzalloc(sizeof(*wbuf), GFP_KERNEL); if (!wbuf) { ret = -ENOMEM; @@ -658,17 +661,29 @@ static int sdw_bank_switch(struct sdw_bus *bus) SDW_MSG_FLAG_WRITE, wbuf); wr_msg->ssp_sync = true; - ret = sdw_transfer(bus, wr_msg); + /* + * Set the multi_link flag only when both the hardware supports + * and there is a stream handled by multiple masters + */ + multi_link = bus->multi_link && (m_rt_count > 1); + + if (multi_link) + ret = sdw_transfer_defer(bus, wr_msg, &bus->defer_msg); + else + ret = sdw_transfer(bus, wr_msg); + if (ret < 0) { dev_err(bus->dev, "Slave frame_ctrl reg write failed"); goto error; } - kfree(wr_msg); - kfree(wbuf); - bus->defer_msg.msg = NULL; - bus->params.curr_bank = !bus->params.curr_bank; - bus->params.next_bank = !bus->params.next_bank; + if (!multi_link) { + kfree(wr_msg); + kfree(wbuf); + bus->defer_msg.msg = NULL; + bus->params.curr_bank = !bus->params.curr_bank; + bus->params.next_bank = !bus->params.next_bank; + } return 0; @@ -679,36 +694,87 @@ error_1: return ret; } +/** + * sdw_ml_sync_bank_switch: Multilink register bank switch + * + * @bus: SDW bus instance + * + * Caller function should free the buffers on error + */ +static int sdw_ml_sync_bank_switch(struct sdw_bus *bus) +{ + unsigned long time_left; + + if (!bus->multi_link) + return 0; + + /* Wait for completion of transfer */ + time_left = wait_for_completion_timeout(&bus->defer_msg.complete, + bus->bank_switch_timeout); + + if (!time_left) { + dev_err(bus->dev, "Controller Timed out on bank switch"); + return -ETIMEDOUT; + } + + bus->params.curr_bank = !bus->params.curr_bank; + bus->params.next_bank = !bus->params.next_bank; + + if (bus->defer_msg.msg) { + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + } + + return 0; +} + static int do_bank_switch(struct sdw_stream_runtime *stream) { struct sdw_master_runtime *m_rt = NULL; const struct sdw_master_ops *ops; struct sdw_bus *bus = NULL; + bool multi_link = false; int ret = 0; - list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; ops = bus->ops; + if (bus->multi_link) { + multi_link = true; + mutex_lock(&bus->msg_lock); + } + /* Pre-bank switch */ if (ops->pre_bank_switch) { ret = ops->pre_bank_switch(bus); if (ret < 0) { dev_err(bus->dev, "Pre bank switch op failed: %d", ret); - return ret; + goto msg_unlock; } } - /* Bank switch */ - ret = sdw_bank_switch(bus); + /* + * Perform Bank switch operation. + * For multi link cases, the actual bank switch is + * synchronized across all Masters and happens later as a + * part of post_bank_switch ops. + */ + ret = sdw_bank_switch(bus, stream->m_rt_count); if (ret < 0) { dev_err(bus->dev, "Bank switch failed: %d", ret); - return ret; + goto error; + } } + /* + * For multi link cases, it is expected that the bank switch is + * triggered by the post_bank_switch for the first Master in the list + * and for the other Masters the post_bank_switch() should return doing + * nothing. + */ list_for_each_entry(m_rt, &stream->master_list, stream_node) { bus = m_rt->bus; ops = bus->ops; @@ -719,7 +785,47 @@ static int do_bank_switch(struct sdw_stream_runtime *stream) if (ret < 0) { dev_err(bus->dev, "Post bank switch op failed: %d", ret); + goto error; } + } else if (bus->multi_link && stream->m_rt_count > 1) { + dev_err(bus->dev, + "Post bank switch ops not implemented"); + goto error; + } + + /* Set the bank switch timeout to default, if not set */ + if (!bus->bank_switch_timeout) + bus->bank_switch_timeout = DEFAULT_BANK_SWITCH_TIMEOUT; + + /* Check if bank switch was successful */ + ret = sdw_ml_sync_bank_switch(bus); + if (ret < 0) { + dev_err(bus->dev, + "multi link bank switch failed: %d", ret); + goto error; + } + + mutex_unlock(&bus->msg_lock); + } + + return ret; + +error: + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + + bus = m_rt->bus; + + kfree(bus->defer_msg.msg->buf); + kfree(bus->defer_msg.msg); + } + +msg_unlock: + + if (multi_link) { + list_for_each_entry(m_rt, &stream->master_list, stream_node) { + bus = m_rt->bus; + if (mutex_is_locked(&bus->msg_lock)) + mutex_unlock(&bus->msg_lock); } } @@ -964,6 +1070,7 @@ int sdw_stream_remove_master(struct sdw_bus *bus, sdw_master_port_release(bus, m_rt); sdw_release_master_stream(m_rt, stream); + stream->m_rt_count--; } if (list_empty(&stream->master_list)) @@ -1150,6 +1257,18 @@ int sdw_stream_add_master(struct sdw_bus *bus, mutex_lock(&bus->bus_lock); + /* + * For multi link streams, add the second master only if + * the bus supports it. + * Check if bus->multi_link is set + */ + if (!bus->multi_link && stream->m_rt_count > 0) { + dev_err(bus->dev, + "Multilink not supported, link %d", bus->link_id); + ret = -EINVAL; + goto unlock; + } + m_rt = sdw_alloc_master_rt(bus, stream_config, stream); if (!m_rt) { dev_err(bus->dev, @@ -1167,6 +1286,8 @@ int sdw_stream_add_master(struct sdw_bus *bus, if (ret) goto stream_error; + stream->m_rt_count++; + goto unlock; stream_error: diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index 03df709fb8ef..c6aa2bf847c7 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -678,6 +678,9 @@ struct sdw_master_ops { * @defer_msg: Defer message * @clk_stop_timeout: Clock stop timeout computed * @bank_switch_timeout: Bank switch timeout computed + * @multi_link: Store bus property that indicates if multi links + * are supported. This flag is populated by drivers after reading + * appropriate firmware (ACPI/DT). */ struct sdw_bus { struct device *dev; @@ -694,6 +697,7 @@ struct sdw_bus { struct sdw_defer defer_msg; unsigned int clk_stop_timeout; u32 bank_switch_timeout; + bool multi_link; }; int sdw_add_bus_master(struct sdw_bus *bus); -- cgit v1.2.3-58-ga151 From 9b5c132a1ec98895fe40ba73a19e0a17293122e5 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Fri, 27 Jul 2018 14:44:15 +0530 Subject: soundwire: keep track of Masters in a stream A multi link bankswitch can be done if the hardware supports and the stream is handled by multiple Master(s). This preparatory patch adds support to track m_rt in a stream. Modifying m_rt_count and usage is added as part of the next patch. Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/stream.c | 1 + include/linux/soundwire/sdw.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/soundwire/stream.c b/drivers/soundwire/stream.c index 3d98f20cbd6a..bd879b1a76c8 100644 --- a/drivers/soundwire/stream.c +++ b/drivers/soundwire/stream.c @@ -865,6 +865,7 @@ struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name) stream->name = stream_name; INIT_LIST_HEAD(&stream->master_list); stream->state = SDW_STREAM_ALLOCATED; + stream->m_rt_count = 0; return stream; } diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index c6aa2bf847c7..df313913e856 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -775,6 +775,7 @@ struct sdw_stream_params { * @master_list: List of Master runtime(s) in this stream. * master_list can contain only one m_rt per Master instance * for a stream + * @m_rt_count: Count of Master runtime(s) in this stream */ struct sdw_stream_runtime { char *name; @@ -782,6 +783,7 @@ struct sdw_stream_runtime { enum sdw_stream_state state; enum sdw_stream_type type; struct list_head master_list; + int m_rt_count; }; struct sdw_stream_runtime *sdw_alloc_stream(char *stream_name); -- cgit v1.2.3-58-ga151 From 30246e2d78c0b06d67f77c4239f6741e77f42185 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Fri, 27 Jul 2018 14:44:17 +0530 Subject: soundwire: intel: Add pre/post bank switch ops To support multi link on Intel platforms, we need to update SDW SHIM registers. So, add pre/post bank switch ops for the same in Intel driver. Signed-off-by: Sanyog Kale Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'drivers') diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index 0a8990e758f9..da7f91f1a588 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -397,6 +397,69 @@ static int intel_config_stream(struct sdw_intel *sdw, return -EIO; } +/* + * bank switch routines + */ + +static int intel_pre_bank_switch(struct sdw_bus *bus) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + struct sdw_intel *sdw = cdns_to_intel(cdns); + void __iomem *shim = sdw->res->shim; + int sync_reg; + + /* Write to register only for multi-link */ + if (!bus->multi_link) + return 0; + + /* Read SYNC register */ + sync_reg = intel_readl(shim, SDW_SHIM_SYNC); + sync_reg |= SDW_SHIM_SYNC_CMDSYNC << sdw->instance; + intel_writel(shim, SDW_SHIM_SYNC, sync_reg); + + return 0; +} + +static int intel_post_bank_switch(struct sdw_bus *bus) +{ + struct sdw_cdns *cdns = bus_to_cdns(bus); + struct sdw_intel *sdw = cdns_to_intel(cdns); + void __iomem *shim = sdw->res->shim; + int sync_reg, ret; + + /* Write to register only for multi-link */ + if (!bus->multi_link) + return 0; + + /* Read SYNC register */ + sync_reg = intel_readl(shim, SDW_SHIM_SYNC); + + /* + * post_bank_switch() ops is called from the bus in loop for + * all the Masters in the steam with the expectation that + * we trigger the bankswitch for the only first Master in the list + * and do nothing for the other Masters + * + * So, set the SYNCGO bit only if CMDSYNC bit is set for any Master. + */ + if (!(sync_reg & SDW_SHIM_SYNC_CMDSYNC_MASK)) + return 0; + + /* + * Set SyncGO bit to synchronously trigger a bank switch for + * all the masters. A write to SYNCGO bit clears CMDSYNC bit for all + * the Masters. + */ + sync_reg |= SDW_SHIM_SYNC_SYNCGO; + + ret = intel_clear_bit(shim, SDW_SHIM_SYNC, sync_reg, + SDW_SHIM_SYNC_SYNCGO); + if (ret < 0) + dev_err(sdw->cdns.dev, "Post bank switch failed: %d", ret); + + return ret; +} + /* * DAI routines */ @@ -750,6 +813,8 @@ static struct sdw_master_ops sdw_intel_ops = { .xfer_msg_defer = cdns_xfer_msg_defer, .reset_page_addr = cdns_reset_page_addr, .set_bus_conf = cdns_bus_conf, + .pre_bank_switch = intel_pre_bank_switch, + .post_bank_switch = intel_post_bank_switch, }; /* -- cgit v1.2.3-58-ga151 From e1c815f4b24a305e5bc9eceb541674bf4d02b709 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Tue, 7 Aug 2018 11:54:50 +0530 Subject: soundwire: intel: Fix uninitialized adev deref In case of error, we can dereference uninitialized 'adev' drivers/soundwire/intel_init.c:154 sdw_intel_acpi_cb() error: uninitialized symbol 'adev'. Fix that by not using adev for warn print and make it pr_err. Reported-by: Dan Carpenter Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/intel_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c index d1ea6b4d0ad3..5c8a20d99878 100644 --- a/drivers/soundwire/intel_init.c +++ b/drivers/soundwire/intel_init.c @@ -151,7 +151,7 @@ static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, struct acpi_device *adev; if (acpi_bus_get_device(handle, &adev)) { - dev_err(&adev->dev, "Couldn't find ACPI handle\n"); + pr_err("%s: Couldn't find ACPI handle\n", __func__); return AE_NOT_FOUND; } -- cgit v1.2.3-58-ga151 From c272a766db4f826d3ba35c734894dfcdb0740827 Mon Sep 17 00:00:00 2001 From: Shreyas NC Date: Tue, 7 Aug 2018 17:26:00 +0530 Subject: soundwire: intel: Remove duplicate assignment Signed-off-by: Shreyas NC Acked-by: Pierre-Louis Bossart Signed-off-by: Vinod Koul --- drivers/soundwire/intel.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c index da7f91f1a588..c5ee97ee7886 100644 --- a/drivers/soundwire/intel.c +++ b/drivers/soundwire/intel.c @@ -845,9 +845,6 @@ static int intel_probe(struct platform_device *pdev) sdw_intel_ops.read_prop = intel_prop_read; sdw->cdns.bus.ops = &sdw_intel_ops; - sdw_intel_ops.read_prop = intel_prop_read; - sdw->cdns.bus.ops = &sdw_intel_ops; - platform_set_drvdata(pdev, sdw); ret = sdw_add_bus_master(&sdw->cdns.bus); -- cgit v1.2.3-58-ga151 From 1213a366817299ed91fcb4e738c057a9ac4c8666 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 7 Aug 2018 18:21:22 +0200 Subject: extcon: maxim: Add SPDX license identifiers Replace GPL v2.0+ license statements with SPDX license identifiers. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max14577.c | 24 +++++++----------------- drivers/extcon/extcon-max77693.c | 22 ++++++---------------- drivers/extcon/extcon-max77843.c | 19 +++++++------------ drivers/extcon/extcon-max8997.c | 22 ++++++---------------- 4 files changed, 26 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index b871836da8a4..22d2feb1f8bc 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -1,20 +1,10 @@ -/* - * extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC - * - * Copyright (C) 2013,2014 Samsung Electronics - * Chanwoo Choi - * Krzysztof Kozlowski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// extcon-max14577.c - MAX14577/77836 extcon driver to support MUIC +// +// Copyright (C) 2013,2014 Samsung Electronics +// Chanwoo Choi +// Krzysztof Kozlowski #include #include diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 227651ff9666..a79537ebb671 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -1,19 +1,9 @@ -/* - * extcon-max77693.c - MAX77693 extcon driver to support MAX77693 MUIC - * - * Copyright (C) 2012 Samsung Electrnoics - * Chanwoo Choi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// extcon-max77693.c - MAX77693 extcon driver to support MAX77693 MUIC +// +// Copyright (C) 2012 Samsung Electrnoics +// Chanwoo Choi #include #include diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index c9fcd6cd41cb..b98cbd0362f5 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -1,15 +1,10 @@ -/* - * extcon-max77843.c - Maxim MAX77843 extcon driver to support - * MUIC(Micro USB Interface Controller) - * - * Copyright (C) 2015 Samsung Electronics - * Author: Jaewon Kim - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// extcon-max77843.c - Maxim MAX77843 extcon driver to support +// MUIC(Micro USB Interface Controller) +// +// Copyright (C) 2015 Samsung Electronics +// Author: Jaewon Kim #include #include diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 9f30f4929b72..bdabb2479e0d 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -1,19 +1,9 @@ -/* - * extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC - * - * Copyright (C) 2012 Samsung Electronics - * Donggeun Kim - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// extcon-max8997.c - MAX8997 extcon driver to support MAX8997 MUIC +// +// Copyright (C) 2012 Samsung Electronics +// Donggeun Kim #include #include -- cgit v1.2.3-58-ga151 From cff7499d7eb4196f21fdd1bf643aad12d846ee5d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:51 +0300 Subject: extcon: Make static analyzer happy about union assignment When assign unions we need to supply non-scalar value, otherwise static analyzer is not happy: CHECK drivers/extcon/extcon.c drivers/extcon/extcon.c:631:22: warning: cast to non-scalar Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index b9d27c8fe57e..c21650a92689 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -628,7 +628,7 @@ int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned long flags; int index, ret = 0; - *prop_val = (union extcon_property_value)(0); + *prop_val = (union extcon_property_value){0}; if (!edev) return -EINVAL; -- cgit v1.2.3-58-ga151 From 69f75a4f8c7b56a5cb050255167cf77a6f4c2fe1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:52 +0300 Subject: extcon: Switch to use kasprintf() instead of open coded Switch to use kasprintf() instead of open coded variant. No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index c21650a92689..5ab0498be652 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -1123,7 +1123,6 @@ int extcon_dev_register(struct extcon_dev *edev) (unsigned long)atomic_inc_return(&edev_no)); if (edev->max_supported) { - char buf[10]; char *str; struct extcon_cable *cable; @@ -1137,9 +1136,7 @@ int extcon_dev_register(struct extcon_dev *edev) for (index = 0; index < edev->max_supported; index++) { cable = &edev->cables[index]; - snprintf(buf, 10, "cable.%d", index); - str = kzalloc(strlen(buf) + 1, - GFP_KERNEL); + str = kasprintf(GFP_KERNEL, "cable.%d", index); if (!str) { for (index--; index >= 0; index--) { cable = &edev->cables[index]; @@ -1149,7 +1146,6 @@ int extcon_dev_register(struct extcon_dev *edev) goto err_alloc_cables; } - strcpy(str, buf); cable->edev = edev; cable->cable_index = index; @@ -1172,7 +1168,6 @@ int extcon_dev_register(struct extcon_dev *edev) } if (edev->max_supported && edev->mutually_exclusive) { - char buf[80]; char *name; /* Count the size of mutually_exclusive array */ @@ -1197,9 +1192,8 @@ int extcon_dev_register(struct extcon_dev *edev) } for (index = 0; edev->mutually_exclusive[index]; index++) { - sprintf(buf, "0x%x", edev->mutually_exclusive[index]); - name = kzalloc(strlen(buf) + 1, - GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "0x%x", + edev->mutually_exclusive[index]); if (!name) { for (index--; index >= 0; index--) { kfree(edev->d_attrs_muex[index].attr. @@ -1210,7 +1204,6 @@ int extcon_dev_register(struct extcon_dev *edev) ret = -ENOMEM; goto err_muex; } - strcpy(name, buf); sysfs_attr_init(&edev->d_attrs_muex[index].attr); edev->d_attrs_muex[index].attr.name = name; edev->d_attrs_muex[index].attr.mode = 0000; -- cgit v1.2.3-58-ga151 From 962341b54b99965ebec5f70c8d39f1c382eea833 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:53 +0300 Subject: extcon: cht-wc: Return from default case to avoid warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we have first case to fall through it's not enough to put single comment there to satisfy compiler. Instead of doing that, return fall back value directly from default case. This to avoid following warnings: drivers/extcon/extcon-intel-cht-wc.c: In function ‘cht_wc_extcon_get_charger’: include/linux/device.h:1420:2: warning: this statement may fall through [-Wimplicit-fallthrough=] _dev_warn(dev, dev_fmt(fmt), ##__VA_ARGS__) ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/extcon/extcon-intel-cht-wc.c:148:3: note: in expansion of macro ‘dev_warn’ dev_warn(ext->dev, ^~~~~~~~ drivers/extcon/extcon-intel-cht-wc.c:152:2: note: here case CHT_WC_USBSRC_TYPE_SDP: ^~~~ Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 5e1dd2772278..bdb67878179e 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -156,7 +156,7 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext, dev_warn(ext->dev, "Unhandled charger type %d, defaulting to SDP\n", ret); - /* Fall through, treat as SDP */ + return EXTCON_CHG_USB_SDP; case CHT_WC_USBSRC_TYPE_SDP: case CHT_WC_USBSRC_TYPE_FLOAT_DP_DN: case CHT_WC_USBSRC_TYPE_OTHER: -- cgit v1.2.3-58-ga151 From 001d3eccf9fc9b598b155f94b5f727ee825252d9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:54 +0300 Subject: extcon: cht-wc: Fix definition names according to spec There is no suffix MASK in the spec and other small spelling fixes. Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index bdb67878179e..71b1126dbb0b 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -32,10 +32,10 @@ #define CHT_WC_CHGRCTRL0_EMRGCHREN BIT(1) #define CHT_WC_CHGRCTRL0_EXTCHRDIS BIT(2) #define CHT_WC_CHGRCTRL0_SWCONTROL BIT(3) -#define CHT_WC_CHGRCTRL0_TTLCK_MASK BIT(4) -#define CHT_WC_CHGRCTRL0_CCSM_OFF_MASK BIT(5) -#define CHT_WC_CHGRCTRL0_DBPOFF_MASK BIT(6) -#define CHT_WC_CHGRCTRL0_WDT_NOKICK BIT(7) +#define CHT_WC_CHGRCTRL0_TTLCK BIT(4) +#define CHT_WC_CHGRCTRL0_CCSM_OFF BIT(5) +#define CHT_WC_CHGRCTRL0_DBPOFF BIT(6) +#define CHT_WC_CHGRCTRL0_CHR_WDT_NOKICK BIT(7) #define CHT_WC_CHGRCTRL1 0x5e17 @@ -52,7 +52,7 @@ #define CHT_WC_USBSRC_TYPE_ACA 4 #define CHT_WC_USBSRC_TYPE_SE1 5 #define CHT_WC_USBSRC_TYPE_MHL 6 -#define CHT_WC_USBSRC_TYPE_FLOAT_DP_DN 7 +#define CHT_WC_USBSRC_TYPE_FLOATING 7 #define CHT_WC_USBSRC_TYPE_OTHER 8 #define CHT_WC_USBSRC_TYPE_DCP_EXTPHY 9 @@ -61,7 +61,7 @@ #define CHT_WC_PWRSRC_STS 0x6e1e #define CHT_WC_PWRSRC_VBUS BIT(0) #define CHT_WC_PWRSRC_DC BIT(1) -#define CHT_WC_PWRSRC_BAT BIT(2) +#define CHT_WC_PWRSRC_BATT BIT(2) #define CHT_WC_PWRSRC_ID_GND BIT(3) #define CHT_WC_PWRSRC_ID_FLOAT BIT(4) @@ -158,7 +158,7 @@ static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext, ret); return EXTCON_CHG_USB_SDP; case CHT_WC_USBSRC_TYPE_SDP: - case CHT_WC_USBSRC_TYPE_FLOAT_DP_DN: + case CHT_WC_USBSRC_TYPE_FLOATING: case CHT_WC_USBSRC_TYPE_OTHER: return EXTCON_CHG_USB_SDP; case CHT_WC_USBSRC_TYPE_CDP: @@ -279,7 +279,7 @@ static int cht_wc_extcon_sw_control(struct cht_wc_extcon_data *ext, bool enable) { int ret, mask, val; - mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF_MASK; + mask = CHT_WC_CHGRCTRL0_SWCONTROL | CHT_WC_CHGRCTRL0_CCSM_OFF; val = enable ? mask : 0; ret = regmap_update_bits(ext->regmap, CHT_WC_CHGRCTRL0, mask, val); if (ret) -- cgit v1.2.3-58-ga151 From a4722503fadf0ed6219153da7ed6fa6cb4da98ec Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:55 +0300 Subject: extcon: cht-wc: Correct USBID bit field handling USBID is 2-bit bit field according to specification. Make it clear. No functional change intended. Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 71b1126dbb0b..ad1fd70e4023 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -62,8 +62,11 @@ #define CHT_WC_PWRSRC_VBUS BIT(0) #define CHT_WC_PWRSRC_DC BIT(1) #define CHT_WC_PWRSRC_BATT BIT(2) -#define CHT_WC_PWRSRC_ID_GND BIT(3) -#define CHT_WC_PWRSRC_ID_FLOAT BIT(4) +#define CHT_WC_PWRSRC_USBID_MASK GENMASK(4, 3) +#define CHT_WC_PWRSRC_USBID_SHIFT 3 +#define CHT_WC_PWRSRC_RID_ACA 0 +#define CHT_WC_PWRSRC_RID_GND 1 +#define CHT_WC_PWRSRC_RID_FLOAT 2 #define CHT_WC_VBUS_GPIO_CTLO 0x6e2d #define CHT_WC_VBUS_GPIO_CTLO_OUTPUT BIT(0) @@ -104,16 +107,20 @@ struct cht_wc_extcon_data { static int cht_wc_extcon_get_id(struct cht_wc_extcon_data *ext, int pwrsrc_sts) { - if (pwrsrc_sts & CHT_WC_PWRSRC_ID_GND) + switch ((pwrsrc_sts & CHT_WC_PWRSRC_USBID_MASK) >> CHT_WC_PWRSRC_USBID_SHIFT) { + case CHT_WC_PWRSRC_RID_GND: return USB_ID_GND; - if (pwrsrc_sts & CHT_WC_PWRSRC_ID_FLOAT) + case CHT_WC_PWRSRC_RID_FLOAT: return USB_ID_FLOAT; - - /* - * Once we have iio support for the gpadc we should read the USBID - * gpadc channel here and determine ACA role based on that. - */ - return USB_ID_FLOAT; + case CHT_WC_PWRSRC_RID_ACA: + default: + /* + * Once we have IIO support for the GPADC we should read + * the USBID GPADC channel here and determine ACA role + * based on that. + */ + return USB_ID_FLOAT; + } } static int cht_wc_extcon_get_charger(struct cht_wc_extcon_data *ext, @@ -292,6 +299,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) { struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); struct cht_wc_extcon_data *ext; + unsigned long mask = ~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_USBID_MASK); int irq, ret; irq = platform_get_irq(pdev, 0); @@ -352,9 +360,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev) } /* Unmask irqs */ - ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, - (int)~(CHT_WC_PWRSRC_VBUS | CHT_WC_PWRSRC_ID_GND | - CHT_WC_PWRSRC_ID_FLOAT)); + ret = regmap_write(ext->regmap, CHT_WC_PWRSRC_IRQ_MASK, mask); if (ret) { dev_err(ext->dev, "Error writing irq-mask: %d\n", ret); goto disable_sw_control; -- cgit v1.2.3-58-ga151 From 900ed55571941c5b61869954dc96e082e2ded78d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:56 +0300 Subject: extcon: cht-wc: Convert to use SPDX identifier Convert driver to use SPDX identifier. Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-cht-wc.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index ad1fd70e4023..5ef215297101 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Extcon charger detection driver for Intel Cherrytrail Whiskey Cove PMIC * Copyright (C) 2017 Hans de Goede * * Based on various non upstream patches to support the CHT Whiskey Cove PMIC: * Copyright (C) 2013-2015 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include -- cgit v1.2.3-58-ga151 From 2e464ff0a9b1ab0fbb56e2ca8b1f5ff8c9f4e02f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 27 Aug 2018 18:35:57 +0300 Subject: extcon: int3496: Convert to use SPDX identifier Convert driver to use SPDX identifier. While here, fix MODULE_LICENSE() string to be the same as license text states. Signed-off-by: Andy Shevchenko Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-intel-int3496.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c index fd24debe58a3..80c9abcc3f97 100644 --- a/drivers/extcon/extcon-intel-int3496.c +++ b/drivers/extcon/extcon-intel-int3496.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Intel INT3496 ACPI device extcon driver * @@ -7,15 +8,6 @@ * * Copyright (c) 2014, Intel Corporation. * Author: David Cohen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include @@ -192,4 +184,4 @@ module_platform_driver(int3496_driver); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Intel INT3496 ACPI device extcon driver"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 83b15fed91fa23facd159fb72c82241cbdc93d3f Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 16 Aug 2018 14:42:14 -0500 Subject: drivers: fpga: fix two trivial spelling mistakes Trivial fix to two spelling mistakes "execeeded" -> "exceeded" "Invaild" -> "Invalid" Signed-off-by: Colin Ian King Acked-by: Alan Tull Acked-by: Moritz Fischer Signed-off-by: Greg Kroah-Hartman --- drivers/fpga/dfl-afu-dma-region.c | 2 +- drivers/fpga/dfl-fme-mgr.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/fpga/dfl-afu-dma-region.c b/drivers/fpga/dfl-afu-dma-region.c index 0e81d33af856..025aba3ea76c 100644 --- a/drivers/fpga/dfl-afu-dma-region.c +++ b/drivers/fpga/dfl-afu-dma-region.c @@ -70,7 +70,7 @@ static int afu_dma_adjust_locked_vm(struct device *dev, long npages, bool incr) dev_dbg(dev, "[%d] RLIMIT_MEMLOCK %c%ld %ld/%ld%s\n", current->pid, incr ? '+' : '-', npages << PAGE_SHIFT, current->mm->locked_vm << PAGE_SHIFT, rlimit(RLIMIT_MEMLOCK), - ret ? "- execeeded" : ""); + ret ? "- exceeded" : ""); up_write(¤t->mm->mmap_sem); diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c index b5ef405b6d88..9f045d058cfd 100644 --- a/drivers/fpga/dfl-fme-mgr.c +++ b/drivers/fpga/dfl-fme-mgr.c @@ -201,7 +201,7 @@ static int fme_mgr_write(struct fpga_manager *mgr, } if (count < 4) { - dev_err(dev, "Invaild PR bitstream size\n"); + dev_err(dev, "Invalid PR bitstream size\n"); return -EINVAL; } -- cgit v1.2.3-58-ga151 From d765edbb301c0e196015a59b17420558088ea33f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Aug 2018 23:06:08 +0000 Subject: vmbus: add driver_override support Add support for overriding the default driver for a VMBus device in the same way that it can be done for PCI devices. This patch adds the /sys/bus/vmbus/devices/.../driver_override file and the logic for matching. This is used by driverctl tool to do driver override. https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitlab.com%2Fdriverctl%2Fdriverctl&data=02%7C01%7Ckys%40microsoft.com%7C42e803feb2c544ef6ea908d5fd538878%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636693457619960040&sdata=kEyYHRIjNZCk%2B37moCSqbrZL426YccNQrsWpENcrZdw%3D&reserved=0 Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-vmbus | 21 ++++++ drivers/hv/vmbus_drv.c | 115 +++++++++++++++++++++++++----- include/linux/hyperv.h | 1 + 3 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-vmbus (limited to 'drivers') diff --git a/Documentation/ABI/testing/sysfs-bus-vmbus b/Documentation/ABI/testing/sysfs-bus-vmbus new file mode 100644 index 000000000000..91e6c065973c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-vmbus @@ -0,0 +1,21 @@ +What: /sys/bus/vmbus/devices/.../driver_override +Date: August 2019 +Contact: Stephen Hemminger +Description: + This file allows the driver for a device to be specified which + will override standard static and dynamic ID matching. When + specified, only a driver with a name matching the value written + to driver_override will have an opportunity to bind to the + device. The override is specified by writing a string to the + driver_override file (echo uio_hv_generic > driver_override) and + may be cleared with an empty string (echo > driver_override). + This returns the device to standard matching rules binding. + Writing to driver_override does not automatically unbind the + device from its current driver or make any attempt to + automatically load the specified driver. If no driver with a + matching name is currently loaded in the kernel, the device + will not bind to any driver. This also allows devices to + opt-out of driver binding using a driver_override name such as + "none". Only a single driver may be specified in the override, + there is no support for parsing delimiters. + diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index b1b548a21f91..e6d8fdac6d8b 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -498,6 +498,54 @@ static ssize_t device_show(struct device *dev, } static DEVICE_ATTR_RO(device); +static ssize_t driver_override_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + char *driver_override, *old, *cp; + + /* We need to keep extra room for a newline */ + if (count >= (PAGE_SIZE - 1)) + return -EINVAL; + + driver_override = kstrndup(buf, count, GFP_KERNEL); + if (!driver_override) + return -ENOMEM; + + cp = strchr(driver_override, '\n'); + if (cp) + *cp = '\0'; + + device_lock(dev); + old = hv_dev->driver_override; + if (strlen(driver_override)) { + hv_dev->driver_override = driver_override; + } else { + kfree(driver_override); + hv_dev->driver_override = NULL; + } + device_unlock(dev); + + kfree(old); + + return count; +} + +static ssize_t driver_override_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + ssize_t len; + + device_lock(dev); + len = snprintf(buf, PAGE_SIZE, "%s\n", hv_dev->driver_override); + device_unlock(dev); + + return len; +} +static DEVICE_ATTR_RW(driver_override); + /* Set up per device attributes in /sys/bus/vmbus/devices/ */ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_id.attr, @@ -528,6 +576,7 @@ static struct attribute *vmbus_dev_attrs[] = { &dev_attr_channel_vp_mapping.attr, &dev_attr_vendor.attr, &dev_attr_device.attr, + &dev_attr_driver_override.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus_dev); @@ -563,17 +612,26 @@ static inline bool is_null_guid(const uuid_le *guid) return true; } -/* - * Return a matching hv_vmbus_device_id pointer. - * If there is no match, return NULL. - */ -static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, - const uuid_le *guid) +static const struct hv_vmbus_device_id * +hv_vmbus_dev_match(const struct hv_vmbus_device_id *id, const uuid_le *guid) + +{ + if (id == NULL) + return NULL; /* empty device table */ + + for (; !is_null_guid(&id->guid); id++) + if (!uuid_le_cmp(id->guid, *guid)) + return id; + + return NULL; +} + +static const struct hv_vmbus_device_id * +hv_vmbus_dynid_match(struct hv_driver *drv, const uuid_le *guid) { const struct hv_vmbus_device_id *id = NULL; struct vmbus_dynid *dynid; - /* Look at the dynamic ids first, before the static ones */ spin_lock(&drv->dynids.lock); list_for_each_entry(dynid, &drv->dynids.list, node) { if (!uuid_le_cmp(dynid->id.guid, *guid)) { @@ -583,18 +641,37 @@ static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, } spin_unlock(&drv->dynids.lock); - if (id) - return id; + return id; +} - id = drv->id_table; - if (id == NULL) - return NULL; /* empty device table */ +static const struct hv_vmbus_device_id vmbus_device_null = { + .guid = NULL_UUID_LE, +}; - for (; !is_null_guid(&id->guid); id++) - if (!uuid_le_cmp(id->guid, *guid)) - return id; +/* + * Return a matching hv_vmbus_device_id pointer. + * If there is no match, return NULL. + */ +static const struct hv_vmbus_device_id *hv_vmbus_get_id(struct hv_driver *drv, + struct hv_device *dev) +{ + const uuid_le *guid = &dev->dev_type; + const struct hv_vmbus_device_id *id; - return NULL; + /* When driver_override is set, only bind to the matching driver */ + if (dev->driver_override && strcmp(dev->driver_override, drv->name)) + return NULL; + + /* Look at the dynamic ids first, before the static ones */ + id = hv_vmbus_dynid_match(drv, guid); + if (!id) + id = hv_vmbus_dev_match(drv->id_table, guid); + + /* driver_override will always match, send a dummy id */ + if (!id && dev->driver_override) + id = &vmbus_device_null; + + return id; } /* vmbus_add_dynid - add a new device ID to this driver and re-probe devices */ @@ -643,7 +720,7 @@ static ssize_t new_id_store(struct device_driver *driver, const char *buf, if (retval) return retval; - if (hv_vmbus_get_id(drv, &guid)) + if (hv_vmbus_dynid_match(drv, &guid)) return -EEXIST; retval = vmbus_add_dynid(drv, &guid); @@ -708,7 +785,7 @@ static int vmbus_match(struct device *device, struct device_driver *driver) if (is_hvsock_channel(hv_dev->channel)) return drv->hvsock; - if (hv_vmbus_get_id(drv, &hv_dev->dev_type)) + if (hv_vmbus_get_id(drv, hv_dev)) return 1; return 0; @@ -725,7 +802,7 @@ static int vmbus_probe(struct device *child_device) struct hv_device *dev = device_to_hv_device(child_device); const struct hv_vmbus_device_id *dev_id; - dev_id = hv_vmbus_get_id(drv, &dev->dev_type); + dev_id = hv_vmbus_get_id(drv, dev); if (drv->probe) { ret = drv->probe(dev, dev_id); if (ret != 0) diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index efda23cf32c7..2c3798bcb01c 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1125,6 +1125,7 @@ struct hv_device { u16 device_id; struct device device; + char *driver_override; /* Driver name to force a match */ struct vmbus_channel *channel; struct kset *channels_kset; -- cgit v1.2.3-58-ga151 From 108ddb8fa1fc310be4a6bd1e308bca62821ee8b5 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Aug 2018 23:06:09 +0000 Subject: uio_hv_generic: increase size of receive and send buffers When using DPDK there is significant performance boost by using the largest possible send and receive buffer area. Unfortunately, with UIO model there is not a good way to configure this at run time. But it is okay to have a bigger buffer available even if application only decides to use a smaller piece of it. Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_hv_generic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index e401be8321ab..9e7d622b4326 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -33,13 +33,13 @@ #include "../hv/hyperv_vmbus.h" -#define DRIVER_VERSION "0.02.0" +#define DRIVER_VERSION "0.02.1" #define DRIVER_AUTHOR "Stephen Hemminger " #define DRIVER_DESC "Generic UIO driver for VMBus devices" #define HV_RING_SIZE 512 /* pages */ -#define SEND_BUFFER_SIZE (15 * 1024 * 1024) -#define RECV_BUFFER_SIZE (15 * 1024 * 1024) +#define SEND_BUFFER_SIZE (16 * 1024 * 1024) +#define RECV_BUFFER_SIZE (31 * 1024 * 1024) /* * List of resources to be mapped to user space -- cgit v1.2.3-58-ga151 From 8e6925631aae550bdaea4c442e8ecbab4a9685d2 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 10 Aug 2018 23:06:10 +0000 Subject: uio_hv_generic: drop #ifdef DEBUG DEBUG is leftover from the development phase, remove it. Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_hv_generic.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 9e7d622b4326..a08860260f55 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -17,7 +17,6 @@ * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \ * > /sys/bus/vmbus/drivers/uio_hv_generic/bind */ -#define DEBUG 1 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include -- cgit v1.2.3-58-ga151 From f25a7ece08bdb1f2b3c4bbeae942682fc3a99dde Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Fri, 10 Aug 2018 23:06:11 +0000 Subject: Drivers: hv: vmbus: Fix synic per-cpu context initialization If hv_synic_alloc() errors out, the state of the per-cpu context for some CPUs is unknown since the zero'ing is done as each CPU is iterated over. In such case, hv_synic_cleanup() may try to free memory based on uninitialized values. Fix this by zero'ing the per-cpu context for all CPUs before doing any memory allocations that might fail. Signed-off-by: Michael Kelley Reported-by: Dan Carpenter Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index 748a1c4172a6..332d7c34be5c 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -189,6 +189,17 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu) int hv_synic_alloc(void) { int cpu; + struct hv_per_cpu_context *hv_cpu; + + /* + * First, zero all per-cpu memory areas so hv_synic_free() can + * detect what memory has been allocated and cleanup properly + * after any failures. + */ + for_each_present_cpu(cpu) { + hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); + memset(hv_cpu, 0, sizeof(*hv_cpu)); + } hv_context.hv_numa_map = kcalloc(nr_node_ids, sizeof(struct cpumask), GFP_KERNEL); @@ -198,10 +209,8 @@ int hv_synic_alloc(void) } for_each_present_cpu(cpu) { - struct hv_per_cpu_context *hv_cpu - = per_cpu_ptr(hv_context.cpu_context, cpu); + hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); - memset(hv_cpu, 0, sizeof(*hv_cpu)); tasklet_init(&hv_cpu->msg_dpc, vmbus_on_msg_dpc, (unsigned long) hv_cpu); -- cgit v1.2.3-58-ga151 From ae61cf5b9913027c6953a79ed3894da4f47061bd Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 16 Aug 2018 09:39:41 +0200 Subject: uio: ensure class is registered before devices When both uio and the uio drivers are built in the kernel, it is possible for a driver to register devices before the uio class is registered. This may result in a NULL pointer dereference later on in get_device_parent() when accessing the class glue_dirs spinlock. The trace looks like that: Unable to handle kernel NULL pointer dereference at virtual address 00000140 [...] [] _raw_spin_lock+0x14/0x48 [] device_add+0x154/0x6a0 [] device_create_groups_vargs+0x120/0x128 [] device_create+0x54/0x60 [] __uio_register_device+0x120/0x4a8 [] jaguar2_pci_probe+0x2d4/0x558 [] local_pci_probe+0x3c/0xb8 [] pci_device_probe+0x11c/0x180 [] driver_probe_device+0x22c/0x2d8 [] __driver_attach+0xbc/0xc0 [] bus_for_each_dev+0x4c/0x98 [] driver_attach+0x20/0x28 [] bus_add_driver+0x1b8/0x228 [] driver_register+0x60/0xf8 [] __pci_register_driver+0x40/0x48 Return EPROBE_DEFER in that case so the driver can register the device later. Signed-off-by: Alexandre Belloni Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 70a7981b94b3..9916edda5271 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -274,6 +274,8 @@ static struct class uio_class = { .dev_groups = uio_groups, }; +bool uio_class_registered; + /* * device functions */ @@ -876,6 +878,9 @@ static int init_uio_class(void) printk(KERN_ERR "class_register failed for uio\n"); goto err_class_register; } + + uio_class_registered = true; + return 0; err_class_register: @@ -886,6 +891,7 @@ exit: static void release_uio_class(void) { + uio_class_registered = false; class_unregister(&uio_class); uio_major_cleanup(); } @@ -912,6 +918,9 @@ int __uio_register_device(struct module *owner, struct uio_device *idev; int ret = 0; + if (!uio_class_registered) + return -EPROBE_DEFER; + if (!parent || !info || !info->name || !info->version) return -EINVAL; -- cgit v1.2.3-58-ga151 From 52ac30592dd6292701863ddbbfbf4c43d7cf706e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 7 Aug 2018 12:09:23 +0100 Subject: misc: apds990x: remove unused array ir_currents Array ir_currents is declared but never used, hence it is redundant and can be removed. Cleans up clang warning: warning: 'ir_currents' defined but not used [-Wunused-const-variable=] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/misc/apds990x.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index ed9412d750b7..24876c615c3c 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -188,7 +188,6 @@ struct apds990x_chip { #define APDS_LUX_DEFAULT_RATE 200 static const u8 again[] = {1, 8, 16, 120}; /* ALS gain steps */ -static const u8 ir_currents[] = {100, 50, 25, 12}; /* IRled currents in mA */ /* Following two tables must match i.e 10Hz rate means 1 as persistence value */ static const u16 arates_hz[] = {10, 5, 2, 1}; -- cgit v1.2.3-58-ga151 From 02b04d23b2cb134d2bf15a0d97bc9c1976da61ab Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 7 Aug 2018 14:20:32 +0100 Subject: misc: bh1770glc: remove unused array prox_curr_ma Array prox_curr_ma is declared but never used, hence it is redundant and can be removed. Cleans up clang warning: warning: 'prox_curr_ma' defined but not used [-Wunused-const-variable=] Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/misc/bh1770glc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 9c62bf064f77..17e81ce9925b 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -180,9 +180,6 @@ static const char reg_vleds[] = "Vleds"; static const s16 prox_rates_hz[] = {100, 50, 33, 25, 14, 10, 5, 2}; static const s16 prox_rates_ms[] = {10, 20, 30, 40, 70, 100, 200, 500}; -/* Supported IR-led currents in mA */ -static const u8 prox_curr_ma[] = {5, 10, 20, 50, 100, 150, 200}; - /* * Supported stand alone rates in ms from chip data sheet * {100, 200, 500, 1000, 2000}; -- cgit v1.2.3-58-ga151 From 0181cfd93c7b09a3fc69ff734e84c49bfe7d9020 Mon Sep 17 00:00:00 2001 From: Parth Y Shah Date: Mon, 6 Aug 2018 12:40:57 +0530 Subject: misc: lkdtm: fixed static variable initialization Resolved "ERROR: do not initialise statics to 0" Signed-off-by: Parth Y Shah Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lkdtm/usercopy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/lkdtm/usercopy.c b/drivers/misc/lkdtm/usercopy.c index 9725aed305bb..d8157b8d78c2 100644 --- a/drivers/misc/lkdtm/usercopy.c +++ b/drivers/misc/lkdtm/usercopy.c @@ -18,7 +18,7 @@ * hardened usercopy checks by added "unconst" to all the const copies, * and making sure "cache_size" isn't optimized into a const. */ -static volatile size_t unconst = 0; +static volatile size_t unconst; static volatile size_t cache_size = 1024; static struct kmem_cache *whitelist_cache; -- cgit v1.2.3-58-ga151 From ca6ac25cecf0e740d7cc8e03e0ebbf8acbeca3df Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 7 Aug 2018 13:19:35 +0100 Subject: nvmem: core: return error code instead of NULL from nvmem_device_get nvmem_device_get() should return ERR_PTR() on error or valid pointer on success, but one of the code path seems to return NULL, so fix it. Reported-by: Niklas Cassel Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index aa1657831b70..6f064002f439 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -679,7 +679,7 @@ static struct nvmem_device *nvmem_find(const char *name) d = bus_find_device_by_name(&nvmem_bus_type, NULL, name); if (!d) - return NULL; + return ERR_PTR(-ENOENT); return to_nvmem_device(d); } -- cgit v1.2.3-58-ga151 From 0952c57c8ff1e4e68de029f64dadd8d82027043b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:48 -0500 Subject: uio: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Greg Kroah-Hartman Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_fsl_elbc_gpcm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c index bbc17effae5e..9cc37fe07d35 100644 --- a/drivers/uio/uio_fsl_elbc_gpcm.c +++ b/drivers/uio/uio_fsl_elbc_gpcm.c @@ -382,8 +382,7 @@ static int uio_fsl_elbc_gpcm_probe(struct platform_device *pdev) } /* set all UIO data */ - if (node->name) - info->mem[0].name = kstrdup(node->name, GFP_KERNEL); + info->mem[0].name = kasprintf(GFP_KERNEL, "%pOFn", node); info->mem[0].addr = res.start; info->mem[0].size = resource_size(&res); info->mem[0].memtype = UIO_MEM_PHYS; -- cgit v1.2.3-58-ga151 From 79c6f4b84b6ee0117b9c11c8f7a108c327277ceb Mon Sep 17 00:00:00 2001 From: Souptick Joarder Date: Sat, 1 Sep 2018 22:28:50 +0530 Subject: uio: convert to vm_fault_t As part of commit 9b85e95a3080 ("uio: Change return type to vm_fault_t") in 4.19-rc1, this conversion was missed. Now converted 'ret' to vm_fault_t type. Signed-off-by: Souptick Joarder Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 9916edda5271..0ffb324aa038 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -670,7 +670,7 @@ static vm_fault_t uio_vma_fault(struct vm_fault *vmf) struct page *page; unsigned long offset; void *addr; - int ret = 0; + vm_fault_t ret = 0; int mi; mutex_lock(&idev->info_lock); -- cgit v1.2.3-58-ga151 From 44b73962cb25f1c8170ea695c4564b05a75e1fd4 Mon Sep 17 00:00:00 2001 From: Sherry Yang Date: Mon, 13 Aug 2018 17:28:53 -0700 Subject: android: binder: no outgoing transaction when thread todo has transaction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a process dies, failed reply is sent to the sender of any transaction queued on a dead thread's todo list. The sender asserts that the received failed reply corresponds to the head of the transaction stack. This assert can fail if the dead thread is allowed to send outgoing transactions when there is already a transaction on its todo list, because this new transaction can end up on the transaction stack of the original sender. The following steps illustrate how this assertion can fail. 1. Thread1 sends txn19 to Thread2 (T1->transaction_stack=txn19, T2->todo+=txn19) 2. Without processing todo list, Thread2 sends txn20 to Thread1 (T1->todo+=txn20, T2->transaction_stack=txn20) 3. T1 processes txn20 on its todo list (T1->transaction_stack=txn20->txn19, T1->todo=) 4. T2 dies, T2->todo cleanup attempts to send failed reply for txn19, but T1->transaction_stack points to txn20 -- assertion failes Step 2. is the incorrect behavior. When there is a transaction on a thread's todo list, this thread should not be able to send any outgoing synchronous transactions. Only the head of the todo list needs to be checked because only threads that are waiting for proc work can directly receive work from another thread, and no work is allowed to be queued on such a thread without waking up the thread. This patch also enforces that a thread is not waiting for proc work when a work is directly enqueued to its todo list. Acked-by: Arve Hjønnevåg Signed-off-by: Sherry Yang Reviewed-by: Martijn Coenen Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/android/binder.c b/drivers/android/binder.c index d58763b6b009..f2081934eb8b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -822,6 +822,7 @@ static void binder_enqueue_deferred_thread_work_ilocked(struct binder_thread *thread, struct binder_work *work) { + WARN_ON(!list_empty(&thread->waiting_thread_node)); binder_enqueue_work_ilocked(work, &thread->todo); } @@ -839,6 +840,7 @@ static void binder_enqueue_thread_work_ilocked(struct binder_thread *thread, struct binder_work *work) { + WARN_ON(!list_empty(&thread->waiting_thread_node)); binder_enqueue_work_ilocked(work, &thread->todo); thread->process_todo = true; } @@ -1270,19 +1272,12 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong, } else node->local_strong_refs++; if (!node->has_strong_ref && target_list) { + struct binder_thread *thread = container_of(target_list, + struct binder_thread, todo); binder_dequeue_work_ilocked(&node->work); - /* - * Note: this function is the only place where we queue - * directly to a thread->todo without using the - * corresponding binder_enqueue_thread_work() helper - * functions; in this case it's ok to not set the - * process_todo flag, since we know this node work will - * always be followed by other work that starts queue - * processing: in case of synchronous transactions, a - * BR_REPLY or BR_ERROR; in case of oneway - * transactions, a BR_TRANSACTION_COMPLETE. - */ - binder_enqueue_work_ilocked(&node->work, target_list); + BUG_ON(&thread->todo != target_list); + binder_enqueue_deferred_thread_work_ilocked(thread, + &node->work); } } else { if (!internal) @@ -2723,6 +2718,7 @@ static void binder_transaction(struct binder_proc *proc, { int ret; struct binder_transaction *t; + struct binder_work *w; struct binder_work *tcomplete; binder_size_t *offp, *off_end, *off_start; binder_size_t off_min; @@ -2864,6 +2860,29 @@ static void binder_transaction(struct binder_proc *proc, goto err_invalid_target_handle; } binder_inner_proc_lock(proc); + + w = list_first_entry_or_null(&thread->todo, + struct binder_work, entry); + if (!(tr->flags & TF_ONE_WAY) && w && + w->type == BINDER_WORK_TRANSACTION) { + /* + * Do not allow new outgoing transaction from a + * thread that has a transaction at the head of + * its todo list. Only need to check the head + * because binder_select_thread_ilocked picks a + * thread from proc->waiting_threads to enqueue + * the transaction, and nothing is queued to the + * todo list while the thread is on waiting_threads. + */ + binder_user_error("%d:%d new transaction not allowed when there is a transaction on thread todo\n", + proc->pid, thread->pid); + binder_inner_proc_unlock(proc); + return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; + goto err_bad_todo_list; + } + if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; @@ -3247,6 +3266,7 @@ err_alloc_tcomplete_failed: kfree(t); binder_stats_deleted(BINDER_STAT_TRANSACTION); err_alloc_t_failed: +err_bad_todo_list: err_bad_call_stack: err_empty_call_stack: err_dead_binder: -- cgit v1.2.3-58-ga151 From 44d8047f1d87adc2fd7eccc88533794f6d88c15e Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Tue, 28 Aug 2018 13:46:25 -0700 Subject: binder: use standard functions to allocate fds Binder uses internal fs interfaces to allocate and install fds: __alloc_fd __fd_install __close_fd get_files_struct put_files_struct These were used to support the passing of fds between processes as part of a transaction. The actual allocation and installation of the fds in the target process was handled by the sending process so the standard functions, alloc_fd() and fd_install() which assume task==current couldn't be used. This patch refactors this mechanism so that the fds are allocated and installed by the target process allowing the standard functions to be used. The sender now creates a list of fd fixups that contains the struct *file and the address to fixup with the new fd once it is allocated. This list is processed by the target process when the transaction is dequeued. A new error case is introduced by this change. If an async transaction with file descriptors cannot allocate new fds in the target (probably due to out of file descriptors), the transaction is discarded with a log message. In the old implementation this would have been detected in the sender context and failed prior to sending. Signed-off-by: Todd Kjos Signed-off-by: Greg Kroah-Hartman --- drivers/android/Kconfig | 2 +- drivers/android/binder.c | 387 ++++++++++++++++++++++++----------------- drivers/android/binder_trace.h | 36 +++- 3 files changed, 260 insertions(+), 165 deletions(-) (limited to 'drivers') diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 432e9ad77070..51e8250d113f 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -10,7 +10,7 @@ if ANDROID config ANDROID_BINDER_IPC bool "Android Binder IPC Driver" - depends on MMU + depends on MMU && !CPU_CACHE_VIVT default n ---help--- Binder is used in Android for both communication between processes, diff --git a/drivers/android/binder.c b/drivers/android/binder.c index f2081934eb8b..5410df2550dd 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -71,6 +71,7 @@ #include #include #include +#include #include @@ -457,9 +458,8 @@ struct binder_ref { }; enum binder_deferred_state { - BINDER_DEFERRED_PUT_FILES = 0x01, - BINDER_DEFERRED_FLUSH = 0x02, - BINDER_DEFERRED_RELEASE = 0x04, + BINDER_DEFERRED_FLUSH = 0x01, + BINDER_DEFERRED_RELEASE = 0x02, }; /** @@ -480,9 +480,6 @@ enum binder_deferred_state { * (invariant after initialized) * @tsk task_struct for group_leader of process * (invariant after initialized) - * @files files_struct for process - * (protected by @files_lock) - * @files_lock mutex to protect @files * @deferred_work_node: element for binder_deferred_list * (protected by binder_deferred_lock) * @deferred_work: bitmap of deferred work to perform @@ -527,8 +524,6 @@ struct binder_proc { struct list_head waiting_threads; int pid; struct task_struct *tsk; - struct files_struct *files; - struct mutex files_lock; struct hlist_node deferred_work_node; int deferred_work; bool is_dead; @@ -611,6 +606,23 @@ struct binder_thread { bool is_dead; }; +/** + * struct binder_txn_fd_fixup - transaction fd fixup list element + * @fixup_entry: list entry + * @file: struct file to be associated with new fd + * @offset: offset in buffer data to this fixup + * + * List element for fd fixups in a transaction. Since file + * descriptors need to be allocated in the context of the + * target process, we pass each fd to be processed in this + * struct. + */ +struct binder_txn_fd_fixup { + struct list_head fixup_entry; + struct file *file; + size_t offset; +}; + struct binder_transaction { int debug_id; struct binder_work work; @@ -628,6 +640,7 @@ struct binder_transaction { long priority; long saved_priority; kuid_t sender_euid; + struct list_head fd_fixups; /** * @lock: protects @from, @to_proc, and @to_thread * @@ -922,66 +935,6 @@ static void binder_free_thread(struct binder_thread *thread); static void binder_free_proc(struct binder_proc *proc); static void binder_inc_node_tmpref_ilocked(struct binder_node *node); -static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) -{ - unsigned long rlim_cur; - unsigned long irqs; - int ret; - - mutex_lock(&proc->files_lock); - if (proc->files == NULL) { - ret = -ESRCH; - goto err; - } - if (!lock_task_sighand(proc->tsk, &irqs)) { - ret = -EMFILE; - goto err; - } - rlim_cur = task_rlimit(proc->tsk, RLIMIT_NOFILE); - unlock_task_sighand(proc->tsk, &irqs); - - ret = __alloc_fd(proc->files, 0, rlim_cur, flags); -err: - mutex_unlock(&proc->files_lock); - return ret; -} - -/* - * copied from fd_install - */ -static void task_fd_install( - struct binder_proc *proc, unsigned int fd, struct file *file) -{ - mutex_lock(&proc->files_lock); - if (proc->files) - __fd_install(proc->files, fd, file); - mutex_unlock(&proc->files_lock); -} - -/* - * copied from sys_close - */ -static long task_close_fd(struct binder_proc *proc, unsigned int fd) -{ - int retval; - - mutex_lock(&proc->files_lock); - if (proc->files == NULL) { - retval = -ESRCH; - goto err; - } - retval = __close_fd(proc->files, fd); - /* can't restart close syscall because file table entry was cleared */ - if (unlikely(retval == -ERESTARTSYS || - retval == -ERESTARTNOINTR || - retval == -ERESTARTNOHAND || - retval == -ERESTART_RESTARTBLOCK)) - retval = -EINTR; -err: - mutex_unlock(&proc->files_lock); - return retval; -} - static bool binder_has_work_ilocked(struct binder_thread *thread, bool do_proc_work) { @@ -1953,10 +1906,32 @@ static struct binder_thread *binder_get_txn_from_and_acq_inner( return NULL; } +/** + * binder_free_txn_fixups() - free unprocessed fd fixups + * @t: binder transaction for t->from + * + * If the transaction is being torn down prior to being + * processed by the target process, free all of the + * fd fixups and fput the file structs. It is safe to + * call this function after the fixups have been + * processed -- in that case, the list will be empty. + */ +static void binder_free_txn_fixups(struct binder_transaction *t) +{ + struct binder_txn_fd_fixup *fixup, *tmp; + + list_for_each_entry_safe(fixup, tmp, &t->fd_fixups, fixup_entry) { + fput(fixup->file); + list_del(&fixup->fixup_entry); + kfree(fixup); + } +} + static void binder_free_transaction(struct binder_transaction *t) { if (t->buffer) t->buffer->transaction = NULL; + binder_free_txn_fixups(t); kfree(t); binder_stats_deleted(BINDER_STAT_TRANSACTION); } @@ -2257,12 +2232,17 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, } break; case BINDER_TYPE_FD: { - struct binder_fd_object *fp = to_binder_fd_object(hdr); - - binder_debug(BINDER_DEBUG_TRANSACTION, - " fd %d\n", fp->fd); - if (failed_at) - task_close_fd(proc, fp->fd); + /* + * No need to close the file here since user-space + * closes it for for successfully delivered + * transactions. For transactions that weren't + * delivered, the new fd was never allocated so + * there is no need to close and the fput on the + * file is done when the transaction is torn + * down. + */ + WARN_ON(failed_at && + proc->tsk == current->group_leader); } break; case BINDER_TYPE_PTR: /* @@ -2278,6 +2258,15 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, size_t fd_index; binder_size_t fd_buf_size; + if (proc->tsk != current->group_leader) { + /* + * Nothing to do if running in sender context + * The fd fixups have not been applied so no + * fds need to be closed. + */ + continue; + } + fda = to_binder_fd_array_object(hdr); parent = binder_validate_ptr(buffer, fda->parent, off_start, @@ -2310,7 +2299,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, } fd_array = (u32 *)(parent_buffer + (uintptr_t)fda->parent_offset); for (fd_index = 0; fd_index < fda->num_fds; fd_index++) - task_close_fd(proc, fd_array[fd_index]); + ksys_close(fd_array[fd_index]); } break; default: pr_err("transaction release %d bad object type %x\n", @@ -2442,17 +2431,18 @@ done: return ret; } -static int binder_translate_fd(int fd, +static int binder_translate_fd(u32 *fdp, struct binder_transaction *t, struct binder_thread *thread, struct binder_transaction *in_reply_to) { struct binder_proc *proc = thread->proc; struct binder_proc *target_proc = t->to_proc; - int target_fd; + struct binder_txn_fd_fixup *fixup; struct file *file; - int ret; + int ret = 0; bool target_allows_fd; + int fd = *fdp; if (in_reply_to) target_allows_fd = !!(in_reply_to->flags & TF_ACCEPT_FDS); @@ -2480,19 +2470,24 @@ static int binder_translate_fd(int fd, goto err_security; } - target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); - if (target_fd < 0) { + /* + * Add fixup record for this transaction. The allocation + * of the fd in the target needs to be done from a + * target thread. + */ + fixup = kzalloc(sizeof(*fixup), GFP_KERNEL); + if (!fixup) { ret = -ENOMEM; - goto err_get_unused_fd; + goto err_alloc; } - task_fd_install(target_proc, target_fd, file); - trace_binder_transaction_fd(t, fd, target_fd); - binder_debug(BINDER_DEBUG_TRANSACTION, " fd %d -> %d\n", - fd, target_fd); + fixup->file = file; + fixup->offset = (uintptr_t)fdp - (uintptr_t)t->buffer->data; + trace_binder_transaction_fd_send(t, fd, fixup->offset); + list_add_tail(&fixup->fixup_entry, &t->fd_fixups); - return target_fd; + return ret; -err_get_unused_fd: +err_alloc: err_security: fput(file); err_fget: @@ -2506,8 +2501,7 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, struct binder_thread *thread, struct binder_transaction *in_reply_to) { - binder_size_t fdi, fd_buf_size, num_installed_fds; - int target_fd; + binder_size_t fdi, fd_buf_size; uintptr_t parent_buffer; u32 *fd_array; struct binder_proc *proc = thread->proc; @@ -2539,23 +2533,12 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, return -EINVAL; } for (fdi = 0; fdi < fda->num_fds; fdi++) { - target_fd = binder_translate_fd(fd_array[fdi], t, thread, + int ret = binder_translate_fd(&fd_array[fdi], t, thread, in_reply_to); - if (target_fd < 0) - goto err_translate_fd_failed; - fd_array[fdi] = target_fd; + if (ret < 0) + return ret; } return 0; - -err_translate_fd_failed: - /* - * Failed to allocate fd or security error, free fds - * installed so far. - */ - num_installed_fds = fdi; - for (fdi = 0; fdi < num_installed_fds; fdi++) - task_close_fd(target_proc, fd_array[fdi]); - return target_fd; } static int binder_fixup_parent(struct binder_transaction *t, @@ -2930,6 +2913,7 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_alloc_t_failed; } + INIT_LIST_HEAD(&t->fd_fixups); binder_stats_created(BINDER_STAT_TRANSACTION); spin_lock_init(&t->lock); @@ -3085,17 +3069,16 @@ static void binder_transaction(struct binder_proc *proc, case BINDER_TYPE_FD: { struct binder_fd_object *fp = to_binder_fd_object(hdr); - int target_fd = binder_translate_fd(fp->fd, t, thread, - in_reply_to); + int ret = binder_translate_fd(&fp->fd, t, thread, + in_reply_to); - if (target_fd < 0) { + if (ret < 0) { return_error = BR_FAILED_REPLY; - return_error_param = target_fd; + return_error_param = ret; return_error_line = __LINE__; goto err_translate_failed; } fp->pad_binder = 0; - fp->fd = target_fd; } break; case BINDER_TYPE_FDA: { struct binder_fd_array_object *fda = @@ -3252,6 +3235,7 @@ err_bad_object_type: err_bad_offset: err_bad_parent: err_copy_data_failed: + binder_free_txn_fixups(t); trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); if (target_node) @@ -3314,6 +3298,47 @@ err_invalid_target_handle: } } +/** + * binder_free_buf() - free the specified buffer + * @proc: binder proc that owns buffer + * @buffer: buffer to be freed + * + * If buffer for an async transaction, enqueue the next async + * transaction from the node. + * + * Cleanup buffer and free it. + */ +void +binder_free_buf(struct binder_proc *proc, struct binder_buffer *buffer) +{ + if (buffer->transaction) { + buffer->transaction->buffer = NULL; + buffer->transaction = NULL; + } + if (buffer->async_transaction && buffer->target_node) { + struct binder_node *buf_node; + struct binder_work *w; + + buf_node = buffer->target_node; + binder_node_inner_lock(buf_node); + BUG_ON(!buf_node->has_async_transaction); + BUG_ON(buf_node->proc != proc); + w = binder_dequeue_work_head_ilocked( + &buf_node->async_todo); + if (!w) { + buf_node->has_async_transaction = false; + } else { + binder_enqueue_work_ilocked( + w, &proc->todo); + binder_wakeup_proc_ilocked(proc); + } + binder_node_inner_unlock(buf_node); + } + trace_binder_transaction_buffer_release(buffer); + binder_transaction_buffer_release(proc, buffer, NULL); + binder_alloc_free_buf(&proc->alloc, buffer); +} + static int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, @@ -3500,33 +3525,7 @@ static int binder_thread_write(struct binder_proc *proc, proc->pid, thread->pid, (u64)data_ptr, buffer->debug_id, buffer->transaction ? "active" : "finished"); - - if (buffer->transaction) { - buffer->transaction->buffer = NULL; - buffer->transaction = NULL; - } - if (buffer->async_transaction && buffer->target_node) { - struct binder_node *buf_node; - struct binder_work *w; - - buf_node = buffer->target_node; - binder_node_inner_lock(buf_node); - BUG_ON(!buf_node->has_async_transaction); - BUG_ON(buf_node->proc != proc); - w = binder_dequeue_work_head_ilocked( - &buf_node->async_todo); - if (!w) { - buf_node->has_async_transaction = false; - } else { - binder_enqueue_work_ilocked( - w, &proc->todo); - binder_wakeup_proc_ilocked(proc); - } - binder_node_inner_unlock(buf_node); - } - trace_binder_transaction_buffer_release(buffer); - binder_transaction_buffer_release(proc, buffer, NULL); - binder_alloc_free_buf(&proc->alloc, buffer); + binder_free_buf(proc, buffer); break; } @@ -3849,6 +3848,76 @@ static int binder_wait_for_work(struct binder_thread *thread, return ret; } +/** + * binder_apply_fd_fixups() - finish fd translation + * @t: binder transaction with list of fd fixups + * + * Now that we are in the context of the transaction target + * process, we can allocate and install fds. Process the + * list of fds to translate and fixup the buffer with the + * new fds. + * + * If we fail to allocate an fd, then free the resources by + * fput'ing files that have not been processed and ksys_close'ing + * any fds that have already been allocated. + */ +static int binder_apply_fd_fixups(struct binder_transaction *t) +{ + struct binder_txn_fd_fixup *fixup, *tmp; + int ret = 0; + + list_for_each_entry(fixup, &t->fd_fixups, fixup_entry) { + int fd = get_unused_fd_flags(O_CLOEXEC); + u32 *fdp; + + if (fd < 0) { + binder_debug(BINDER_DEBUG_TRANSACTION, + "failed fd fixup txn %d fd %d\n", + t->debug_id, fd); + ret = -ENOMEM; + break; + } + binder_debug(BINDER_DEBUG_TRANSACTION, + "fd fixup txn %d fd %d\n", + t->debug_id, fd); + trace_binder_transaction_fd_recv(t, fd, fixup->offset); + fd_install(fd, fixup->file); + fixup->file = NULL; + fdp = (u32 *)(t->buffer->data + fixup->offset); + /* + * This store can cause problems for CPUs with a + * VIVT cache (eg ARMv5) since the cache cannot + * detect virtual aliases to the same physical cacheline. + * To support VIVT, this address and the user-space VA + * would both need to be flushed. Since this kernel + * VA is not constructed via page_to_virt(), we can't + * use flush_dcache_page() on it, so we'd have to use + * an internal function. If devices with VIVT ever + * need to run Android, we'll either need to go back + * to patching the translated fd from the sender side + * (using the non-standard kernel functions), or rework + * how the kernel uses the buffer to use page_to_virt() + * addresses instead of allocating in our own vm area. + * + * For now, we disable compilation if CONFIG_CPU_CACHE_VIVT. + */ + *fdp = fd; + } + list_for_each_entry_safe(fixup, tmp, &t->fd_fixups, fixup_entry) { + if (fixup->file) { + fput(fixup->file); + } else if (ret) { + u32 *fdp = (u32 *)(t->buffer->data + fixup->offset); + + ksys_close(*fdp); + } + list_del(&fixup->fixup_entry); + kfree(fixup); + } + + return ret; +} + static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, @@ -4130,6 +4199,34 @@ retry: tr.sender_pid = 0; } + ret = binder_apply_fd_fixups(t); + if (ret) { + struct binder_buffer *buffer = t->buffer; + bool oneway = !!(t->flags & TF_ONE_WAY); + int tid = t->debug_id; + + if (t_from) + binder_thread_dec_tmpref(t_from); + buffer->transaction = NULL; + binder_cleanup_transaction(t, "fd fixups failed", + BR_FAILED_REPLY); + binder_free_buf(proc, buffer); + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "%d:%d %stransaction %d fd fixups failed %d/%d, line %d\n", + proc->pid, thread->pid, + oneway ? "async " : + (cmd == BR_REPLY ? "reply " : ""), + tid, BR_FAILED_REPLY, ret, __LINE__); + if (cmd == BR_REPLY) { + cmd = BR_FAILED_REPLY; + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + binder_stat_br(proc, thread, cmd); + break; + } + continue; + } tr.data_size = t->buffer->data_size; tr.offsets_size = t->buffer->offsets_size; tr.data.ptr.buffer = (binder_uintptr_t) @@ -4713,7 +4810,6 @@ static void binder_vma_close(struct vm_area_struct *vma) (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); binder_alloc_vma_close(&proc->alloc); - binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } static vm_fault_t binder_vm_fault(struct vm_fault *vmf) @@ -4759,9 +4855,6 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) ret = binder_alloc_mmap_handler(&proc->alloc, vma); if (ret) return ret; - mutex_lock(&proc->files_lock); - proc->files = get_files_struct(current); - mutex_unlock(&proc->files_lock); return 0; err_bad_arg: @@ -4785,7 +4878,6 @@ static int binder_open(struct inode *nodp, struct file *filp) spin_lock_init(&proc->outer_lock); get_task_struct(current->group_leader); proc->tsk = current->group_leader; - mutex_init(&proc->files_lock); INIT_LIST_HEAD(&proc->todo); proc->default_priority = task_nice(current); binder_dev = container_of(filp->private_data, struct binder_device, @@ -4935,8 +5027,6 @@ static void binder_deferred_release(struct binder_proc *proc) struct rb_node *n; int threads, nodes, incoming_refs, outgoing_refs, active_transactions; - BUG_ON(proc->files); - mutex_lock(&binder_procs_lock); hlist_del(&proc->proc_node); mutex_unlock(&binder_procs_lock); @@ -5018,7 +5108,6 @@ static void binder_deferred_release(struct binder_proc *proc) static void binder_deferred_func(struct work_struct *work) { struct binder_proc *proc; - struct files_struct *files; int defer; @@ -5036,23 +5125,11 @@ static void binder_deferred_func(struct work_struct *work) } mutex_unlock(&binder_deferred_lock); - files = NULL; - if (defer & BINDER_DEFERRED_PUT_FILES) { - mutex_lock(&proc->files_lock); - files = proc->files; - if (files) - proc->files = NULL; - mutex_unlock(&proc->files_lock); - } - if (defer & BINDER_DEFERRED_FLUSH) binder_deferred_flush(proc); if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - - if (files) - put_files_struct(files); } while (proc); } static DECLARE_WORK(binder_deferred_work, binder_deferred_func); diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 588eb3ec3507..14de7ac57a34 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -223,22 +223,40 @@ TRACE_EVENT(binder_transaction_ref_to_ref, __entry->dest_ref_debug_id, __entry->dest_ref_desc) ); -TRACE_EVENT(binder_transaction_fd, - TP_PROTO(struct binder_transaction *t, int src_fd, int dest_fd), - TP_ARGS(t, src_fd, dest_fd), +TRACE_EVENT(binder_transaction_fd_send, + TP_PROTO(struct binder_transaction *t, int fd, size_t offset), + TP_ARGS(t, fd, offset), TP_STRUCT__entry( __field(int, debug_id) - __field(int, src_fd) - __field(int, dest_fd) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t->debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d src_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + +TRACE_EVENT(binder_transaction_fd_recv, + TP_PROTO(struct binder_transaction *t, int fd, size_t offset), + TP_ARGS(t, fd, offset), + + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) ), TP_fast_assign( __entry->debug_id = t->debug_id; - __entry->src_fd = src_fd; - __entry->dest_fd = dest_fd; + __entry->fd = fd; + __entry->offset = offset; ), - TP_printk("transaction=%d src_fd=%d ==> dest_fd=%d", - __entry->debug_id, __entry->src_fd, __entry->dest_fd) + TP_printk("transaction=%d dest_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) ); DECLARE_EVENT_CLASS(binder_buffer_class, -- cgit v1.2.3-58-ga151 From 6b6642dadd685af885367d6e30f18553e2a23b22 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Fri, 7 Sep 2018 10:01:46 +0200 Subject: android: binder: use kstrdup instead of open-coding it Signed-off-by: Rasmus Villemoes Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 5410df2550dd..46dad7d724ac 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -5764,12 +5764,11 @@ static int __init binder_init(void) * Copy the module_parameter string, because we don't want to * tokenize it in-place. */ - device_names = kzalloc(strlen(binder_devices_param) + 1, GFP_KERNEL); + device_names = kstrdup(binder_devices_param, GFP_KERNEL); if (!device_names) { ret = -ENOMEM; goto err_alloc_device_names_failed; } - strcpy(device_names, binder_devices_param); device_tmp = device_names; while ((device_name = strsep(&device_tmp, ","))) { -- cgit v1.2.3-58-ga151 From b7e6a8961b5d6dd3fc535970e65d497d868bb49f Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Fri, 7 Sep 2018 15:38:37 +0200 Subject: binder: Add BINDER_GET_NODE_INFO_FOR_REF ioctl. This allows the context manager to retrieve information about nodes that it holds a reference to, such as the current number of references to those nodes. Such information can for example be used to determine whether the servicemanager is the only process holding a reference to a node. This information can then be passed on to the process holding the node, which can in turn decide whether it wants to shut down to reduce resource usage. Signed-off-by: Martijn Coenen Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 55 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/android/binder.h | 10 +++++++ 2 files changed, 65 insertions(+) (limited to 'drivers') diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 46dad7d724ac..1b54bb57a9fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -4661,6 +4661,42 @@ out: return ret; } +static int binder_ioctl_get_node_info_for_ref(struct binder_proc *proc, + struct binder_node_info_for_ref *info) +{ + struct binder_node *node; + struct binder_context *context = proc->context; + __u32 handle = info->handle; + + if (info->strong_count || info->weak_count || info->reserved1 || + info->reserved2 || info->reserved3) { + binder_user_error("%d BINDER_GET_NODE_INFO_FOR_REF: only handle may be non-zero.", + proc->pid); + return -EINVAL; + } + + /* This ioctl may only be used by the context manager */ + mutex_lock(&context->context_mgr_node_lock); + if (!context->binder_context_mgr_node || + context->binder_context_mgr_node->proc != proc) { + mutex_unlock(&context->context_mgr_node_lock); + return -EPERM; + } + mutex_unlock(&context->context_mgr_node_lock); + + node = binder_get_node_from_ref(proc, handle, true, NULL); + if (!node) + return -EINVAL; + + info->strong_count = node->local_strong_refs + + node->internal_strong_refs; + info->weak_count = node->local_weak_refs; + + binder_put_node(node); + + return 0; +} + static int binder_ioctl_get_node_debug_info(struct binder_proc *proc, struct binder_node_debug_info *info) { @@ -4755,6 +4791,25 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) } break; } + case BINDER_GET_NODE_INFO_FOR_REF: { + struct binder_node_info_for_ref info; + + if (copy_from_user(&info, ubuf, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + ret = binder_ioctl_get_node_info_for_ref(proc, &info); + if (ret < 0) + goto err; + + if (copy_to_user(ubuf, &info, sizeof(info))) { + ret = -EFAULT; + goto err; + } + + break; + } case BINDER_GET_NODE_DEBUG_INFO: { struct binder_node_debug_info info; diff --git a/include/uapi/linux/android/binder.h b/include/uapi/linux/android/binder.h index bfaec6903b8b..b9ba520f7e4b 100644 --- a/include/uapi/linux/android/binder.h +++ b/include/uapi/linux/android/binder.h @@ -200,6 +200,15 @@ struct binder_node_debug_info { __u32 has_weak_ref; }; +struct binder_node_info_for_ref { + __u32 handle; + __u32 strong_count; + __u32 weak_count; + __u32 reserved1; + __u32 reserved2; + __u32 reserved3; +}; + #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) @@ -208,6 +217,7 @@ struct binder_node_debug_info { #define BINDER_THREAD_EXIT _IOW('b', 8, __s32) #define BINDER_VERSION _IOWR('b', 9, struct binder_version) #define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info) +#define BINDER_GET_NODE_INFO_FOR_REF _IOWR('b', 12, struct binder_node_info_for_ref) /* * NOTE: Two special error codes you should check for when calling -- cgit v1.2.3-58-ga151 From a71bcc1b09497d5cb7f24a7a6ce74d8ebd99d457 Mon Sep 17 00:00:00 2001 From: Ding Xiang Date: Thu, 6 Sep 2018 16:51:10 +0800 Subject: vme: remove unneeded kfree put_device will call vme_dev_release to free vdev, kfree is unnecessary here. Signed-off-by: Ding Xiang Signed-off-by: Greg Kroah-Hartman --- drivers/vme/vme.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/vme/vme.c b/drivers/vme/vme.c index 92500f6bdad1..520a5f9c27de 100644 --- a/drivers/vme/vme.c +++ b/drivers/vme/vme.c @@ -1890,7 +1890,6 @@ static int __vme_register_driver_bus(struct vme_driver *drv, err_reg: put_device(&vdev->dev); - kfree(vdev); err_devalloc: list_for_each_entry_safe(vdev, tmp, &drv->devices, drv_list) { list_del(&vdev->drv_list); -- cgit v1.2.3-58-ga151 From 162aa53b1840612b231b992776acb50528ab2de4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 3 Sep 2018 09:34:02 +0200 Subject: firmware: google: make structure gsmi_dev static The structure gsmi_dev is local to the source and does not need to be in global scope, so make it static. Cleans up sparse warning: symbol 'gsmi_dev' was not declared. Should it be static? Signed-off-by: Colin Ian King Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/gsmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index c8f169bf2e27..734146eec1b9 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -84,7 +84,7 @@ struct gsmi_buf { u32 address; /* physical address of buffer */ }; -struct gsmi_device { +static struct gsmi_device { struct platform_device *pdev; /* platform device */ struct gsmi_buf *name_buf; /* variable name buffer */ struct gsmi_buf *data_buf; /* generic data buffer */ -- cgit v1.2.3-58-ga151 From aa2eb86060f4473aa22d70164478bc14ce4bb8f8 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:32 -0500 Subject: misc: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Frederic Barrat Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Rob Herring Acked-by: Andrew Donnellan Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/flash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/flash.c b/drivers/misc/cxl/flash.c index 43917898fb9a..4d6836f19489 100644 --- a/drivers/misc/cxl/flash.c +++ b/drivers/misc/cxl/flash.c @@ -92,8 +92,8 @@ static int update_property(struct device_node *dn, const char *name, val = (u32 *)new_prop->value; rc = cxl_update_properties(dn, new_prop); - pr_devel("%s: update property (%s, length: %i, value: %#x)\n", - dn->name, name, vd, be32_to_cpu(*val)); + pr_devel("%pOFn: update property (%s, length: %i, value: %#x)\n", + dn, name, vd, be32_to_cpu(*val)); if (rc) { kfree(new_prop->name); -- cgit v1.2.3-58-ga151 From ed824215658cf2e5fb670e40b26bf99f7210d856 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:22:56 -0700 Subject: platform: goldfish: pipe: Fix comments to fit 80 columns Some comment lines are longer than 80 symbols. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 2da567540c2d..6094ca414d9a 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -85,7 +85,10 @@ enum PipePollFlags { PIPE_POLL_HUP = 1 << 2 }; -/* Possible status values used to signal errors - see goldfish_pipe_error_convert */ +/* + * Possible status values used to signal errors + * see: goldfish_pipe_error_convert + */ enum PipeErrors { PIPE_ERROR_INVAL = -1, PIPE_ERROR_AGAIN = -2, @@ -150,9 +153,9 @@ struct goldfish_pipe_command; /* A per-pipe command structure, shared with the host */ struct goldfish_pipe_command { - s32 cmd; /* PipeCmdCode, guest -> host */ - s32 id; /* pipe id, guest -> host */ - s32 status; /* command execution status, host -> guest */ + s32 cmd; /* PipeCmdCode, guest -> host */ + s32 id; /* pipe id, guest -> host */ + s32 status; /* command execution status, host -> guest */ s32 reserved; /* to pad to 64-bit boundary */ union { /* Parameters for PIPE_CMD_{READ,WRITE} */ -- cgit v1.2.3-58-ga151 From c3c4e307dbb686fb92901649e0241d8aa384ad03 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:22:57 -0700 Subject: platform: goldfish: pipe: Update license goldfish_pipe is distributed under GPL v2. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 6094ca414d9a..d2f0582f445e 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2012 Intel, Inc. * Copyright (C) 2013 Intel, Inc. @@ -984,4 +985,4 @@ static struct platform_driver goldfish_pipe_driver = { module_platform_driver(goldfish_pipe_driver); MODULE_AUTHOR("David Turner "); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 53bdf6686ef49c72f7dc3bb3dc2b3817425aa599 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:22:58 -0700 Subject: platform: goldfish: pipe: Move an opening brace to the next line checkpatch: Function's opening brace has to be at the beginning of the next line. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index d2f0582f445e..4e7e100dc7a0 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -588,7 +588,8 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, } static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev, - struct goldfish_pipe *pipe) { + struct goldfish_pipe *pipe) +{ if (pipe->prev_signalled) pipe->prev_signalled->next_signalled = pipe->next_signalled; if (pipe->next_signalled) -- cgit v1.2.3-58-ga151 From 95577010a45c70356602dbe0d2e8860ba9c0851d Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:22:59 -0700 Subject: platform: goldfish: pipe: Separate the host interface to a separate header These are several enums that must kept in sync with the host side. This change explicitly separates them into a dedicated header file. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 69 +-------------- drivers/platform/goldfish/goldfish_pipe_qemu.h | 112 +++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 68 deletions(-) create mode 100644 drivers/platform/goldfish/goldfish_pipe_qemu.h (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 4e7e100dc7a0..caf514aafb21 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -64,6 +64,7 @@ #include #include #include +#include "goldfish_pipe_qemu.h" /* * Update this when something changes in the driver's behavior so the host @@ -74,74 +75,6 @@ enum { PIPE_CURRENT_DEVICE_VERSION = 2 }; -/* - * IMPORTANT: The following constants must match the ones used and defined - * in external/qemu/hw/goldfish_pipe.c in the Android source tree. - */ - -/* List of bitflags returned in status of CMD_POLL command */ -enum PipePollFlags { - PIPE_POLL_IN = 1 << 0, - PIPE_POLL_OUT = 1 << 1, - PIPE_POLL_HUP = 1 << 2 -}; - -/* - * Possible status values used to signal errors - * see: goldfish_pipe_error_convert - */ -enum PipeErrors { - PIPE_ERROR_INVAL = -1, - PIPE_ERROR_AGAIN = -2, - PIPE_ERROR_NOMEM = -3, - PIPE_ERROR_IO = -4 -}; - -/* Bit-flags used to signal events from the emulator */ -enum PipeWakeFlags { - PIPE_WAKE_CLOSED = 1 << 0, /* emulator closed pipe */ - PIPE_WAKE_READ = 1 << 1, /* pipe can now be read from */ - PIPE_WAKE_WRITE = 1 << 2 /* pipe can now be written to */ -}; - -/* Bit flags for the 'flags' field */ -enum PipeFlagsBits { - BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */ - BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */ - BIT_WAKE_ON_READ = 2, /* want to be woken on reads */ -}; - -enum PipeRegs { - PIPE_REG_CMD = 0, - - PIPE_REG_SIGNAL_BUFFER_HIGH = 4, - PIPE_REG_SIGNAL_BUFFER = 8, - PIPE_REG_SIGNAL_BUFFER_COUNT = 12, - - PIPE_REG_OPEN_BUFFER_HIGH = 20, - PIPE_REG_OPEN_BUFFER = 24, - - PIPE_REG_VERSION = 36, - - PIPE_REG_GET_SIGNALLED = 48, -}; - -enum PipeCmdCode { - PIPE_CMD_OPEN = 1, /* to be used by the pipe device itself */ - PIPE_CMD_CLOSE, - PIPE_CMD_POLL, - PIPE_CMD_WRITE, - PIPE_CMD_WAKE_ON_WRITE, - PIPE_CMD_READ, - PIPE_CMD_WAKE_ON_READ, - - /* - * TODO(zyy): implement a deferred read/write execution to allow - * parallel processing of pipe operations on the host. - */ - PIPE_CMD_WAKE_ON_DONE_IO, -}; - enum { MAX_BUFFERS_PER_COMMAND = 336, MAX_SIGNALLED_PIPES = 64, diff --git a/drivers/platform/goldfish/goldfish_pipe_qemu.h b/drivers/platform/goldfish/goldfish_pipe_qemu.h new file mode 100644 index 000000000000..c272e11c5433 --- /dev/null +++ b/drivers/platform/goldfish/goldfish_pipe_qemu.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* + * IMPORTANT: The following constants must match the ones used and defined in + * external/qemu/include/hw/misc/goldfish_pipe.h + */ + +#ifndef GOLDFISH_PIPE_QEMU_H +#define GOLDFISH_PIPE_QEMU_H + +/* List of bitflags returned in status of CMD_POLL command */ +enum PipePollFlags { + PIPE_POLL_IN = 1 << 0, + PIPE_POLL_OUT = 1 << 1, + PIPE_POLL_HUP = 1 << 2 +}; + +/* Possible status values used to signal errors */ +enum PipeErrors { + PIPE_ERROR_INVAL = -1, + PIPE_ERROR_AGAIN = -2, + PIPE_ERROR_NOMEM = -3, + PIPE_ERROR_IO = -4 +}; + +/* Bit-flags used to signal events from the emulator */ +enum PipeWakeFlags { + /* emulator closed pipe */ + PIPE_WAKE_CLOSED = 1 << 0, + + /* pipe can now be read from */ + PIPE_WAKE_READ = 1 << 1, + + /* pipe can now be written to */ + PIPE_WAKE_WRITE = 1 << 2, + + /* unlock this pipe's DMA buffer */ + PIPE_WAKE_UNLOCK_DMA = 1 << 3, + + /* unlock DMA buffer of the pipe shared to this pipe */ + PIPE_WAKE_UNLOCK_DMA_SHARED = 1 << 4, +}; + +/* Possible pipe closing reasons */ +enum PipeCloseReason { + /* guest sent a close command */ + PIPE_CLOSE_GRACEFUL = 0, + + /* guest rebooted, we're closing the pipes */ + PIPE_CLOSE_REBOOT = 1, + + /* close old pipes on snapshot load */ + PIPE_CLOSE_LOAD_SNAPSHOT = 2, + + /* some unrecoverable error on the pipe */ + PIPE_CLOSE_ERROR = 3, +}; + +/* Bit flags for the 'flags' field */ +enum PipeFlagsBits { + BIT_CLOSED_ON_HOST = 0, /* pipe closed by host */ + BIT_WAKE_ON_WRITE = 1, /* want to be woken on writes */ + BIT_WAKE_ON_READ = 2, /* want to be woken on reads */ +}; + +enum PipeRegs { + PIPE_REG_CMD = 0, + + PIPE_REG_SIGNAL_BUFFER_HIGH = 4, + PIPE_REG_SIGNAL_BUFFER = 8, + PIPE_REG_SIGNAL_BUFFER_COUNT = 12, + + PIPE_REG_OPEN_BUFFER_HIGH = 20, + PIPE_REG_OPEN_BUFFER = 24, + + PIPE_REG_VERSION = 36, + + PIPE_REG_GET_SIGNALLED = 48, +}; + +enum PipeCmdCode { + /* to be used by the pipe device itself */ + PIPE_CMD_OPEN = 1, + + PIPE_CMD_CLOSE, + PIPE_CMD_POLL, + PIPE_CMD_WRITE, + PIPE_CMD_WAKE_ON_WRITE, + PIPE_CMD_READ, + PIPE_CMD_WAKE_ON_READ, + + /* + * TODO(zyy): implement a deferred read/write execution to allow + * parallel processing of pipe operations on the host. + */ + PIPE_CMD_WAKE_ON_DONE_IO, +}; + +#endif /* GOLDFISH_PIPE_QEMU_H */ -- cgit v1.2.3-58-ga151 From 84ae527aa58c0f790233fec287d3a060a7501baa Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:00 -0700 Subject: platform: goldfish: pipe: Update the comment for GFP_ATOMIC Provide an explanation why GFP_ATOMIC is needed to prevent changing it to other values. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index caf514aafb21..0c55e657da5a 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -640,7 +640,10 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) return id; { - /* Reallocate the array */ + /* Reallocate the array. + * Since get_free_pipe_id_locked runs with interrupts disabled, + * we don't want to make calls that could lead to sleep. + */ u32 new_capacity = 2 * dev->pipes_capacity; struct goldfish_pipe **pipes = kcalloc(new_capacity, sizeof(*pipes), GFP_ATOMIC); -- cgit v1.2.3-58-ga151 From d23069a5a53b652e3802be039830b69dd17e1a3d Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:01 -0700 Subject: platform: goldfish: pipe: Fail compilation if structs are too large Since the driver provides no workaround prevent in cases if structs do no fit into a memory page, it is better to fail complation to find about the issue earlt instead of returning errors at runtime. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 0c55e657da5a..24e40deb98cc 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -64,6 +64,7 @@ #include #include #include +#include #include "goldfish_pipe_qemu.h" /* @@ -689,6 +690,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) * Command buffer needs to be allocated on its own page to make sure * it is physically contiguous in host's address space. */ + BUILD_BUG_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE); pipe->command_buffer = (struct goldfish_pipe_command *)__get_free_page(GFP_KERNEL); if (!pipe->command_buffer) { @@ -798,9 +800,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) * needs to be contained in a single physical page. The easiest choice * is to just allocate a page and place the buffers in it. */ - if (WARN_ON(sizeof(*dev->buffers) > PAGE_SIZE)) - return -ENOMEM; - + BUILD_BUG_ON(sizeof(struct goldfish_pipe_dev_buffers) > PAGE_SIZE); page = (char *)__get_free_page(GFP_KERNEL); if (!page) { kfree(dev->pipes); @@ -843,9 +843,6 @@ static int goldfish_pipe_probe(struct platform_device *pdev) struct resource *r; struct goldfish_pipe_dev *dev = pipe_dev; - if (WARN_ON(sizeof(struct goldfish_pipe_command) > PAGE_SIZE)) - return -ENOMEM; - /* not thread safe, but this should not happen */ WARN_ON(dev->base != NULL); -- cgit v1.2.3-58-ga151 From 869fd5023a719a8d2af602ce95afba98751c33fc Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:02 -0700 Subject: platform: goldfish: pipe: Replace an array of 1 with a variable There is no reason to have an array of 1. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 24e40deb98cc..e9e3e791c0d4 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -205,7 +205,7 @@ struct goldfish_pipe_dev { unsigned char __iomem *base; }; -static struct goldfish_pipe_dev pipe_dev[1] = {}; +struct goldfish_pipe_dev goldfish_pipe_dev; static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) { @@ -564,12 +564,12 @@ static struct goldfish_pipe *signalled_pipes_pop_front( static void goldfish_interrupt_task(unsigned long unused) { - struct goldfish_pipe_dev *dev = pipe_dev; /* Iterate over the signalled pipes and wake them one by one */ struct goldfish_pipe *pipe; int wakes; - while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) { + while ((pipe = signalled_pipes_pop_front(&goldfish_pipe_dev, &wakes)) != + NULL) { if (wakes & PIPE_WAKE_CLOSED) { pipe->flags = 1 << BIT_CLOSED_ON_HOST; } else { @@ -607,7 +607,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) unsigned long flags; struct goldfish_pipe_dev *dev = dev_id; - if (dev != pipe_dev) + if (dev != &goldfish_pipe_dev) return IRQ_NONE; /* Request the signalled pipes from the device */ @@ -672,7 +672,7 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) */ static int goldfish_pipe_open(struct inode *inode, struct file *file) { - struct goldfish_pipe_dev *dev = pipe_dev; + struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; unsigned long flags; int id; int status; @@ -763,7 +763,7 @@ static const struct file_operations goldfish_pipe_fops = { .release = goldfish_pipe_release, }; -static struct miscdevice goldfish_pipe_dev = { +static struct miscdevice goldfish_pipe_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "goldfish_pipe", .fops = &goldfish_pipe_fops, @@ -771,8 +771,8 @@ static struct miscdevice goldfish_pipe_dev = { static int goldfish_pipe_device_init(struct platform_device *pdev) { + struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; char *page; - struct goldfish_pipe_dev *dev = pipe_dev; int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt, IRQF_SHARED, "goldfish_pipe", dev); @@ -781,7 +781,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) return err; } - err = misc_register(&goldfish_pipe_dev); + err = misc_register(&goldfish_pipe_miscdev); if (err) { dev_err(&pdev->dev, "unable to register v2 device\n"); return err; @@ -830,18 +830,16 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) static void goldfish_pipe_device_deinit(struct platform_device *pdev) { - struct goldfish_pipe_dev *dev = pipe_dev; - - misc_deregister(&goldfish_pipe_dev); - kfree(dev->pipes); - free_page((unsigned long)dev->buffers); + misc_deregister(&goldfish_pipe_miscdev); + kfree(goldfish_pipe_dev.pipes); + free_page((unsigned long)goldfish_pipe_dev.buffers); } static int goldfish_pipe_probe(struct platform_device *pdev) { int err; struct resource *r; - struct goldfish_pipe_dev *dev = pipe_dev; + struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; /* not thread safe, but this should not happen */ WARN_ON(dev->base != NULL); @@ -889,7 +887,7 @@ error: static int goldfish_pipe_remove(struct platform_device *pdev) { - struct goldfish_pipe_dev *dev = pipe_dev; + struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; goldfish_pipe_device_deinit(pdev); dev->base = NULL; return 0; -- cgit v1.2.3-58-ga151 From 25b97d57065ef5ee8554bf0c7c76e7f3fe6b974c Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:03 -0700 Subject: platform: goldfish: pipe: Replace pr_ with dev_ for logging dev_ is preferred if struct device is available. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index e9e3e791c0d4..a61cd444e8ff 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -199,6 +199,9 @@ struct goldfish_pipe_dev { /* Head of a doubly linked list of signalled pipes */ struct goldfish_pipe *first_signalled_pipe; + /* ptr to platform device's device struct */ + struct device *pdev_dev; + /* Some device-specific data */ int irq; int version; @@ -434,7 +437,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, * err. */ if (status != PIPE_ERROR_AGAIN) - pr_info_ratelimited("goldfish_pipe: backend error %d on %s\n", + dev_err_ratelimited(pipe->dev->pdev_dev, + "backend error %d on %s\n", status, is_write ? "write" : "read"); break; } @@ -787,6 +791,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) return err; } + dev->pdev_dev = &pdev->dev; dev->first_signalled_pipe = NULL; dev->pipes_capacity = INITIAL_PIPES_CAPACITY; dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), -- cgit v1.2.3-58-ga151 From 46928cc6ff6d3734eda0194f4dce840989d4f19d Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:04 -0700 Subject: platform: goldfish: pipe: Add blank lines to separate struct members To improve readability and to be consistent with other struct members. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index a61cd444e8ff..62422a4282f5 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -130,11 +130,13 @@ struct goldfish_pipe_dev_buffers { struct goldfish_pipe { /* pipe ID - index into goldfish_pipe_dev::pipes array */ u32 id; + /* The wake flags pipe is waiting for * Note: not protected with any lock, uses atomic operations * and barriers to make it thread-safe. */ unsigned long flags; + /* wake flags host have signalled, * - protected by goldfish_pipe_dev::lock */ @@ -158,6 +160,7 @@ struct goldfish_pipe { /* A wake queue for sleeping until host signals an event */ wait_queue_head_t wake_queue; + /* Pointer to the parent goldfish_pipe_dev instance */ struct goldfish_pipe_dev *dev; }; -- cgit v1.2.3-58-ga151 From 2ed43e53e35907456389dc9d8733d2221e6ec0e7 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:05 -0700 Subject: platform: goldfish: pipe: Remove a redundant variable The variable was not very useful. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 62422a4282f5..3d28a9be5722 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -779,7 +779,6 @@ static struct miscdevice goldfish_pipe_miscdev = { static int goldfish_pipe_device_init(struct platform_device *pdev) { struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; - char *page; int err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt, IRQF_SHARED, "goldfish_pipe", dev); @@ -809,12 +808,12 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) * is to just allocate a page and place the buffers in it. */ BUILD_BUG_ON(sizeof(struct goldfish_pipe_dev_buffers) > PAGE_SIZE); - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) { + dev->buffers = (struct goldfish_pipe_dev_buffers *) + __get_free_page(GFP_KERNEL); + if (!dev->buffers) { kfree(dev->pipes); return -ENOMEM; } - dev->buffers = (struct goldfish_pipe_dev_buffers *)page; /* Send the buffer addresses to the host */ { -- cgit v1.2.3-58-ga151 From 610a72b70733b6d5b7d3a1ee93f2e6daa2742ef3 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:06 -0700 Subject: platform: goldfish: pipe: Replace two code blocks with a function call Two function calls look cleaner because the function introduces takes case of all bit shifting and casting. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 35 +++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 3d28a9be5722..e3c09ff7867a 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -776,6 +776,14 @@ static struct miscdevice goldfish_pipe_miscdev = { .fops = &goldfish_pipe_fops, }; +static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth) +{ + const unsigned long paddr = __pa(addr); + + writel(upper_32_bits(paddr), porth); + writel(lower_32_bits(paddr), portl); +} + static int goldfish_pipe_device_init(struct platform_device *pdev) { struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; @@ -816,22 +824,17 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) } /* Send the buffer addresses to the host */ - { - u64 paddr = __pa(&dev->buffers->signalled_pipe_buffers); - - writel((u32)(unsigned long)(paddr >> 32), - dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); - writel((u32)(unsigned long)paddr, - dev->base + PIPE_REG_SIGNAL_BUFFER); - writel((u32)MAX_SIGNALLED_PIPES, - dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); - - paddr = __pa(&dev->buffers->open_command_params); - writel((u32)(unsigned long)(paddr >> 32), - dev->base + PIPE_REG_OPEN_BUFFER_HIGH); - writel((u32)(unsigned long)paddr, - dev->base + PIPE_REG_OPEN_BUFFER); - } + write_pa_addr(&dev->buffers->signalled_pipe_buffers, + dev->base + PIPE_REG_SIGNAL_BUFFER, + dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); + + writel((u32)MAX_SIGNALLED_PIPES, + dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); + + write_pa_addr(&dev->buffers->open_command_params, + dev->base + PIPE_REG_OPEN_BUFFER, + dev->base + PIPE_REG_OPEN_BUFFER_HIGH); + return 0; } -- cgit v1.2.3-58-ga151 From 468e62f92e90232260cd2dab49b7ca2fdf086c63 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:07 -0700 Subject: platform: goldfish: pipe: Remove reduntant casting Casting to u32 is not required here. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index e3c09ff7867a..ff0e150d15d5 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -828,7 +828,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) dev->base + PIPE_REG_SIGNAL_BUFFER, dev->base + PIPE_REG_SIGNAL_BUFFER_HIGH); - writel((u32)MAX_SIGNALLED_PIPES, + writel(MAX_SIGNALLED_PIPES, dev->base + PIPE_REG_SIGNAL_BUFFER_COUNT); write_pa_addr(&dev->buffers->open_command_params, -- cgit v1.2.3-58-ga151 From 1d1021a0f9177f6082c7baed7f781794cd32b5cd Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:08 -0700 Subject: platform: goldfish: pipe: Remove a blank line before '}' checkpatch: Blank lines aren't necessary before a close brace '}' Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index ff0e150d15d5..14441e7106ba 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -272,8 +272,8 @@ static int pin_user_pages(unsigned long first_page, unsigned long last_page, return -EFAULT; if (ret < requested_pages) *iter_last_page_size = PAGE_SIZE; - return ret; + return ret; } static void release_user_pages(struct page **pages, int pages_count, -- cgit v1.2.3-58-ga151 From 61b38f027f85bb64e67e14a61a5abf4616bb331a Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:09 -0700 Subject: platform: goldfish: pipe: Rename 'wakeBit' to 'wake_bit' checkpatch: Avoid CamelCase Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 14441e7106ba..cab1935a04c5 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -363,18 +363,18 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe, static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) { - u32 wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; + u32 wake_bit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; - set_bit(wakeBit, &pipe->flags); + set_bit(wake_bit, &pipe->flags); /* Tell the emulator we're going to wait for a wake event */ (void)goldfish_cmd(pipe, is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ); - while (test_bit(wakeBit, &pipe->flags)) { + while (test_bit(wake_bit, &pipe->flags)) { if (wait_event_interruptible( pipe->wake_queue, - !test_bit(wakeBit, &pipe->flags))) + !test_bit(wake_bit, &pipe->flags))) return -ERESTARTSYS; if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) -- cgit v1.2.3-58-ga151 From a0739604f48918a9b47867d28fd0c59d33ef9640 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:10 -0700 Subject: platform: goldfish: pipe: Remove reduntant casting to (void) Casting to (void) is no-op. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index cab1935a04c5..fb37499731a4 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -368,7 +368,7 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) set_bit(wake_bit, &pipe->flags); /* Tell the emulator we're going to wait for a wake event */ - (void)goldfish_cmd(pipe, + goldfish_cmd(pipe, is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ); while (test_bit(wake_bit, &pipe->flags)) { @@ -748,7 +748,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp) struct goldfish_pipe_dev *dev = pipe->dev; /* The guest is closing the channel, so tell the emulator right now */ - (void)goldfish_cmd(pipe, PIPE_CMD_CLOSE); + goldfish_cmd(pipe, PIPE_CMD_CLOSE); spin_lock_irqsave(&dev->lock, flags); dev->pipes[pipe->id] = NULL; -- cgit v1.2.3-58-ga151 From 92c320b97eb3103d68aab1a69347d1ad3b5c489e Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:11 -0700 Subject: platform: goldfish: pipe: Rename goldfish_x to goldfish_pipe_x Add "pipe" to the pipe related function names. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index fb37499731a4..95256a5c593c 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -213,7 +213,8 @@ struct goldfish_pipe_dev { struct goldfish_pipe_dev goldfish_pipe_dev; -static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) +static int goldfish_pipe_cmd_locked(struct goldfish_pipe *pipe, + enum PipeCmdCode cmd) { pipe->command_buffer->cmd = cmd; /* failure by default */ @@ -222,13 +223,13 @@ static int goldfish_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) return pipe->command_buffer->status; } -static int goldfish_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) +static int goldfish_pipe_cmd(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) { int status; if (mutex_lock_interruptible(&pipe->lock)) return PIPE_ERROR_IO; - status = goldfish_cmd_locked(pipe, cmd); + status = goldfish_pipe_cmd_locked(pipe, cmd); mutex_unlock(&pipe->lock); return status; } @@ -349,7 +350,7 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe, pipe->command_buffer); /* Transfer the data */ - *status = goldfish_cmd_locked(pipe, + *status = goldfish_pipe_cmd_locked(pipe, is_write ? PIPE_CMD_WRITE : PIPE_CMD_READ); *consumed_size = pipe->command_buffer->rw_params.consumed_size; @@ -368,7 +369,7 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) set_bit(wake_bit, &pipe->flags); /* Tell the emulator we're going to wait for a wake event */ - goldfish_cmd(pipe, + goldfish_pipe_cmd(pipe, is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ); while (test_bit(wake_bit, &pipe->flags)) { @@ -490,7 +491,7 @@ static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait) poll_wait(filp, &pipe->wake_queue, wait); - status = goldfish_cmd(pipe, PIPE_CMD_POLL); + status = goldfish_pipe_cmd(pipe, PIPE_CMD_POLL); if (status < 0) return -ERESTARTSYS; @@ -722,7 +723,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) MAX_BUFFERS_PER_COMMAND; dev->buffers->open_command_params.command_buffer_ptr = (u64)(unsigned long)__pa(pipe->command_buffer); - status = goldfish_cmd_locked(pipe, PIPE_CMD_OPEN); + status = goldfish_pipe_cmd_locked(pipe, PIPE_CMD_OPEN); spin_unlock_irqrestore(&dev->lock, flags); if (status < 0) goto err_cmd; @@ -748,7 +749,7 @@ static int goldfish_pipe_release(struct inode *inode, struct file *filp) struct goldfish_pipe_dev *dev = pipe->dev; /* The guest is closing the channel, so tell the emulator right now */ - goldfish_cmd(pipe, PIPE_CMD_CLOSE); + goldfish_pipe_cmd(pipe, PIPE_CMD_CLOSE); spin_lock_irqsave(&dev->lock, flags); dev->pipes[pipe->id] = NULL; -- cgit v1.2.3-58-ga151 From 562a74de9757d1ab92a2061ed48e8592827064e1 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:12 -0700 Subject: platform: goldfish: pipe: Fix lines to not end an opening bracket checkpatch: Lines should not end with a '(' or '[' Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 95256a5c593c..324129879b09 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -122,8 +122,8 @@ struct open_command_param { /* Device-level set of buffers shared with the host */ struct goldfish_pipe_dev_buffers { struct open_command_param open_command_params; - struct signalled_pipe_buffer signalled_pipe_buffers[ - MAX_SIGNALLED_PIPES]; + struct signalled_pipe_buffer + signalled_pipe_buffers[MAX_SIGNALLED_PIPES]; }; /* This data type models a given pipe instance */ @@ -373,8 +373,7 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) is_write ? PIPE_CMD_WAKE_ON_WRITE : PIPE_CMD_WAKE_ON_READ); while (test_bit(wake_bit, &pipe->flags)) { - if (wait_event_interruptible( - pipe->wake_queue, + if (wait_event_interruptible(pipe->wake_queue, !test_bit(wake_bit, &pipe->flags))) return -ERESTARTSYS; -- cgit v1.2.3-58-ga151 From 52bcc7d942f09c03d558fe43843b13a60ac1a351 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:13 -0700 Subject: platform: goldfish: pipe: Fix alignment to match parenthesis checkpatch: Alignment should match open parenthesis. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 83 ++++++++++++++++++------------- 1 file changed, 48 insertions(+), 35 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 324129879b09..02783bc59dd7 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -252,10 +252,12 @@ static int goldfish_pipe_error_convert(int status) } } -static int pin_user_pages(unsigned long first_page, unsigned long last_page, - unsigned int last_page_size, int is_write, - struct page *pages[MAX_BUFFERS_PER_COMMAND], - unsigned int *iter_last_page_size) +static int pin_user_pages(unsigned long first_page, + unsigned long last_page, + unsigned int last_page_size, + int is_write, + struct page *pages[MAX_BUFFERS_PER_COMMAND], + unsigned int *iter_last_page_size) { int ret; int requested_pages = ((last_page - first_page) >> PAGE_SHIFT) + 1; @@ -267,8 +269,8 @@ static int pin_user_pages(unsigned long first_page, unsigned long last_page, *iter_last_page_size = last_page_size; } - ret = get_user_pages_fast( - first_page, requested_pages, !is_write, pages); + ret = get_user_pages_fast(first_page, requested_pages, !is_write, + pages); if (ret <= 0) return -EFAULT; if (ret < requested_pages) @@ -278,7 +280,7 @@ static int pin_user_pages(unsigned long first_page, unsigned long last_page, } static void release_user_pages(struct page **pages, int pages_count, - int is_write, s32 consumed_size) + int is_write, s32 consumed_size) { int i; @@ -290,12 +292,15 @@ static void release_user_pages(struct page **pages, int pages_count, } /* Populate the call parameters, merging adjacent pages together */ -static void populate_rw_params( - struct page **pages, int pages_count, - unsigned long address, unsigned long address_end, - unsigned long first_page, unsigned long last_page, - unsigned int iter_last_page_size, int is_write, - struct goldfish_pipe_command *command) +static void populate_rw_params(struct page **pages, + int pages_count, + unsigned long address, + unsigned long address_end, + unsigned long first_page, + unsigned long last_page, + unsigned int iter_last_page_size, + int is_write, + struct goldfish_pipe_command *command) { /* * Process the first page separately - it's the only page that @@ -327,16 +332,20 @@ static void populate_rw_params( } static int transfer_max_buffers(struct goldfish_pipe *pipe, - unsigned long address, unsigned long address_end, int is_write, - unsigned long last_page, unsigned int last_page_size, - s32 *consumed_size, int *status) + unsigned long address, + unsigned long address_end, + int is_write, + unsigned long last_page, + unsigned int last_page_size, + s32 *consumed_size, + int *status) { static struct page *pages[MAX_BUFFERS_PER_COMMAND]; unsigned long first_page = address & PAGE_MASK; unsigned int iter_last_page_size; int pages_count = pin_user_pages(first_page, last_page, - last_page_size, is_write, - pages, &iter_last_page_size); + last_page_size, is_write, + pages, &iter_last_page_size); if (pages_count < 0) return pages_count; @@ -346,8 +355,8 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe, return -ERESTARTSYS; populate_rw_params(pages, pages_count, address, address_end, - first_page, last_page, iter_last_page_size, is_write, - pipe->command_buffer); + first_page, last_page, iter_last_page_size, is_write, + pipe->command_buffer); /* Transfer the data */ *status = goldfish_pipe_cmd_locked(pipe, @@ -374,7 +383,7 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) while (test_bit(wake_bit, &pipe->flags)) { if (wait_event_interruptible(pipe->wake_queue, - !test_bit(wake_bit, &pipe->flags))) + !test_bit(wake_bit, &pipe->flags))) return -ERESTARTSYS; if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) @@ -385,7 +394,9 @@ static int wait_for_host_signal(struct goldfish_pipe *pipe, int is_write) } static ssize_t goldfish_pipe_read_write(struct file *filp, - char __user *buffer, size_t bufflen, int is_write) + char __user *buffer, + size_t bufflen, + int is_write) { struct goldfish_pipe *pipe = filp->private_data; int count = 0, ret = -EINVAL; @@ -400,7 +411,7 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, return 0; /* Check the buffer range for access */ if (unlikely(!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ, - buffer, bufflen))) + buffer, bufflen))) return -EFAULT; address = (unsigned long)buffer; @@ -413,8 +424,8 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, int status; ret = transfer_max_buffers(pipe, address, address_end, is_write, - last_page, last_page_size, &consumed_size, - &status); + last_page, last_page_size, + &consumed_size, &status); if (ret < 0) break; @@ -467,19 +478,21 @@ static ssize_t goldfish_pipe_read_write(struct file *filp, } static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer, - size_t bufflen, loff_t *ppos) + size_t bufflen, loff_t *ppos) { return goldfish_pipe_read_write(filp, buffer, bufflen, - /* is_write */ 0); + /* is_write */ 0); } static ssize_t goldfish_pipe_write(struct file *filp, - const char __user *buffer, size_t bufflen, - loff_t *ppos) + const char __user *buffer, size_t bufflen, + loff_t *ppos) { - return goldfish_pipe_read_write(filp, - /* cast away the const */(char __user *)buffer, bufflen, - /* is_write */ 1); + /* cast away the const */ + char __user *no_const_buffer = (char __user *)buffer; + + return goldfish_pipe_read_write(filp, no_const_buffer, bufflen, + /* is_write */ 1); } static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait) @@ -507,7 +520,7 @@ static __poll_t goldfish_pipe_poll(struct file *filp, poll_table *wait) } static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, - u32 id, u32 flags) + u32 id, u32 flags) { struct goldfish_pipe *pipe; @@ -529,7 +542,7 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, } static void signalled_pipes_remove_locked(struct goldfish_pipe_dev *dev, - struct goldfish_pipe *pipe) + struct goldfish_pipe *pipe) { if (pipe->prev_signalled) pipe->prev_signalled->next_signalled = pipe->next_signalled; @@ -805,7 +818,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) dev->first_signalled_pipe = NULL; dev->pipes_capacity = INITIAL_PIPES_CAPACITY; dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), - GFP_KERNEL); + GFP_KERNEL); if (!dev->pipes) return -ENOMEM; -- cgit v1.2.3-58-ga151 From cc14057f514bc8500bd792b06409557114ec4b53 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:14 -0700 Subject: platform: goldfish: pipe: Move logical ops to the end of the prev line checkpatch: Logical continuations should be on the previous line Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 02783bc59dd7..c892a0447a81 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -532,8 +532,8 @@ static void signalled_pipes_add_locked(struct goldfish_pipe_dev *dev, return; pipe->signalled_flags |= flags; - if (pipe->prev_signalled || pipe->next_signalled - || dev->first_signalled_pipe == pipe) + if (pipe->prev_signalled || pipe->next_signalled || + dev->first_signalled_pipe == pipe) return; /* already in the list */ pipe->next_signalled = dev->first_signalled_pipe; if (dev->first_signalled_pipe) -- cgit v1.2.3-58-ga151 From bfb8e83847931ac854810dfa37f7cbd56bcc2f1f Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Mon, 27 Aug 2018 11:23:15 -0700 Subject: platform: goldfish: pipe: Replace "x==NULL" to "!x" checkpatch: Comparison to NULL could be written "!x" Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index c892a0447a81..b4a484bbcdaa 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -699,7 +699,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) /* Allocate new pipe kernel object */ struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); - if (pipe == NULL) + if (!pipe) return -ENOMEM; pipe->dev = dev; @@ -865,23 +865,23 @@ static int goldfish_pipe_probe(struct platform_device *pdev) struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; /* not thread safe, but this should not happen */ - WARN_ON(dev->base != NULL); + WARN_ON(dev->base); spin_lock_init(&dev->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL || resource_size(r) < PAGE_SIZE) { + if (!r || resource_size(r) < PAGE_SIZE) { dev_err(&pdev->dev, "can't allocate i/o page\n"); return -EINVAL; } dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); - if (dev->base == NULL) { + if (!dev->base) { dev_err(&pdev->dev, "ioremap failed\n"); return -EINVAL; } r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (r == NULL) { + if (!r) { err = -EINVAL; goto error; } -- cgit v1.2.3-58-ga151 From 2e6ae11dd0d1c37f44cec51a58fb2092e55ed0f5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 20 Aug 2018 11:35:37 +0200 Subject: slimbus: ngd: mark PM functions as __maybe_unused qcom_slim_ngd_runtime_suspend is protected by an #ifdef, qcom_slim_ngd_runtime_idle is now, which causes a build time warning: drivers/slimbus/qcom-ngd-ctrl.c:1470:12: error: 'qcom_slim_ngd_runtime_idle' defined but not used [-Werror=unused-function] Marking both as __maybe_unused lets us get rid of the warning as well as the #ifdef. Fixes: 917809e2280b ("slimbus: ngd: Add qcom SLIMBus NGD driver") Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 8be4d6786c61..14a9d18306cb 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1467,7 +1467,7 @@ static int qcom_slim_ngd_remove(struct platform_device *pdev) return 0; } -static int qcom_slim_ngd_runtime_idle(struct device *dev) +static int __maybe_unused qcom_slim_ngd_runtime_idle(struct device *dev) { struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev); @@ -1477,8 +1477,7 @@ static int qcom_slim_ngd_runtime_idle(struct device *dev) return -EAGAIN; } -#ifdef CONFIG_PM -static int qcom_slim_ngd_runtime_suspend(struct device *dev) +static int __maybe_unused qcom_slim_ngd_runtime_suspend(struct device *dev) { struct qcom_slim_ngd_ctrl *ctrl = dev_get_drvdata(dev); int ret = 0; @@ -1491,7 +1490,6 @@ static int qcom_slim_ngd_runtime_suspend(struct device *dev) return ret; } -#endif static const struct dev_pm_ops qcom_slim_ngd_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, -- cgit v1.2.3-58-ga151 From 99aeebe8140c642cdf2c3822b1dcbd37433fe770 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Fri, 17 Aug 2018 11:42:35 +0800 Subject: misc: sgi-xp: remove meaningless null check before kfree kfree has taken null pointer into account. so check the null pointer before kfree is meaningless. Signed-off-by: zhong jiang Acked-by: Robin Holt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-xp/xpc_partition.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sgi-xp/xpc_partition.c b/drivers/misc/sgi-xp/xpc_partition.c index 0c3ef6f1df54..3eba1c420cc0 100644 --- a/drivers/misc/sgi-xp/xpc_partition.c +++ b/drivers/misc/sgi-xp/xpc_partition.c @@ -98,8 +98,7 @@ xpc_get_rsvd_page_pa(int nasid) len = L1_CACHE_ALIGN(len); if (len > buf_len) { - if (buf_base != NULL) - kfree(buf_base); + kfree(buf_base); buf_len = L1_CACHE_ALIGN(len); buf = xpc_kmalloc_cacheline_aligned(buf_len, GFP_KERNEL, &buf_base); -- cgit v1.2.3-58-ga151 From dbd71398bdd44a54734eff6610ee04371047cecc Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 14 Aug 2018 13:30:18 +0200 Subject: drivers: misc: ad525x_dpot: Update MODULE AUTHOR email address no functional changes Signed-off-by: Michael Hennerich Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ad525x_dpot-i2c.c | 2 +- drivers/misc/ad525x_dpot-spi.c | 2 +- drivers/misc/ad525x_dpot.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 4f832002d116..1827c69959fb 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -114,6 +114,6 @@ static struct i2c_driver ad_dpot_i2c_driver = { module_i2c_driver(ad_dpot_i2c_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("digital potentiometer I2C bus driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/misc/ad525x_dpot-spi.c b/drivers/misc/ad525x_dpot-spi.c index 39a7f517ee7e..0383ec153725 100644 --- a/drivers/misc/ad525x_dpot-spi.c +++ b/drivers/misc/ad525x_dpot-spi.c @@ -140,7 +140,7 @@ static struct spi_driver ad_dpot_spi_driver = { module_spi_driver(ad_dpot_spi_driver); -MODULE_AUTHOR("Michael Hennerich "); +MODULE_AUTHOR("Michael Hennerich "); MODULE_DESCRIPTION("digital potentiometer SPI bus driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("spi:ad_dpot"); diff --git a/drivers/misc/ad525x_dpot.c b/drivers/misc/ad525x_dpot.c index bc591b7168db..a0afadefcc49 100644 --- a/drivers/misc/ad525x_dpot.c +++ b/drivers/misc/ad525x_dpot.c @@ -1,7 +1,7 @@ /* * ad525x_dpot: Driver for the Analog Devices digital potentiometers * Copyright (c) 2009-2010 Analog Devices, Inc. - * Author: Michael Hennerich + * Author: Michael Hennerich * * DEVID #Wipers #Positions Resistor Options (kOhm) * AD5258 1 64 1, 10, 50, 100 @@ -64,7 +64,7 @@ * Author: Chris Verges * * derived from ad5252.c - * Copyright (c) 2006-2011 Michael Hennerich + * Copyright (c) 2006-2011 Michael Hennerich * * Licensed under the GPL-2 or later. */ @@ -760,6 +760,6 @@ EXPORT_SYMBOL(ad_dpot_remove); MODULE_AUTHOR("Chris Verges , " - "Michael Hennerich "); + "Michael Hennerich "); MODULE_DESCRIPTION("Digital potentiometer driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 09ed061a4f56d50758851ca3997510f27115f81b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Aug 2018 13:37:03 -0700 Subject: firmware: coreboot: Let OF core populate platform device Now that the /firmware/coreboot node in DT is populated by the core DT platform code with commit 3aa0582fdb82 ("of: platform: populate /firmware/ node from of_platform_default_populate_init()") we should and can remove the platform device creation here. Otherwise, the of_platform_device_create() call will fail, the coreboot of driver won't be registered, and this driver will never bind. At the same time, we should move this driver to use MODULE_DEVICE_TABLE so that module auto-load works properly when the coreboot device is auto-populated and we should drop the of_node handling that was presumably placed here to hold a reference to the DT node created during module init that no longer happens. Cc: Wei-Ning Huang Cc: Julius Werner Reviewed-by: Brian Norris Cc: Samuel Holland Reviewed-by: Sudeep Holla Fixes: 3aa0582fdb82 ("of: platform: populate /firmware/ node from of_platform_default_populate_init()") Signed-off-by: Stephen Boyd Reviewed-by: Julius Werner Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/coreboot_table-of.c | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/coreboot_table-of.c b/drivers/firmware/google/coreboot_table-of.c index f15bf404c579..9b90c0fa4a0b 100644 --- a/drivers/firmware/google/coreboot_table-of.c +++ b/drivers/firmware/google/coreboot_table-of.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include "coreboot_table.h" @@ -30,7 +29,6 @@ static int coreboot_table_of_probe(struct platform_device *pdev) void __iomem *ptr; ptr = of_iomap(fw_dn, 0); - of_node_put(fw_dn); if (!ptr) return -ENOMEM; @@ -44,8 +42,9 @@ static int coreboot_table_of_remove(struct platform_device *pdev) static const struct of_device_id coreboot_of_match[] = { { .compatible = "coreboot" }, - {}, + {} }; +MODULE_DEVICE_TABLE(of, coreboot_of_match); static struct platform_driver coreboot_table_of_driver = { .probe = coreboot_table_of_probe, @@ -55,28 +54,7 @@ static struct platform_driver coreboot_table_of_driver = { .of_match_table = coreboot_of_match, }, }; - -static int __init platform_coreboot_table_of_init(void) -{ - struct platform_device *pdev; - struct device_node *of_node; - - /* Limit device creation to the presence of /firmware/coreboot node */ - of_node = of_find_node_by_path("/firmware/coreboot"); - if (!of_node) - return -ENODEV; - - if (!of_match_node(coreboot_of_match, of_node)) - return -ENODEV; - - pdev = of_platform_device_create(of_node, "coreboot_table_of", NULL); - if (!pdev) - return -ENODEV; - - return platform_driver_register(&coreboot_table_of_driver); -} - -module_init(platform_coreboot_table_of_init); +module_platform_driver(coreboot_table_of_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 20edec388277b62ddfddb8b2b376a937a2cd6d1b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Aug 2018 13:37:04 -0700 Subject: firmware: coreboot: Unmap ioregion after device population Both callers of coreboot_table_init() ioremap the pointer that comes in but they don't unmap the memory on failure. Both of them also fail probe immediately with the return value of coreboot_table_init(), leaking a mapping when it fails. The mapping isn't necessary at all after devices are populated either, so we can just drop the mapping here when we exit the function. Let's do that to simplify the code a bit and plug the leak. Cc: Wei-Ning Huang Cc: Julius Werner Cc: Brian Norris Cc: Samuel Holland Fixes: 570d30c2823f ("firmware: coreboot: Expose the coreboot table as a bus") Signed-off-by: Stephen Boyd Reviewed-by: Julius Werner Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/coreboot_table.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 19db5709ae28..898bb9abc41f 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -110,7 +110,8 @@ int coreboot_table_init(struct device *dev, void __iomem *ptr) if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { pr_warn("coreboot_table: coreboot table missing or corrupt!\n"); - return -ENODEV; + ret = -ENODEV; + goto out; } ptr_entry = (void *)ptr_header + header.header_bytes; @@ -137,7 +138,8 @@ int coreboot_table_init(struct device *dev, void __iomem *ptr) ptr_entry += entry.size; } - +out: + iounmap(ptr); return ret; } EXPORT_SYMBOL(coreboot_table_init); @@ -146,7 +148,6 @@ int coreboot_table_exit(void) { if (ptr_header) { bus_unregister(&coreboot_bus_type); - iounmap(ptr_header); ptr_header = NULL; } -- cgit v1.2.3-58-ga151 From b81e3140e4128921f25119a2b5ae0049f8373d1a Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Aug 2018 13:37:05 -0700 Subject: firmware: coreboot: Make bus registration symmetric The bus is registered in module_init() but is unregistered when the platform driver remove() function calls coreboot_table_exit(). That isn't symmetric and it causes the bus to appear on systems that compile this code in, even when there isn't any coreboot firmware on the device. Let's move the registration to the coreboot_table_init() function so that it matches the exit path. Cc: Wei-Ning Huang Cc: Julius Werner Cc: Brian Norris Cc: Samuel Holland Signed-off-by: Stephen Boyd Reviewed-by: Julius Werner Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/coreboot_table.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 898bb9abc41f..f417170f83ea 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -70,12 +70,6 @@ static struct bus_type coreboot_bus_type = { .remove = coreboot_bus_remove, }; -static int __init coreboot_bus_init(void) -{ - return bus_register(&coreboot_bus_type); -} -module_init(coreboot_bus_init); - static void coreboot_device_release(struct device *dev) { struct coreboot_device *device = CB_DEV(dev); @@ -114,6 +108,10 @@ int coreboot_table_init(struct device *dev, void __iomem *ptr) goto out; } + ret = bus_register(&coreboot_bus_type); + if (ret) + goto out; + ptr_entry = (void *)ptr_header + header.header_bytes; for (i = 0; i < header.table_entries; i++) { memcpy_fromio(&entry, ptr_entry, sizeof(entry)); @@ -138,6 +136,10 @@ int coreboot_table_init(struct device *dev, void __iomem *ptr) ptr_entry += entry.size; } + + if (ret) + bus_unregister(&coreboot_bus_type); + out: iounmap(ptr); return ret; -- cgit v1.2.3-58-ga151 From a28aad66da8bd19b249670d003bb9a698bdda397 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Aug 2018 13:37:06 -0700 Subject: firmware: coreboot: Collapse platform drivers into bus core The DT based and ACPI based platform drivers here do the same thing; map some memory and hand it over to the coreboot bus to populate devices. The only major difference is that the DT based driver doesn't map the coreboot table header to figure out how large of a region to map for the whole coreboot table and it uses of_iomap() instead of ioremap_cache(). A cached or non-cached mapping shouldn't matter here and mapping some smaller region first before mapping the whole table is just more work but should be OK. In the end, we can remove two files and combine the code all in one place making it easier to reason about things. We leave the old Kconfigs in place for a little while longer but make them hidden and select the previously hidden config option. This way users can upgrade without having to know to reselect this config in the future. Later on we can remove the old hidden configs. Cc: Wei-Ning Huang Cc: Julius Werner Cc: Brian Norris Cc: Samuel Holland Signed-off-by: Stephen Boyd Reviewed-by: Julius Werner Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/Kconfig | 26 +++----- drivers/firmware/google/Makefile | 2 - drivers/firmware/google/coreboot_table-acpi.c | 88 --------------------------- drivers/firmware/google/coreboot_table-of.c | 60 ------------------ drivers/firmware/google/coreboot_table.c | 66 ++++++++++++++++++-- drivers/firmware/google/coreboot_table.h | 6 -- 6 files changed, 72 insertions(+), 176 deletions(-) delete mode 100644 drivers/firmware/google/coreboot_table-acpi.c delete mode 100644 drivers/firmware/google/coreboot_table-of.c (limited to 'drivers') diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index a456a000048b..16564cfa47ef 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -19,28 +19,22 @@ config GOOGLE_SMI variables. config GOOGLE_COREBOOT_TABLE - tristate - depends on GOOGLE_COREBOOT_TABLE_ACPI || GOOGLE_COREBOOT_TABLE_OF - -config GOOGLE_COREBOOT_TABLE_ACPI - tristate "Coreboot Table Access - ACPI" - depends on ACPI - select GOOGLE_COREBOOT_TABLE + tristate "Coreboot Table Access" + depends on ACPI || OF help This option enables the coreboot_table module, which provides other - firmware modules to access to the coreboot table. The coreboot table - pointer is accessed through the ACPI "GOOGCB00" object. + firmware modules access to the coreboot table. The coreboot table + pointer is accessed through the ACPI "GOOGCB00" object or the + device tree node /firmware/coreboot. If unsure say N. +config GOOGLE_COREBOOT_TABLE_ACPI + tristate + select GOOGLE_COREBOOT_TABLE + config GOOGLE_COREBOOT_TABLE_OF - tristate "Coreboot Table Access - Device Tree" - depends on OF + tristate select GOOGLE_COREBOOT_TABLE - help - This option enable the coreboot_table module, which provide other - firmware modules to access coreboot table. The coreboot table pointer - is accessed through the device tree node /firmware/coreboot. - If unsure say N. config GOOGLE_MEMCONSOLE tristate diff --git a/drivers/firmware/google/Makefile b/drivers/firmware/google/Makefile index d0b3fba96194..d17caded5d88 100644 --- a/drivers/firmware/google/Makefile +++ b/drivers/firmware/google/Makefile @@ -2,8 +2,6 @@ obj-$(CONFIG_GOOGLE_SMI) += gsmi.o obj-$(CONFIG_GOOGLE_COREBOOT_TABLE) += coreboot_table.o -obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_ACPI) += coreboot_table-acpi.o -obj-$(CONFIG_GOOGLE_COREBOOT_TABLE_OF) += coreboot_table-of.o obj-$(CONFIG_GOOGLE_FRAMEBUFFER_COREBOOT) += framebuffer-coreboot.o obj-$(CONFIG_GOOGLE_MEMCONSOLE) += memconsole.o obj-$(CONFIG_GOOGLE_MEMCONSOLE_COREBOOT) += memconsole-coreboot.o diff --git a/drivers/firmware/google/coreboot_table-acpi.c b/drivers/firmware/google/coreboot_table-acpi.c deleted file mode 100644 index 77197fe3d42f..000000000000 --- a/drivers/firmware/google/coreboot_table-acpi.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * coreboot_table-acpi.c - * - * Using ACPI to locate Coreboot table and provide coreboot table access. - * - * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "coreboot_table.h" - -static int coreboot_table_acpi_probe(struct platform_device *pdev) -{ - phys_addr_t phyaddr; - resource_size_t len; - struct coreboot_table_header __iomem *header = NULL; - struct resource *res; - void __iomem *ptr = NULL; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; - - len = resource_size(res); - if (!res->start || !len) - return -EINVAL; - - phyaddr = res->start; - header = ioremap_cache(phyaddr, sizeof(*header)); - if (header == NULL) - return -ENOMEM; - - ptr = ioremap_cache(phyaddr, - header->header_bytes + header->table_bytes); - iounmap(header); - if (!ptr) - return -ENOMEM; - - return coreboot_table_init(&pdev->dev, ptr); -} - -static int coreboot_table_acpi_remove(struct platform_device *pdev) -{ - return coreboot_table_exit(); -} - -static const struct acpi_device_id cros_coreboot_acpi_match[] = { - { "GOOGCB00", 0 }, - { "BOOT0000", 0 }, - { } -}; -MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); - -static struct platform_driver coreboot_table_acpi_driver = { - .probe = coreboot_table_acpi_probe, - .remove = coreboot_table_acpi_remove, - .driver = { - .name = "coreboot_table_acpi", - .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match), - }, -}; - -static int __init coreboot_table_acpi_init(void) -{ - return platform_driver_register(&coreboot_table_acpi_driver); -} - -module_init(coreboot_table_acpi_init); - -MODULE_AUTHOR("Google, Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table-of.c b/drivers/firmware/google/coreboot_table-of.c deleted file mode 100644 index 9b90c0fa4a0b..000000000000 --- a/drivers/firmware/google/coreboot_table-of.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * coreboot_table-of.c - * - * Coreboot table access through open firmware. - * - * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include -#include -#include -#include -#include - -#include "coreboot_table.h" - -static int coreboot_table_of_probe(struct platform_device *pdev) -{ - struct device_node *fw_dn = pdev->dev.of_node; - void __iomem *ptr; - - ptr = of_iomap(fw_dn, 0); - if (!ptr) - return -ENOMEM; - - return coreboot_table_init(&pdev->dev, ptr); -} - -static int coreboot_table_of_remove(struct platform_device *pdev) -{ - return coreboot_table_exit(); -} - -static const struct of_device_id coreboot_of_match[] = { - { .compatible = "coreboot" }, - {} -}; -MODULE_DEVICE_TABLE(of, coreboot_of_match); - -static struct platform_driver coreboot_table_of_driver = { - .probe = coreboot_table_of_probe, - .remove = coreboot_table_of_remove, - .driver = { - .name = "coreboot_table_of", - .of_match_table = coreboot_of_match, - }, -}; -module_platform_driver(coreboot_table_of_driver); - -MODULE_AUTHOR("Google, Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index f417170f83ea..e9cd4da61404 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -16,12 +16,15 @@ * GNU General Public License for more details. */ +#include #include #include #include #include #include #include +#include +#include #include #include "coreboot_table.h" @@ -91,7 +94,7 @@ void coreboot_driver_unregister(struct coreboot_driver *driver) } EXPORT_SYMBOL(coreboot_driver_unregister); -int coreboot_table_init(struct device *dev, void __iomem *ptr) +static int coreboot_table_init(struct device *dev, void __iomem *ptr) { int i, ret; void *ptr_entry; @@ -144,9 +147,38 @@ out: iounmap(ptr); return ret; } -EXPORT_SYMBOL(coreboot_table_init); -int coreboot_table_exit(void) +static int coreboot_table_probe(struct platform_device *pdev) +{ + phys_addr_t phyaddr; + resource_size_t len; + struct coreboot_table_header __iomem *header = NULL; + struct resource *res; + void __iomem *ptr = NULL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + len = resource_size(res); + if (!res->start || !len) + return -EINVAL; + + phyaddr = res->start; + header = ioremap_cache(phyaddr, sizeof(*header)); + if (header == NULL) + return -ENOMEM; + + ptr = ioremap_cache(phyaddr, + header->header_bytes + header->table_bytes); + iounmap(header); + if (!ptr) + return -ENOMEM; + + return coreboot_table_init(&pdev->dev, ptr); +} + +static int coreboot_table_remove(struct platform_device *pdev) { if (ptr_header) { bus_unregister(&coreboot_bus_type); @@ -155,7 +187,33 @@ int coreboot_table_exit(void) return 0; } -EXPORT_SYMBOL(coreboot_table_exit); +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_coreboot_acpi_match[] = { + { "GOOGCB00", 0 }, + { "BOOT0000", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_coreboot_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id coreboot_of_match[] = { + { .compatible = "coreboot" }, + {} +}; +MODULE_DEVICE_TABLE(of, coreboot_of_match); +#endif + +static struct platform_driver coreboot_table_driver = { + .probe = coreboot_table_probe, + .remove = coreboot_table_remove, + .driver = { + .name = "coreboot_table", + .acpi_match_table = ACPI_PTR(cros_coreboot_acpi_match), + .of_match_table = of_match_ptr(coreboot_of_match), + }, +}; +module_platform_driver(coreboot_table_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h index 8ad95a94481b..71a9de6b15fa 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/google/coreboot_table.h @@ -91,10 +91,4 @@ int coreboot_driver_register(struct coreboot_driver *driver); /* Unregister a driver that uses the data from a coreboot table. */ void coreboot_driver_unregister(struct coreboot_driver *driver); -/* Initialize coreboot table module given a pointer to iomem */ -int coreboot_table_init(struct device *dev, void __iomem *ptr); - -/* Cleanup coreboot table module */ -int coreboot_table_exit(void); - #endif /* __COREBOOT_TABLE_H */ -- cgit v1.2.3-58-ga151 From a7d9b5f0120eea9f0f58c2ed2b98d2fc86389af3 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Aug 2018 13:37:07 -0700 Subject: firmware: coreboot: Remap RAM with memremap() instead of ioremap() This is all system memory, so we shouldn't be mapping this all with ioremap() as these aren't I/O regions. Instead, they're memory regions so we should use memremap(). Pick MEMREMAP_WB so we can map memory from RAM directly if that's possible, otherwise it falls back to ioremap_cache() like is being done here already. This also nicely silences the sparse warnings in this code and reduces the need to copy anything around anymore. Cc: Wei-Ning Huang Cc: Julius Werner Cc: Brian Norris Cc: Samuel Holland Signed-off-by: Stephen Boyd Reviewed-by: Julius Werner Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/coreboot_table.c | 40 +++++++++++++++----------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index e9cd4da61404..2c6c35f0da32 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -32,7 +32,7 @@ #define CB_DEV(d) container_of(d, struct coreboot_device, dev) #define CB_DRV(d) container_of(d, struct coreboot_driver, drv) -static struct coreboot_table_header __iomem *ptr_header; +static struct coreboot_table_header *ptr_header; static int coreboot_bus_match(struct device *dev, struct device_driver *drv) { @@ -94,18 +94,18 @@ void coreboot_driver_unregister(struct coreboot_driver *driver) } EXPORT_SYMBOL(coreboot_driver_unregister); -static int coreboot_table_init(struct device *dev, void __iomem *ptr) +static int coreboot_table_init(struct device *dev, void *ptr) { int i, ret; void *ptr_entry; struct coreboot_device *device; - struct coreboot_table_entry entry; - struct coreboot_table_header header; + struct coreboot_table_entry *entry; + struct coreboot_table_header *header; ptr_header = ptr; - memcpy_fromio(&header, ptr_header, sizeof(header)); + header = ptr; - if (strncmp(header.signature, "LBIO", sizeof(header.signature))) { + if (strncmp(header->signature, "LBIO", sizeof(header->signature))) { pr_warn("coreboot_table: coreboot table missing or corrupt!\n"); ret = -ENODEV; goto out; @@ -115,11 +115,11 @@ static int coreboot_table_init(struct device *dev, void __iomem *ptr) if (ret) goto out; - ptr_entry = (void *)ptr_header + header.header_bytes; - for (i = 0; i < header.table_entries; i++) { - memcpy_fromio(&entry, ptr_entry, sizeof(entry)); + ptr_entry = ptr_header + header->header_bytes; + for (i = 0; i < header->table_entries; i++) { + entry = ptr_entry; - device = kzalloc(sizeof(struct device) + entry.size, GFP_KERNEL); + device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL); if (!device) { ret = -ENOMEM; break; @@ -129,7 +129,7 @@ static int coreboot_table_init(struct device *dev, void __iomem *ptr) device->dev.parent = dev; device->dev.bus = &coreboot_bus_type; device->dev.release = coreboot_device_release; - memcpy_fromio(&device->entry, ptr_entry, entry.size); + memcpy(&device->entry, ptr_entry, entry->size); ret = device_register(&device->dev); if (ret) { @@ -137,24 +137,23 @@ static int coreboot_table_init(struct device *dev, void __iomem *ptr) break; } - ptr_entry += entry.size; + ptr_entry += entry->size; } if (ret) bus_unregister(&coreboot_bus_type); out: - iounmap(ptr); + memunmap(ptr); return ret; } static int coreboot_table_probe(struct platform_device *pdev) { - phys_addr_t phyaddr; resource_size_t len; - struct coreboot_table_header __iomem *header = NULL; + struct coreboot_table_header *header; struct resource *res; - void __iomem *ptr = NULL; + void *ptr; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -164,14 +163,13 @@ static int coreboot_table_probe(struct platform_device *pdev) if (!res->start || !len) return -EINVAL; - phyaddr = res->start; - header = ioremap_cache(phyaddr, sizeof(*header)); + header = memremap(res->start, sizeof(*header), MEMREMAP_WB); if (header == NULL) return -ENOMEM; - ptr = ioremap_cache(phyaddr, - header->header_bytes + header->table_bytes); - iounmap(header); + ptr = memremap(res->start, header->header_bytes + header->table_bytes, + MEMREMAP_WB); + memunmap(header); if (!ptr) return -ENOMEM; -- cgit v1.2.3-58-ga151 From 7adb05bb813d1ba4863c8914eead6139d3d5f8ff Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Aug 2018 13:37:08 -0700 Subject: firmware: coreboot: Only populate devices in coreboot_table_init() This function checks the header for sanity, registers a bus, and populates devices for each coreboot table entry. Let's just populate devices here and pull the other bits up into the caller so that this function can be repurposed for pure device creation and registration. Cc: Wei-Ning Huang Cc: Julius Werner Cc: Brian Norris Cc: Samuel Holland Suggested-by: Julius Werner Signed-off-by: Stephen Boyd Reviewed-by: Julius Werner Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/coreboot_table.c | 67 ++++++++++++++------------------ 1 file changed, 29 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 2c6c35f0da32..078d3bbe632f 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -32,8 +32,6 @@ #define CB_DEV(d) container_of(d, struct coreboot_device, dev) #define CB_DRV(d) container_of(d, struct coreboot_driver, drv) -static struct coreboot_table_header *ptr_header; - static int coreboot_bus_match(struct device *dev, struct device_driver *drv) { struct coreboot_device *device = CB_DEV(dev); @@ -94,36 +92,21 @@ void coreboot_driver_unregister(struct coreboot_driver *driver) } EXPORT_SYMBOL(coreboot_driver_unregister); -static int coreboot_table_init(struct device *dev, void *ptr) +static int coreboot_table_populate(struct device *dev, void *ptr) { int i, ret; void *ptr_entry; struct coreboot_device *device; struct coreboot_table_entry *entry; - struct coreboot_table_header *header; - - ptr_header = ptr; - header = ptr; + struct coreboot_table_header *header = ptr; - if (strncmp(header->signature, "LBIO", sizeof(header->signature))) { - pr_warn("coreboot_table: coreboot table missing or corrupt!\n"); - ret = -ENODEV; - goto out; - } - - ret = bus_register(&coreboot_bus_type); - if (ret) - goto out; - - ptr_entry = ptr_header + header->header_bytes; + ptr_entry = ptr + header->header_bytes; for (i = 0; i < header->table_entries; i++) { entry = ptr_entry; device = kzalloc(sizeof(struct device) + entry->size, GFP_KERNEL); - if (!device) { - ret = -ENOMEM; - break; - } + if (!device) + return -ENOMEM; dev_set_name(&device->dev, "coreboot%d", i); device->dev.parent = dev; @@ -134,18 +117,13 @@ static int coreboot_table_init(struct device *dev, void *ptr) ret = device_register(&device->dev); if (ret) { put_device(&device->dev); - break; + return ret; } ptr_entry += entry->size; } - if (ret) - bus_unregister(&coreboot_bus_type); - -out: - memunmap(ptr); - return ret; + return 0; } static int coreboot_table_probe(struct platform_device *pdev) @@ -153,7 +131,9 @@ static int coreboot_table_probe(struct platform_device *pdev) resource_size_t len; struct coreboot_table_header *header; struct resource *res; + struct device *dev = &pdev->dev; void *ptr; + int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) @@ -163,26 +143,37 @@ static int coreboot_table_probe(struct platform_device *pdev) if (!res->start || !len) return -EINVAL; + /* Check just the header first to make sure things are sane */ header = memremap(res->start, sizeof(*header), MEMREMAP_WB); - if (header == NULL) + if (!header) return -ENOMEM; - ptr = memremap(res->start, header->header_bytes + header->table_bytes, - MEMREMAP_WB); + len = header->header_bytes + header->table_bytes; + ret = strncmp(header->signature, "LBIO", sizeof(header->signature)); memunmap(header); + if (ret) { + dev_warn(dev, "coreboot table missing or corrupt!\n"); + return -ENODEV; + } + + ptr = memremap(res->start, len, MEMREMAP_WB); if (!ptr) return -ENOMEM; - return coreboot_table_init(&pdev->dev, ptr); + ret = bus_register(&coreboot_bus_type); + if (!ret) { + ret = coreboot_table_populate(dev, ptr); + if (ret) + bus_unregister(&coreboot_bus_type); + } + memunmap(ptr); + + return ret; } static int coreboot_table_remove(struct platform_device *pdev) { - if (ptr_header) { - bus_unregister(&coreboot_bus_type); - ptr_header = NULL; - } - + bus_unregister(&coreboot_bus_type); return 0; } -- cgit v1.2.3-58-ga151 From b31bd669b45dca9c42d9dbd7eb1a4b02fa8f5323 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 13 Sep 2018 13:53:18 -0500 Subject: misc: sgi-gru: fix fall-through annotations Replace "fallthru" with a proper "fall through" annotation. This fix is part of the ongoing efforts to enabling -Wimplicit-fallthrough Signed-off-by: Gustavo A. R. Silva Acked-by: Dimitri Sivanich Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-gru/grukservices.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sgi-gru/grukservices.c b/drivers/misc/sgi-gru/grukservices.c index 030769018461..4b23d586fc3f 100644 --- a/drivers/misc/sgi-gru/grukservices.c +++ b/drivers/misc/sgi-gru/grukservices.c @@ -634,7 +634,7 @@ static int send_noop_message(void *cb, struct gru_message_queue_desc *mqd, break; case CBSS_PAGE_OVERFLOW: STAT(mesq_noop_page_overflow); - /* fallthru */ + /* fall through */ default: BUG(); } @@ -792,7 +792,7 @@ static int send_message_failure(void *cb, struct gru_message_queue_desc *mqd, break; case CBSS_PAGE_OVERFLOW: STAT(mesq_page_overflow); - /* fallthru */ + /* fall through */ default: BUG(); } -- cgit v1.2.3-58-ga151 From fac253e52fda73b6610f39716abe04dd2d919fb8 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:36 -0600 Subject: coresight: Document error handling in coresight_register commit 6403587a930c ("coresight: use put_device() instead of kfree()") fixes the double freeing of resources and ensures that the device refcount is dropped properly. Add a comment to explain this to help the readers and prevent people trying to "unfix" it again. While at it, rename the labels for better readability. Cc: Mathieu Poirier Cc: Arvind Yadav Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 3e07fd335f8c..9fd0c387e678 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -1006,7 +1006,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev = kzalloc(sizeof(*csdev), GFP_KERNEL); if (!csdev) { ret = -ENOMEM; - goto err_kzalloc_csdev; + goto err_out; } if (desc->type == CORESIGHT_DEV_TYPE_LINK || @@ -1022,7 +1022,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) refcnts = kcalloc(nr_refcnts, sizeof(*refcnts), GFP_KERNEL); if (!refcnts) { ret = -ENOMEM; - goto err_kzalloc_refcnts; + goto err_free_csdev; } csdev->refcnt = refcnts; @@ -1035,7 +1035,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); if (!conns) { ret = -ENOMEM; - goto err_kzalloc_conns; + goto err_free_refcnts; } for (i = 0; i < csdev->nr_outport; i++) { @@ -1062,7 +1062,11 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) ret = device_register(&csdev->dev); if (ret) { put_device(&csdev->dev); - goto err_kzalloc_csdev; + /* + * All resources are free'd explicitly via + * coresight_device_release(), triggered from put_device(). + */ + goto err_out; } mutex_lock(&coresight_mutex); @@ -1074,11 +1078,11 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) return csdev; -err_kzalloc_conns: +err_free_refcnts: kfree(refcnts); -err_kzalloc_refcnts: +err_free_csdev: kfree(csdev); -err_kzalloc_csdev: +err_out: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(coresight_register); -- cgit v1.2.3-58-ga151 From 6575fdb74645c14453f3119568ec45cbc54d4afb Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:37 -0600 Subject: coresight: platform: Refactor graph endpoint parsing Refactor the of graph endpoint parsing code, to make the error handling easier. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 138 +++++++++++++++++------------ 1 file changed, 83 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 6880bee195c8..70205f3eae8e 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -114,17 +114,70 @@ int of_coresight_get_cpu(const struct device_node *node) } EXPORT_SYMBOL_GPL(of_coresight_get_cpu); +/* + * of_coresight_parse_endpoint : Parse the given output endpoint @ep + * and fill the connection information in @pdata[@i]. + * + * Parses the local port, remote device name and the remote port. + * + * Returns : + * 1 - If the parsing is successful and a connection record + * was created for an output connection. + * 0 - If the parsing completed without any fatal errors. + * -Errno - Fatal error, abort the scanning. + */ +static int of_coresight_parse_endpoint(struct device *dev, + struct device_node *ep, + struct coresight_platform_data *pdata, + int i) +{ + int ret = 0; + struct of_endpoint endpoint, rendpoint; + struct device_node *rparent = NULL; + struct device_node *rport = NULL; + struct device *rdev = NULL; + + do { + /* Parse the local port details */ + if (of_graph_parse_endpoint(ep, &endpoint)) + break; + /* + * Get a handle on the remote port and parent + * attached to it. + */ + rparent = of_graph_get_remote_port_parent(ep); + if (!rparent) + break; + rport = of_graph_get_remote_port(ep); + if (!rport) + break; + if (of_graph_parse_endpoint(rport, &rendpoint)) + break; + + /* If the remote device is not available, defer probing */ + rdev = of_coresight_get_endpoint_device(rparent); + if (!rdev) { + ret = -EPROBE_DEFER; + break; + } + + pdata->outports[i] = endpoint.port; + pdata->child_names[i] = dev_name(rdev); + pdata->child_ports[i] = rendpoint.id; + /* Connection record updated */ + ret = 1; + } while (0); + + return ret; +} + struct coresight_platform_data * of_get_coresight_platform_data(struct device *dev, const struct device_node *node) { int i = 0, ret = 0; struct coresight_platform_data *pdata; - struct of_endpoint endpoint, rendpoint; - struct device *rdev; struct device_node *ep = NULL; - struct device_node *rparent = NULL; - struct device_node *rport = NULL; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -132,64 +185,39 @@ of_get_coresight_platform_data(struct device *dev, /* Use device name as sysfs handle */ pdata->name = dev_name(dev); + pdata->cpu = of_coresight_get_cpu(node); /* Get the number of input and output port for this component */ of_coresight_get_ports(node, &pdata->nr_inport, &pdata->nr_outport); - if (pdata->nr_outport) { - ret = of_coresight_alloc_memory(dev, pdata); - if (ret) + /* If there are no output connections, we are done */ + if (!pdata->nr_outport) + return pdata; + + ret = of_coresight_alloc_memory(dev, pdata); + if (ret) + return ERR_PTR(ret); + + /* Iterate through each port to discover topology */ + while ((ep = of_graph_get_next_endpoint(node, ep))) { + /* + * No need to deal with input ports, as processing the + * output ports connected to them will process the details. + */ + if (of_find_property(ep, "slave-mode", NULL)) + continue; + + ret = of_coresight_parse_endpoint(dev, ep, pdata, i); + switch (ret) { + case 1: + i++; /* Fall through */ + case 0: + break; + default: return ERR_PTR(ret); - - /* Iterate through each port to discover topology */ - do { - /* Get a handle on a port */ - ep = of_graph_get_next_endpoint(node, ep); - if (!ep) - break; - - /* - * No need to deal with input ports, processing for as - * processing for output ports will deal with them. - */ - if (of_find_property(ep, "slave-mode", NULL)) - continue; - - /* Get a handle on the local endpoint */ - ret = of_graph_parse_endpoint(ep, &endpoint); - - if (ret) - continue; - - /* The local out port number */ - pdata->outports[i] = endpoint.port; - - /* - * Get a handle on the remote port and parent - * attached to it. - */ - rparent = of_graph_get_remote_port_parent(ep); - rport = of_graph_get_remote_port(ep); - - if (!rparent || !rport) - continue; - - if (of_graph_parse_endpoint(rport, &rendpoint)) - continue; - - rdev = of_coresight_get_endpoint_device(rparent); - if (!rdev) - return ERR_PTR(-EPROBE_DEFER); - - pdata->child_names[i] = dev_name(rdev); - pdata->child_ports[i] = rendpoint.id; - - i++; - } while (ep); + } } - pdata->cpu = of_coresight_get_cpu(node); - return pdata; } EXPORT_SYMBOL_GPL(of_get_coresight_platform_data); -- cgit v1.2.3-58-ga151 From a0f9992c809fb73a05de1894734418a88178539f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:38 -0600 Subject: coresight: platform: Fix refcounting for graph nodes The coresight driver doesn't drop the references on the remote endpoint/port nodes. Add the missing of_node_put() calls. Reported-by: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 70205f3eae8e..28d3aef1660b 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -168,6 +168,11 @@ static int of_coresight_parse_endpoint(struct device *dev, ret = 1; } while (0); + if (rparent) + of_node_put(rparent); + if (rport) + of_node_put(rport); + return ret; } -- cgit v1.2.3-58-ga151 From 5111e749c775ebae6f7d39c6f836cb3f06c7b938 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:39 -0600 Subject: coresight: platform: Fix leaking device reference We don't drop the reference on the remote device while parsing the connection, held by bus_find_device(). Fix this by duplicating the device name and dropping the reference. Cc: Mathieu Poirier Cc: Kim Phillips Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 28d3aef1660b..4b279f8fea0c 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -162,7 +162,9 @@ static int of_coresight_parse_endpoint(struct device *dev, } pdata->outports[i] = endpoint.port; - pdata->child_names[i] = dev_name(rdev); + pdata->child_names[i] = devm_kstrdup(dev, + dev_name(rdev), + GFP_KERNEL); pdata->child_ports[i] = rendpoint.id; /* Connection record updated */ ret = 1; @@ -172,6 +174,8 @@ static int of_coresight_parse_endpoint(struct device *dev, of_node_put(rparent); if (rport) of_node_put(rport); + if (rdev) + put_device(rdev); return ret; } -- cgit v1.2.3-58-ga151 From 96330407f86abe8e5f0594734cff28b6196c94d7 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:40 -0600 Subject: coresight: Fix remote endpoint parsing When parsing the remote endpoint of an output port, we do : rport = of_graph_get_remote_port(ep); rparent = of_graph_get_remote_port_parent(ep); and then parse the "remote_port" as if it was the remote endpoint, which is wrong. The code worked fine because we used endpoint number as the port number. Let us fix it and optimise a bit as: remote_ep = of_graph_get_remote_endpoint(ep); if (remote_ep) remote_parent = of_graph_get_port_parent(remote_ep); and then, parse the remote_ep for the port/endpoint details. Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 4b279f8fea0c..2ecdd1432b5c 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -134,7 +134,7 @@ static int of_coresight_parse_endpoint(struct device *dev, int ret = 0; struct of_endpoint endpoint, rendpoint; struct device_node *rparent = NULL; - struct device_node *rport = NULL; + struct device_node *rep = NULL; struct device *rdev = NULL; do { @@ -142,16 +142,16 @@ static int of_coresight_parse_endpoint(struct device *dev, if (of_graph_parse_endpoint(ep, &endpoint)) break; /* - * Get a handle on the remote port and parent - * attached to it. + * Get a handle on the remote endpoint and the device it is + * attached to. */ - rparent = of_graph_get_remote_port_parent(ep); - if (!rparent) + rep = of_graph_get_remote_endpoint(ep); + if (!rep) break; - rport = of_graph_get_remote_port(ep); - if (!rport) + rparent = of_graph_get_port_parent(rep); + if (!rparent) break; - if (of_graph_parse_endpoint(rport, &rendpoint)) + if (of_graph_parse_endpoint(rep, &rendpoint)) break; /* If the remote device is not available, defer probing */ @@ -165,15 +165,15 @@ static int of_coresight_parse_endpoint(struct device *dev, pdata->child_names[i] = devm_kstrdup(dev, dev_name(rdev), GFP_KERNEL); - pdata->child_ports[i] = rendpoint.id; + pdata->child_ports[i] = rendpoint.port; /* Connection record updated */ ret = 1; } while (0); if (rparent) of_node_put(rparent); - if (rport) - of_node_put(rport); + if (rep) + of_node_put(rep); if (rdev) put_device(rdev); -- cgit v1.2.3-58-ga151 From 2058224f5415df895551ee8f8e48a2b26acfc20f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:41 -0600 Subject: coresight: Add helper to check if the endpoint is input Add a helper to check if the given endpoint is input. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 2ecdd1432b5c..44903d35009f 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -45,6 +45,11 @@ of_coresight_get_endpoint_device(struct device_node *endpoint) endpoint, of_dev_node_match); } +static inline bool of_coresight_ep_is_input(struct device_node *ep) +{ + return of_property_read_bool(ep, "slave-mode"); +} + static void of_coresight_get_ports(const struct device_node *node, int *nr_inport, int *nr_outport) { @@ -56,7 +61,7 @@ static void of_coresight_get_ports(const struct device_node *node, if (!ep) break; - if (of_property_read_bool(ep, "slave-mode")) + if (of_coresight_ep_is_input(ep)) in++; else out++; @@ -213,7 +218,7 @@ of_get_coresight_platform_data(struct device *dev, * No need to deal with input ports, as processing the * output ports connected to them will process the details. */ - if (of_find_property(ep, "slave-mode", NULL)) + if (of_coresight_ep_is_input(ep)) continue; ret = of_coresight_parse_endpoint(dev, ep, pdata, i); -- cgit v1.2.3-58-ga151 From c2c729415b2d21329104fecaa86878d295f1041f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:42 -0600 Subject: coresight: platform: Cleanup coresight connection handling The platform code parses the component connections and populates a platform-description of the output connections in arrays of fields (which is never freed). This is later copied in the coresight_register to a newly allocated area, represented by coresight_connection(s). This patch cleans up the code dealing with connections by making use of the "coresight_connection" structure right at the platform code and lets the generic driver simply re-use information provided by the platform. Thus making it reader friendly as well as avoiding the wastage of unused memory. Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 21 +----------- drivers/hwtracing/coresight/of_coresight.c | 53 +++++++++++------------------- include/linux/coresight.h | 9 ++--- 3 files changed, 22 insertions(+), 61 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 9fd0c387e678..5e8880ca8078 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -995,13 +995,11 @@ postcore_initcall(coresight_init); struct coresight_device *coresight_register(struct coresight_desc *desc) { - int i; int ret; int link_subtype; int nr_refcnts = 1; atomic_t *refcnts = NULL; struct coresight_device *csdev; - struct coresight_connection *conns = NULL; csdev = kzalloc(sizeof(*csdev), GFP_KERNEL); if (!csdev) { @@ -1030,22 +1028,7 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) csdev->nr_inport = desc->pdata->nr_inport; csdev->nr_outport = desc->pdata->nr_outport; - /* Initialise connections if there is at least one outport */ - if (csdev->nr_outport) { - conns = kcalloc(csdev->nr_outport, sizeof(*conns), GFP_KERNEL); - if (!conns) { - ret = -ENOMEM; - goto err_free_refcnts; - } - - for (i = 0; i < csdev->nr_outport; i++) { - conns[i].outport = desc->pdata->outports[i]; - conns[i].child_name = desc->pdata->child_names[i]; - conns[i].child_port = desc->pdata->child_ports[i]; - } - } - - csdev->conns = conns; + csdev->conns = desc->pdata->conns; csdev->type = desc->type; csdev->subtype = desc->subtype; @@ -1078,8 +1061,6 @@ struct coresight_device *coresight_register(struct coresight_desc *desc) return csdev; -err_free_refcnts: - kfree(refcnts); err_free_csdev: kfree(csdev); err_out: diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 44903d35009f..e8fb4e124744 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -75,29 +75,13 @@ static void of_coresight_get_ports(const struct device_node *node, static int of_coresight_alloc_memory(struct device *dev, struct coresight_platform_data *pdata) { - /* List of output port on this component */ - pdata->outports = devm_kcalloc(dev, - pdata->nr_outport, - sizeof(*pdata->outports), - GFP_KERNEL); - if (!pdata->outports) - return -ENOMEM; - - /* Children connected to this component via @outports */ - pdata->child_names = devm_kcalloc(dev, - pdata->nr_outport, - sizeof(*pdata->child_names), - GFP_KERNEL); - if (!pdata->child_names) - return -ENOMEM; - - /* Port number on the child this component is connected to */ - pdata->child_ports = devm_kcalloc(dev, - pdata->nr_outport, - sizeof(*pdata->child_ports), - GFP_KERNEL); - if (!pdata->child_ports) - return -ENOMEM; + if (pdata->nr_outport) { + pdata->conns = devm_kzalloc(dev, pdata->nr_outport * + sizeof(*pdata->conns), + GFP_KERNEL); + if (!pdata->conns) + return -ENOMEM; + } return 0; } @@ -121,7 +105,7 @@ EXPORT_SYMBOL_GPL(of_coresight_get_cpu); /* * of_coresight_parse_endpoint : Parse the given output endpoint @ep - * and fill the connection information in @pdata[@i]. + * and fill the connection information in @conn * * Parses the local port, remote device name and the remote port. * @@ -133,8 +117,7 @@ EXPORT_SYMBOL_GPL(of_coresight_get_cpu); */ static int of_coresight_parse_endpoint(struct device *dev, struct device_node *ep, - struct coresight_platform_data *pdata, - int i) + struct coresight_connection *conn) { int ret = 0; struct of_endpoint endpoint, rendpoint; @@ -166,11 +149,11 @@ static int of_coresight_parse_endpoint(struct device *dev, break; } - pdata->outports[i] = endpoint.port; - pdata->child_names[i] = devm_kstrdup(dev, - dev_name(rdev), - GFP_KERNEL); - pdata->child_ports[i] = rendpoint.port; + conn->outport = endpoint.port; + conn->child_name = devm_kstrdup(dev, + dev_name(rdev), + GFP_KERNEL); + conn->child_port = rendpoint.port; /* Connection record updated */ ret = 1; } while (0); @@ -189,8 +172,9 @@ struct coresight_platform_data * of_get_coresight_platform_data(struct device *dev, const struct device_node *node) { - int i = 0, ret = 0; + int ret = 0; struct coresight_platform_data *pdata; + struct coresight_connection *conn; struct device_node *ep = NULL; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -212,6 +196,7 @@ of_get_coresight_platform_data(struct device *dev, if (ret) return ERR_PTR(ret); + conn = pdata->conns; /* Iterate through each port to discover topology */ while ((ep = of_graph_get_next_endpoint(node, ep))) { /* @@ -221,10 +206,10 @@ of_get_coresight_platform_data(struct device *dev, if (of_coresight_ep_is_input(ep)) continue; - ret = of_coresight_parse_endpoint(dev, ep, pdata, i); + ret = of_coresight_parse_endpoint(dev, ep, conn); switch (ret) { case 1: - i++; /* Fall through */ + conn++; /* Fall through */ case 0: break; default: diff --git a/include/linux/coresight.h b/include/linux/coresight.h index d828a6efe0b1..41e1f4333bf2 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -94,20 +94,15 @@ union coresight_dev_subtype { * @cpu: the CPU a source belongs to. Only applicable for ETM/PTMs. * @name: name of the component as shown under sysfs. * @nr_inport: number of input ports for this component. - * @outports: list of remote endpoint port number. - * @child_names:name of all child components connected to this device. - * @child_ports:child component port number the current component is - connected to. * @nr_outport: number of output ports for this component. + * @conns: Array of nr_outport connections from this component */ struct coresight_platform_data { int cpu; const char *name; int nr_inport; - int *outports; - const char **child_names; - int *child_ports; int nr_outport; + struct coresight_connection *conns; }; /** -- cgit v1.2.3-58-ga151 From 5ecc7120194c6f10c09197e368b378c699100123 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:43 -0600 Subject: coresight: Cleanup coresight DT bindings The coresight drivers relied on default bindings for graph in DT, while reusing the "reg" field of the "ports" to indicate the actual hardware port number for the connections. This can cause duplicate ports with same addresses, but different direction. However, with the rules getting stricter for the address mismatch with the label, it is no longer possible to use the port address field for the hardware port number. This patch introduces new DT binding rules for coresight components, based on the same generic DT graph bindings, but avoiding the address duplication. - All output ports must be specified under a child node with name "out-ports". - All input ports must be specified under a childe node with name "in-ports". - Port address should match the hardware port number. The support for legacy bindings is retained, with a warning. Cc: Sudeep Holla Cc: Rob Herring Signed-off-by: Suzuki K Poulose Reviewed-by: Rob Herring Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/arm/coresight.txt | 95 ++++++++++++--------- drivers/hwtracing/coresight/of_coresight.c | 98 +++++++++++++++++++--- 2 files changed, 143 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 5d1ad09bafb4..f39d2c6eb49c 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -54,9 +54,7 @@ its hardware characteristcs. clocks the core of that coresight component. The latter clock is optional. - * port or ports: The representation of the component's port - layout using the generic DT graph presentation found in - "bindings/graph.txt". + * port or ports: see "Graph bindings for Coresight" below. * Additional required properties for System Trace Macrocells (STM): * reg: along with the physical base address and length of the register @@ -73,7 +71,7 @@ its hardware characteristcs. AMBA markee): - "arm,coresight-replicator" - * port or ports: same as above. + * port or ports: see "Graph bindings for Coresight" below. * Optional properties for ETM/PTMs: @@ -96,6 +94,20 @@ its hardware characteristcs. * interrupts : Exactly one SPI may be listed for reporting the address error +Graph bindings for Coresight +------------------------------- + +Coresight components are interconnected to create a data path for the flow of +trace data generated from the "sources" to their collection points "sink". +Each coresight component must describe the "input" and "output" connections. +The connections must be described via generic DT graph bindings as described +by the "bindings/graph.txt", where each "port" along with an "endpoint" +component represents a hardware port and the connection. + + * All output ports must be listed inside a child node named "out-ports" + * All input ports must be listed inside a child node named "in-ports". + * Port address must match the hardware port number. + Example: 1. Sinks @@ -105,10 +117,11 @@ Example: clocks = <&oscclk6a>; clock-names = "apb_pclk"; - port { - etb_in_port: endpoint@0 { - slave-mode; - remote-endpoint = <&replicator_out_port0>; + in-ports { + port { + etb_in_port: endpoint@0 { + remote-endpoint = <&replicator_out_port0>; + }; }; }; }; @@ -119,10 +132,11 @@ Example: clocks = <&oscclk6a>; clock-names = "apb_pclk"; - port { - tpiu_in_port: endpoint@0 { - slave-mode; - remote-endpoint = <&replicator_out_port1>; + out-ports { + port { + tpiu_in_port: endpoint@0 { + remote-endpoint = <&replicator_out_port1>; + }; }; }; }; @@ -163,7 +177,7 @@ Example: */ compatible = "arm,coresight-replicator"; - ports { + out-ports { #address-cells = <1>; #size-cells = <0>; @@ -181,12 +195,11 @@ Example: remote-endpoint = <&tpiu_in_port>; }; }; + }; - /* replicator input port */ - port@2 { - reg = <0>; + in-ports { + port { replicator_in_port0: endpoint { - slave-mode; remote-endpoint = <&funnel_out_port0>; }; }; @@ -199,40 +212,36 @@ Example: clocks = <&oscclk6a>; clock-names = "apb_pclk"; - ports { - #address-cells = <1>; - #size-cells = <0>; - - /* funnel output port */ - port@0 { - reg = <0>; + out-ports { + port { funnel_out_port0: endpoint { remote-endpoint = <&replicator_in_port0>; }; }; + }; - /* funnel input ports */ - port@1 { + in-ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { reg = <0>; funnel_in_port0: endpoint { - slave-mode; remote-endpoint = <&ptm0_out_port>; }; }; - port@2 { + port@1 { reg = <1>; funnel_in_port1: endpoint { - slave-mode; remote-endpoint = <&ptm1_out_port>; }; }; - port@3 { + port@2 { reg = <2>; funnel_in_port2: endpoint { - slave-mode; remote-endpoint = <&etm0_out_port>; }; }; @@ -248,9 +257,11 @@ Example: cpu = <&cpu0>; clocks = <&oscclk6a>; clock-names = "apb_pclk"; - port { - ptm0_out_port: endpoint { - remote-endpoint = <&funnel_in_port0>; + out-ports { + port { + ptm0_out_port: endpoint { + remote-endpoint = <&funnel_in_port0>; + }; }; }; }; @@ -262,9 +273,11 @@ Example: cpu = <&cpu1>; clocks = <&oscclk6a>; clock-names = "apb_pclk"; - port { - ptm1_out_port: endpoint { - remote-endpoint = <&funnel_in_port1>; + out-ports { + port { + ptm1_out_port: endpoint { + remote-endpoint = <&funnel_in_port1>; + }; }; }; }; @@ -278,9 +291,11 @@ Example: clocks = <&soc_smc50mhz>; clock-names = "apb_pclk"; - port { - stm_out_port: endpoint { - remote-endpoint = <&main_funnel_in_port2>; + out-ports { + port { + stm_out_port: endpoint { + remote-endpoint = <&main_funnel_in_port2>; + }; }; }; }; diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index e8fb4e124744..da71c975e3f7 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -45,13 +45,13 @@ of_coresight_get_endpoint_device(struct device_node *endpoint) endpoint, of_dev_node_match); } -static inline bool of_coresight_ep_is_input(struct device_node *ep) +static inline bool of_coresight_legacy_ep_is_input(struct device_node *ep) { return of_property_read_bool(ep, "slave-mode"); } -static void of_coresight_get_ports(const struct device_node *node, - int *nr_inport, int *nr_outport) +static void of_coresight_get_ports_legacy(const struct device_node *node, + int *nr_inport, int *nr_outport) { struct device_node *ep = NULL; int in = 0, out = 0; @@ -61,7 +61,7 @@ static void of_coresight_get_ports(const struct device_node *node, if (!ep) break; - if (of_coresight_ep_is_input(ep)) + if (of_coresight_legacy_ep_is_input(ep)) in++; else out++; @@ -72,6 +72,67 @@ static void of_coresight_get_ports(const struct device_node *node, *nr_outport = out; } +static struct device_node *of_coresight_get_port_parent(struct device_node *ep) +{ + struct device_node *parent = of_graph_get_port_parent(ep); + + /* + * Skip one-level up to the real device node, if we + * are using the new bindings. + */ + if (!of_node_cmp(parent->name, "in-ports") || + !of_node_cmp(parent->name, "out-ports")) + parent = of_get_next_parent(parent); + + return parent; +} + +static inline struct device_node * +of_coresight_get_input_ports_node(const struct device_node *node) +{ + return of_get_child_by_name(node, "in-ports"); +} + +static inline struct device_node * +of_coresight_get_output_ports_node(const struct device_node *node) +{ + return of_get_child_by_name(node, "out-ports"); +} + +static inline int +of_coresight_count_ports(struct device_node *port_parent) +{ + int i = 0; + struct device_node *ep = NULL; + + while ((ep = of_graph_get_next_endpoint(port_parent, ep))) + i++; + return i; +} + +static void of_coresight_get_ports(const struct device_node *node, + int *nr_inport, int *nr_outport) +{ + struct device_node *input_ports = NULL, *output_ports = NULL; + + input_ports = of_coresight_get_input_ports_node(node); + output_ports = of_coresight_get_output_ports_node(node); + + if (input_ports || output_ports) { + if (input_ports) { + *nr_inport = of_coresight_count_ports(input_ports); + of_node_put(input_ports); + } + if (output_ports) { + *nr_outport = of_coresight_count_ports(output_ports); + of_node_put(output_ports); + } + } else { + /* Fall back to legacy DT bindings parsing */ + of_coresight_get_ports_legacy(node, nr_inport, nr_outport); + } +} + static int of_coresight_alloc_memory(struct device *dev, struct coresight_platform_data *pdata) { @@ -136,7 +197,7 @@ static int of_coresight_parse_endpoint(struct device *dev, rep = of_graph_get_remote_endpoint(ep); if (!rep) break; - rparent = of_graph_get_port_parent(rep); + rparent = of_coresight_get_port_parent(rep); if (!rparent) break; if (of_graph_parse_endpoint(rep, &rendpoint)) @@ -176,6 +237,8 @@ of_get_coresight_platform_data(struct device *dev, struct coresight_platform_data *pdata; struct coresight_connection *conn; struct device_node *ep = NULL; + const struct device_node *parent = NULL; + bool legacy_binding = false; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -196,14 +259,29 @@ of_get_coresight_platform_data(struct device *dev, if (ret) return ERR_PTR(ret); + parent = of_coresight_get_output_ports_node(node); + /* + * If the DT uses obsoleted bindings, the ports are listed + * under the device and we need to filter out the input + * ports. + */ + if (!parent) { + legacy_binding = true; + parent = node; + dev_warn_once(dev, "Uses obsolete Coresight DT bindings\n"); + } + conn = pdata->conns; - /* Iterate through each port to discover topology */ - while ((ep = of_graph_get_next_endpoint(node, ep))) { + + /* Iterate through each output port to discover topology */ + while ((ep = of_graph_get_next_endpoint(parent, ep))) { /* - * No need to deal with input ports, as processing the - * output ports connected to them will process the details. + * Legacy binding mixes input/output ports under the + * same parent. So, skip the input ports if we are dealing + * with legacy binding, as they processed with their + * connected output ports. */ - if (of_coresight_ep_is_input(ep)) + if (legacy_binding && of_coresight_legacy_ep_is_input(ep)) continue; ret = of_coresight_parse_endpoint(dev, ep, conn); -- cgit v1.2.3-58-ga151 From bbd35ba6fab5419e58e96f35f1431f13bdc14f98 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Thu, 20 Sep 2018 13:17:44 -0600 Subject: coresight: Use ERR_CAST instead of ERR_PTR Use ERR_CAT inlined function to replace the ERR_PTR(PTR_ERR). It make the code more concise. Signed-off-by: zhong jiang Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 2eda5de304c2..11963647e19a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -536,7 +536,7 @@ tmc_init_etr_sg_table(struct device *dev, int node, sg_table = tmc_alloc_sg_table(dev, node, nr_tpages, nr_dpages, pages); if (IS_ERR(sg_table)) { kfree(etr_table); - return ERR_PTR(PTR_ERR(sg_table)); + return ERR_CAST(sg_table); } etr_table->sg_table = sg_table; -- cgit v1.2.3-58-ga151 From c71369de02b285d9da526a526d8f2affc7b17c59 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:45 -0600 Subject: coresight: Fix handling of sinks The coresight components could be operated either in sysfs mode or in perf mode. For some of the components, the mode of operation doesn't matter as they simply relay the data to the next component in the trace path. But for sinks, they need to be able to provide the trace data back to the user. Thus we need to make sure that "mode" is handled appropriately. e.g, the sysfs mode could have multiple sources driving the trace data, while perf mode doesn't allow sharing the sink. The coresight_enable_sink() however doesn't really allow this check to trigger as it skips the "enable_sink" callback if the component is already enabled, irrespective of the mode. This could cause mixing of data from different modes or even same mode (in perf), if the sources are different. Also, if we fail to enable the sink while enabling a path (where sink is the first component enabled), we could end up in disabling the components in the "entire" path which were not enabled in this trial, causing disruptions in the existing trace paths. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 5e8880ca8078..07382c55b31d 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -132,12 +132,14 @@ static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) { int ret; - if (!csdev->enable) { - if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev, mode); - if (ret) - return ret; - } + /* + * We need to make sure the "new" session is compatible with the + * existing "mode" of operation. + */ + if (sink_ops(csdev)->enable) { + ret = sink_ops(csdev)->enable(csdev, mode); + if (ret) + return ret; csdev->enable = true; } @@ -339,8 +341,14 @@ int coresight_enable_path(struct list_head *path, u32 mode) switch (type) { case CORESIGHT_DEV_TYPE_SINK: ret = coresight_enable_sink(csdev, mode); + /* + * Sink is the first component turned on. If we + * failed to enable the sink, there are no components + * that need disabling. Disabling the path here + * would mean we could disrupt an existing session. + */ if (ret) - goto err; + goto out; break; case CORESIGHT_DEV_TYPE_SOURCE: /* sources are enabled from either sysFS or Perf */ -- cgit v1.2.3-58-ga151 From 987d1e8dcd370d96029a3d76a0031b043c4a69ae Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:46 -0600 Subject: coresight: etb10: Fix handling of perf mode If the ETB is already enabled in sysfs mode, the ETB reports success even if a perf mode is requested. Fix this by checking the requested mode. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 306119eaf16a..0dad8626bcfb 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -147,6 +147,10 @@ static int etb_enable(struct coresight_device *csdev, u32 mode) if (val == CS_MODE_PERF) return -EBUSY; + /* Don't let perf disturb sysFS sessions */ + if (val == CS_MODE_SYSFS && mode == CS_MODE_PERF) + return -EBUSY; + /* Nothing to do, the tracer is already enabled. */ if (val == CS_MODE_SYSFS) goto out; -- cgit v1.2.3-58-ga151 From 5ecabe4a76e8cdb61fa3e24862d9ca240a1c4ddf Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:47 -0600 Subject: coresight: perf: Fix per cpu path management We create a coresight trace path for each online CPU when we start the event. We rely on the number of online CPUs and then go on to allocate an array matching the "number of online CPUs" for holding the path and then uses normal CPU id as the index to the array. This is problematic as we could have some offline CPUs causing us to access beyond the actual array size (e.g, on a dual SMP system, if CPU0 is offline, CPU1 could be really accessing beyond the array). The solution is to switch to per-cpu array for holding the path. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 55 +++++++++++++++++------- 1 file changed, 40 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 677695635211..6338dd180031 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,7 @@ struct etm_event_data { struct work_struct work; cpumask_t mask; void *snk_config; - struct list_head **path; + struct list_head * __percpu *path; }; static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle); @@ -61,6 +62,18 @@ static const struct attribute_group *etm_pmu_attr_groups[] = { NULL, }; +static inline struct list_head ** +etm_event_cpu_path_ptr(struct etm_event_data *data, int cpu) +{ + return per_cpu_ptr(data->path, cpu); +} + +static inline struct list_head * +etm_event_cpu_path(struct etm_event_data *data, int cpu) +{ + return *etm_event_cpu_path_ptr(data, cpu); +} + static void etm_event_read(struct perf_event *event) {} static int etm_addr_filters_alloc(struct perf_event *event) @@ -120,23 +133,26 @@ static void free_event_data(struct work_struct *work) */ if (event_data->snk_config) { cpu = cpumask_first(mask); - sink = coresight_get_sink(event_data->path[cpu]); + sink = coresight_get_sink(etm_event_cpu_path(event_data, cpu)); if (sink_ops(sink)->free_buffer) sink_ops(sink)->free_buffer(event_data->snk_config); } for_each_cpu(cpu, mask) { - if (!(IS_ERR_OR_NULL(event_data->path[cpu]))) - coresight_release_path(event_data->path[cpu]); + struct list_head **ppath; + + ppath = etm_event_cpu_path_ptr(event_data, cpu); + if (!(IS_ERR_OR_NULL(*ppath))) + coresight_release_path(*ppath); + *ppath = NULL; } - kfree(event_data->path); + free_percpu(event_data->path); kfree(event_data); } static void *alloc_event_data(int cpu) { - int size; cpumask_t *mask; struct etm_event_data *event_data; @@ -147,7 +163,6 @@ static void *alloc_event_data(int cpu) /* Make sure nothing disappears under us */ get_online_cpus(); - size = num_online_cpus(); mask = &event_data->mask; if (cpu != -1) @@ -164,8 +179,8 @@ static void *alloc_event_data(int cpu) * unused memory when dealing with single CPU trace scenarios is small * compared to the cost of searching through an optimized array. */ - event_data->path = kcalloc(size, - sizeof(struct list_head *), GFP_KERNEL); + event_data->path = alloc_percpu(struct list_head *); + if (!event_data->path) { kfree(event_data); return NULL; @@ -213,6 +228,7 @@ static void *etm_setup_aux(int event_cpu, void **pages, /* Setup the path for each CPU in a trace session */ for_each_cpu(cpu, mask) { + struct list_head *path; struct coresight_device *csdev; csdev = per_cpu(csdev_src, cpu); @@ -224,9 +240,11 @@ static void *etm_setup_aux(int event_cpu, void **pages, * list of devices from source to sink that can be * referenced later when the path is actually needed. */ - event_data->path[cpu] = coresight_build_path(csdev, sink); - if (IS_ERR(event_data->path[cpu])) + path = coresight_build_path(csdev, sink); + if (IS_ERR(path)) goto err; + + *etm_event_cpu_path_ptr(event_data, cpu) = path; } if (!sink_ops(sink)->alloc_buffer) @@ -255,6 +273,7 @@ static void etm_event_start(struct perf_event *event, int flags) struct etm_event_data *event_data; struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + struct list_head *path; if (!csdev) goto fail; @@ -267,8 +286,9 @@ static void etm_event_start(struct perf_event *event, int flags) if (!event_data) goto fail; + path = etm_event_cpu_path(event_data, cpu); /* We need a sink, no need to continue without one */ - sink = coresight_get_sink(event_data->path[cpu]); + sink = coresight_get_sink(path); if (WARN_ON_ONCE(!sink || !sink_ops(sink)->set_buffer)) goto fail_end_stop; @@ -278,7 +298,7 @@ static void etm_event_start(struct perf_event *event, int flags) goto fail_end_stop; /* Nothing will happen without a path */ - if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF)) + if (coresight_enable_path(path, CS_MODE_PERF)) goto fail_end_stop; /* Tell the perf core the event is alive */ @@ -306,6 +326,7 @@ static void etm_event_stop(struct perf_event *event, int mode) struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); struct etm_event_data *event_data = perf_get_aux(handle); + struct list_head *path; if (event->hw.state == PERF_HES_STOPPED) return; @@ -313,7 +334,11 @@ static void etm_event_stop(struct perf_event *event, int mode) if (!csdev) return; - sink = coresight_get_sink(event_data->path[cpu]); + path = etm_event_cpu_path(event_data, cpu); + if (!path) + return; + + sink = coresight_get_sink(path); if (!sink) return; @@ -344,7 +369,7 @@ static void etm_event_stop(struct perf_event *event, int mode) } /* Disabling the path make its elements available to other sessions */ - coresight_disable_path(event_data->path[cpu]); + coresight_disable_path(path); } static int etm_event_add(struct perf_event *event, int mode) -- cgit v1.2.3-58-ga151 From c48fb3bbe912a295e5b75eaabaf39874d5b9b773 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:48 -0600 Subject: coresight: perf: Avoid unncessary CPU hotplug read lock We hold the read lock on CPU hotplug to simply copy the online mask, which is not really needed. And this can cause a lockdep warning, like : [ 54.632093] ====================================================== [ 54.638207] WARNING: possible circular locking dependency detected [ 54.644322] 4.18.0-rc3-00042-g2d39e6356bb7-dirty #309 Not tainted [ 54.650350] ------------------------------------------------------ [ 54.656464] perf/2862 is trying to acquire lock: [ 54.661031] 000000007e21d170 (&event->mmap_mutex){+.+.}, at: perf_event_set_output+0x98/0x138 [ 54.669486] [ 54.669486] but task is already holding lock: [ 54.675256] 000000001080eb1b (&cpuctx_mutex){+.+.}, at: perf_event_ctx_lock_nested+0xf8/0x1f0 [ 54.683704] [ 54.683704] which lock already depends on the new lock. [ 54.683704] [ 54.691797] [ 54.691797] the existing dependency chain (in reverse order) is: [ 54.699201] [ 54.699201] -> #3 (&cpuctx_mutex){+.+.}: [ 54.704556] __mutex_lock+0x70/0x808 [ 54.708608] mutex_lock_nested+0x1c/0x28 [ 54.713005] perf_event_init_cpu+0x8c/0xd8 [ 54.717574] perf_event_init+0x194/0x1d4 [ 54.721971] start_kernel+0x2b8/0x42c [ 54.726107] [ 54.726107] -> #2 (pmus_lock){+.+.}: [ 54.731114] __mutex_lock+0x70/0x808 [ 54.735165] mutex_lock_nested+0x1c/0x28 [ 54.739560] perf_event_init_cpu+0x30/0xd8 [ 54.744129] cpuhp_invoke_callback+0x84/0x248 [ 54.748954] _cpu_up+0xe8/0x1c8 [ 54.752576] do_cpu_up+0xa8/0xc8 [ 54.756283] cpu_up+0x10/0x18 [ 54.759731] smp_init+0xa0/0x114 [ 54.763438] kernel_init_freeable+0x120/0x288 [ 54.768264] kernel_init+0x10/0x108 [ 54.772230] ret_from_fork+0x10/0x18 [ 54.776279] [ 54.776279] -> #1 (cpu_hotplug_lock.rw_sem){++++}: [ 54.782492] cpus_read_lock+0x34/0xb0 [ 54.786631] etm_setup_aux+0x5c/0x308 [ 54.790769] rb_alloc_aux+0x1ec/0x300 [ 54.794906] perf_mmap+0x284/0x610 [ 54.798787] mmap_region+0x388/0x570 [ 54.802838] do_mmap+0x344/0x4f8 [ 54.806544] vm_mmap_pgoff+0xe4/0x110 [ 54.810682] ksys_mmap_pgoff+0xa8/0x240 [ 54.814992] sys_mmap+0x18/0x28 [ 54.818613] el0_svc_naked+0x30/0x34 [ 54.822661] [ 54.822661] -> #0 (&event->mmap_mutex){+.+.}: [ 54.828445] lock_acquire+0x48/0x68 [ 54.832409] __mutex_lock+0x70/0x808 [ 54.836459] mutex_lock_nested+0x1c/0x28 [ 54.840855] perf_event_set_output+0x98/0x138 [ 54.845680] _perf_ioctl+0x2a0/0x6a0 [ 54.849731] perf_ioctl+0x3c/0x68 [ 54.853526] do_vfs_ioctl+0xb8/0xa20 [ 54.857577] ksys_ioctl+0x80/0xb8 [ 54.861370] sys_ioctl+0xc/0x18 [ 54.864990] el0_svc_naked+0x30/0x34 [ 54.869039] [ 54.869039] other info that might help us debug this: [ 54.869039] [ 54.876960] Chain exists of: [ 54.876960] &event->mmap_mutex --> pmus_lock --> &cpuctx_mutex [ 54.876960] [ 54.887217] Possible unsafe locking scenario: [ 54.887217] [ 54.893073] CPU0 CPU1 [ 54.897552] ---- ---- [ 54.902030] lock(&cpuctx_mutex); [ 54.905396] lock(pmus_lock); [ 54.910911] lock(&cpuctx_mutex); [ 54.916770] lock(&event->mmap_mutex); [ 54.920566] [ 54.920566] *** DEADLOCK *** [ 54.920566] [ 54.926424] 1 lock held by perf/2862: [ 54.930042] #0: 000000001080eb1b (&cpuctx_mutex){+.+.}, at: perf_event_ctx_lock_nested+0xf8/0x1f0 Since we have per-cpu array for the paths, we simply don't care about the number of online CPUs. This patch gets rid of the {get/put}_online_cpus(). Reported-by: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 6338dd180031..6beb662d230c 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -161,15 +161,12 @@ static void *alloc_event_data(int cpu) if (!event_data) return NULL; - /* Make sure nothing disappears under us */ - get_online_cpus(); mask = &event_data->mask; if (cpu != -1) cpumask_set_cpu(cpu, mask); else cpumask_copy(mask, cpu_online_mask); - put_online_cpus(); /* * Each CPU has a single path between source and destination. As such -- cgit v1.2.3-58-ga151 From f9d81a657bb833ef030a092c50230359dfef4648 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:49 -0600 Subject: coresight: perf: Allow tracing on hotplugged CPUs At the moment, if there is no CPU specified for a given event, we use cpu_online_mask and try to build path for each of the CPUs in the mask. This could prevent any CPU that is turned online later to be used for the tracing. This patch changes to use the cpu_present_mask and tries to build path for as much CPUs as possible ignoring the failures in building path for some of the CPUs. If ever we try to trace on those CPUs, we fail the operation. Based on a patch from Mathieu Poirier. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 44 ++++++++++++++++-------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 6beb662d230c..afe7e7fc1a93 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -127,11 +127,9 @@ static void free_event_data(struct work_struct *work) event_data = container_of(work, struct etm_event_data, work); mask = &event_data->mask; - /* - * First deal with the sink configuration. See comment in - * etm_setup_aux() about why we take the first available path. - */ - if (event_data->snk_config) { + + /* Free the sink buffers, if there are any */ + if (event_data->snk_config && !WARN_ON(cpumask_empty(mask))) { cpu = cpumask_first(mask); sink = coresight_get_sink(etm_event_cpu_path(event_data, cpu)); if (sink_ops(sink)->free_buffer) @@ -166,7 +164,7 @@ static void *alloc_event_data(int cpu) if (cpu != -1) cpumask_set_cpu(cpu, mask); else - cpumask_copy(mask, cpu_online_mask); + cpumask_copy(mask, cpu_present_mask); /* * Each CPU has a single path between source and destination. As such @@ -218,19 +216,32 @@ static void *etm_setup_aux(int event_cpu, void **pages, * on the cmd line. As such the "enable_sink" flag in sysFS is reset. */ sink = coresight_get_enabled_sink(true); - if (!sink) + if (!sink || !sink_ops(sink)->alloc_buffer) goto err; mask = &event_data->mask; - /* Setup the path for each CPU in a trace session */ + /* + * Setup the path for each CPU in a trace session. We try to build + * trace path for each CPU in the mask. If we don't find an ETM + * for the CPU or fail to build a path, we clear the CPU from the + * mask and continue with the rest. If ever we try to trace on those + * CPUs, we can handle it and fail the session. + */ for_each_cpu(cpu, mask) { struct list_head *path; struct coresight_device *csdev; csdev = per_cpu(csdev_src, cpu); - if (!csdev) - goto err; + /* + * If there is no ETM associated with this CPU clear it from + * the mask and continue with the rest. If ever we try to trace + * on this CPU, we handle it accordingly. + */ + if (!csdev) { + cpumask_clear_cpu(cpu, mask); + continue; + } /* * Building a path doesn't enable it, it simply builds a @@ -238,17 +249,20 @@ static void *etm_setup_aux(int event_cpu, void **pages, * referenced later when the path is actually needed. */ path = coresight_build_path(csdev, sink); - if (IS_ERR(path)) - goto err; + if (IS_ERR(path)) { + cpumask_clear_cpu(cpu, mask); + continue; + } *etm_event_cpu_path_ptr(event_data, cpu) = path; } - if (!sink_ops(sink)->alloc_buffer) + /* If we don't have any CPUs ready for tracing, abort */ + cpu = cpumask_first(mask); + if (cpu >= nr_cpu_ids) goto err; - cpu = cpumask_first(mask); - /* Get the AUX specific data from the sink buffer */ + /* Allocate the sink buffer for this session */ event_data->snk_config = sink_ops(sink)->alloc_buffer(sink, cpu, pages, nr_pages, overwrite); -- cgit v1.2.3-58-ga151 From 4f8ef21007531c3d7cb5b826e7b2c8999b65ecae Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:50 -0600 Subject: coresight: perf: Disable trace path upon source error We enable the trace path, before activating the source. If we fail to enable the source, we must disable the path to make sure it is available for another session. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index afe7e7fc1a93..6db76ce6ba5f 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -317,11 +317,13 @@ static void etm_event_start(struct perf_event *event, int flags) /* Finally enable the tracer */ if (source_ops(csdev)->enable(csdev, event, CS_MODE_PERF)) - goto fail_end_stop; + goto fail_disable_path; out: return; +fail_disable_path: + coresight_disable_path(path); fail_end_stop: perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); perf_aux_output_end(handle, 0); -- cgit v1.2.3-58-ga151 From 96a7f644006ecc05eaaa1a5d09373d0ee63beb0a Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:51 -0600 Subject: coresight: tmc-etr: Handle driver mode specific ETR buffers Since the ETR could be driven either by SYSFS or by perf, it becomes complicated how we deal with the buffers used for each of these modes. The ETR driver cannot simply free the current attached buffer without knowing the provider (i.e, sysfs vs perf). To solve this issue, we provide: 1) the driver-mode specific etr buffer to be retained in the drvdata 2) the etr_buf for a session should be passed on when enabling the hardware, which will be stored in drvdata->etr_buf. This will be replaced (not free'd) as soon as the hardware is disabled, after necessary sync operation. The advantages of this are : 1) The common code path doesn't need to worry about how to dispose an existing buffer, if it is about to start a new session with a different buffer, possibly in a different mode. 2) The driver mode can control its buffers and can get access to the saved session even when the hardware is operating in a different mode. (e.g, we can still access a trace buffer from a sysfs mode even if the etr is now used in perf mode, without disrupting the current session.) Towards this, we introduce a sysfs specific data which will hold the etr_buf used for sysfs mode of operation, controlled solely by the sysfs mode handling code. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 58 ++++++++++++++++--------- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 2 files changed, 40 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 11963647e19a..2d6f428176ff 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -895,10 +895,15 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); } -static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) +static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata, + struct etr_buf *etr_buf) { u32 axictl, sts; - struct etr_buf *etr_buf = drvdata->etr_buf; + + /* Callers should provide an appropriate buffer for use */ + if (WARN_ON(!etr_buf || drvdata->etr_buf)) + return; + drvdata->etr_buf = etr_buf; /* * If this ETR is connected to a CATU, enable it before we turn @@ -960,13 +965,16 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) * also updating the @bufpp on where to find it. Since the trace data * starts at anywhere in the buffer, depending on the RRP, we adjust the * @len returned to handle buffer wrapping around. + * + * We are protected here by drvdata->reading != 0, which ensures the + * sysfs_buf stays alive. */ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata, loff_t pos, size_t len, char **bufpp) { s64 offset; ssize_t actual = len; - struct etr_buf *etr_buf = drvdata->etr_buf; + struct etr_buf *etr_buf = drvdata->sysfs_buf; if (pos + actual > etr_buf->len) actual = etr_buf->len - pos; @@ -996,7 +1004,14 @@ tmc_etr_free_sysfs_buf(struct etr_buf *buf) static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) { - tmc_sync_etr_buf(drvdata); + struct etr_buf *etr_buf = drvdata->etr_buf; + + if (WARN_ON(drvdata->sysfs_buf != etr_buf)) { + tmc_etr_free_sysfs_buf(drvdata->sysfs_buf); + drvdata->sysfs_buf = NULL; + } else { + tmc_sync_etr_buf(drvdata); + } } static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) @@ -1017,6 +1032,8 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) /* Disable CATU device if this ETR is connected to one */ tmc_etr_disable_catu(drvdata); + /* Reset the ETR buf used by hardware */ + drvdata->etr_buf = NULL; } static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) @@ -1024,7 +1041,7 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) int ret = 0; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - struct etr_buf *new_buf = NULL, *free_buf = NULL; + struct etr_buf *sysfs_buf = NULL, *new_buf = NULL, *free_buf = NULL; /* * If we are enabling the ETR from disabled state, we need to make @@ -1035,7 +1052,8 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) * with the lock released. */ spin_lock_irqsave(&drvdata->spinlock, flags); - if (!drvdata->etr_buf || (drvdata->etr_buf->size != drvdata->size)) { + sysfs_buf = READ_ONCE(drvdata->sysfs_buf); + if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) { spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Allocate memory with the locks released */ @@ -1064,14 +1082,14 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) * If we don't have a buffer or it doesn't match the requested size, * use the buffer allocated above. Otherwise reuse the existing buffer. */ - if (!drvdata->etr_buf || - (new_buf && drvdata->etr_buf->size != new_buf->size)) { - free_buf = drvdata->etr_buf; - drvdata->etr_buf = new_buf; + sysfs_buf = READ_ONCE(drvdata->sysfs_buf); + if (!sysfs_buf || (new_buf && sysfs_buf->size != new_buf->size)) { + free_buf = sysfs_buf; + drvdata->sysfs_buf = new_buf; } drvdata->mode = CS_MODE_SYSFS; - tmc_etr_enable_hw(drvdata); + tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -1156,13 +1174,13 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) goto out; } - /* If drvdata::etr_buf is NULL the trace data has been read already */ - if (drvdata->etr_buf == NULL) { + /* If sysfs_buf is NULL the trace data has been read already */ + if (!drvdata->sysfs_buf) { ret = -EINVAL; goto out; } - /* Disable the TMC if need be */ + /* Disable the TMC if we are trying to read from a running session */ if (drvdata->mode == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); @@ -1176,7 +1194,7 @@ out: int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) { unsigned long flags; - struct etr_buf *etr_buf = NULL; + struct etr_buf *sysfs_buf = NULL; /* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) @@ -1191,22 +1209,22 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) * buffer. Since the tracer is still enabled drvdata::buf can't * be NULL. */ - tmc_etr_enable_hw(drvdata); + tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf); } else { /* * The ETR is not tracing and the buffer was just read. * As such prepare to free the trace buffer. */ - etr_buf = drvdata->etr_buf; - drvdata->etr_buf = NULL; + sysfs_buf = drvdata->sysfs_buf; + drvdata->sysfs_buf = NULL; } drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Free allocated memory out side of the spinlock */ - if (etr_buf) - tmc_free_etr_buf(etr_buf); + if (sysfs_buf) + tmc_etr_free_sysfs_buf(sysfs_buf); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 7027bd60c4cc..872f63e3651b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -170,6 +170,7 @@ struct etr_buf { * @trigger_cntr: amount of words to store after a trigger. * @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the * device configuration register (DEVID) + * @sysfs_data: SYSFS buffer for ETR. */ struct tmc_drvdata { void __iomem *base; @@ -189,6 +190,7 @@ struct tmc_drvdata { enum tmc_mem_intf_width memwidth; u32 trigger_cntr; u32 etr_caps; + struct etr_buf *sysfs_buf; }; struct etr_buf_operations { -- cgit v1.2.3-58-ga151 From cad5f8d399bbe3c4ed61a21f649d61b09f6efb7b Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:52 -0600 Subject: coresight: tmc-etr: Relax collection of trace from sysfs mode Since the ETR now uses mode specific buffers, we can reliably provide the trace data captured in sysfs mode, even when the ETR is operating in PERF mode. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 2d6f428176ff..bafd73e71c4c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1168,19 +1168,17 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) goto out; } - /* Don't interfere if operated from Perf */ - if (drvdata->mode == CS_MODE_PERF) { - ret = -EINVAL; - goto out; - } - - /* If sysfs_buf is NULL the trace data has been read already */ + /* + * We can safely allow reads even if the ETR is operating in PERF mode, + * since the sysfs session is captured in mode specific data. + * If drvdata::sysfs_data is NULL the trace data has been read already. + */ if (!drvdata->sysfs_buf) { ret = -EINVAL; goto out; } - /* Disable the TMC if we are trying to read from a running session */ + /* Disable the TMC if we are trying to read from a running session. */ if (drvdata->mode == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); -- cgit v1.2.3-58-ga151 From 41a75cdde7351a902066bf7ddf44e0a27996f13c Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:53 -0600 Subject: coresight: Convert driver messages to dev_dbg Convert component enable/disable messages from dev_info to dev_dbg. When used with perf, the components in the paths are enabled/disabled during each schedule of the run, which can flood the dmesg with these messages. Moreover, they are only useful for debug purposes. So, convert such messages to dev_dbg() which can be turned on as needed. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-dynamic-replicator.c | 4 ++-- drivers/hwtracing/coresight/coresight-etb10.c | 6 +++--- drivers/hwtracing/coresight/coresight-etm3x.c | 4 ++-- drivers/hwtracing/coresight/coresight-etm4x.c | 4 ++-- drivers/hwtracing/coresight/coresight-funnel.c | 4 ++-- drivers/hwtracing/coresight/coresight-replicator.c | 4 ++-- drivers/hwtracing/coresight/coresight-stm.c | 4 ++-- drivers/hwtracing/coresight/coresight-tmc-etf.c | 8 ++++---- drivers/hwtracing/coresight/coresight-tmc-etr.c | 4 ++-- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- drivers/hwtracing/coresight/coresight-tpiu.c | 4 ++-- 11 files changed, 25 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index f6d0571ab9dd..ebb80438f6a5 100644 --- a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c @@ -56,7 +56,7 @@ static int replicator_enable(struct coresight_device *csdev, int inport, CS_LOCK(drvdata->base); - dev_info(drvdata->dev, "REPLICATOR enabled\n"); + dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); return 0; } @@ -75,7 +75,7 @@ static void replicator_disable(struct coresight_device *csdev, int inport, CS_LOCK(drvdata->base); - dev_info(drvdata->dev, "REPLICATOR disabled\n"); + dev_dbg(drvdata->dev, "REPLICATOR disabled\n"); } static const struct coresight_ops_link replicator_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 0dad8626bcfb..3d4b6df32a06 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -160,7 +160,7 @@ static int etb_enable(struct coresight_device *csdev, u32 mode) spin_unlock_irqrestore(&drvdata->spinlock, flags); out: - dev_info(drvdata->dev, "ETB enabled\n"); + dev_dbg(drvdata->dev, "ETB enabled\n"); return 0; } @@ -266,7 +266,7 @@ static void etb_disable(struct coresight_device *csdev) local_set(&drvdata->mode, CS_MODE_DISABLED); - dev_info(drvdata->dev, "ETB disabled\n"); + dev_dbg(drvdata->dev, "ETB disabled\n"); } static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, @@ -509,7 +509,7 @@ static void etb_dump(struct etb_drvdata *drvdata) } spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "ETB dumped\n"); + dev_dbg(drvdata->dev, "ETB dumped\n"); } static int etb_open(struct inode *inode, struct file *file) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 7c74263c333d..9ce8fba20b0f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -501,7 +501,7 @@ static int etm_enable_sysfs(struct coresight_device *csdev) drvdata->sticky_enable = true; spin_unlock(&drvdata->spinlock); - dev_info(drvdata->dev, "ETM tracing enabled\n"); + dev_dbg(drvdata->dev, "ETM tracing enabled\n"); return 0; err: @@ -604,7 +604,7 @@ static void etm_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock(); - dev_info(drvdata->dev, "ETM tracing disabled\n"); + dev_dbg(drvdata->dev, "ETM tracing disabled\n"); } static void etm_disable(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 1d94ebec027b..c1dcc7c289a5 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -267,7 +267,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) drvdata->sticky_enable = true; spin_unlock(&drvdata->spinlock); - dev_info(drvdata->dev, "ETM tracing enabled\n"); + dev_dbg(drvdata->dev, "ETM tracing enabled\n"); return 0; err: @@ -380,7 +380,7 @@ static void etm4_disable_sysfs(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); cpus_read_unlock(); - dev_info(drvdata->dev, "ETM tracing disabled\n"); + dev_dbg(drvdata->dev, "ETM tracing disabled\n"); } static void etm4_disable(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 448145a36675..ee7a30bf9480 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -65,7 +65,7 @@ static int funnel_enable(struct coresight_device *csdev, int inport, funnel_enable_hw(drvdata, inport); - dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); + dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport); return 0; } @@ -89,7 +89,7 @@ static void funnel_disable(struct coresight_device *csdev, int inport, funnel_disable_hw(drvdata, inport); - dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); + dev_dbg(drvdata->dev, "FUNNEL inport %d disabled\n", inport); } static const struct coresight_ops_link funnel_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 8d2eaaab6c2f..feac98315471 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -35,7 +35,7 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - dev_info(drvdata->dev, "REPLICATOR enabled\n"); + dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); return 0; } @@ -44,7 +44,7 @@ static void replicator_disable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - dev_info(drvdata->dev, "REPLICATOR disabled\n"); + dev_dbg(drvdata->dev, "REPLICATOR disabled\n"); } static const struct coresight_ops_link replicator_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index c46c70aec1d5..35d6f9709274 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -211,7 +211,7 @@ static int stm_enable(struct coresight_device *csdev, stm_enable_hw(drvdata); spin_unlock(&drvdata->spinlock); - dev_info(drvdata->dev, "STM tracing enabled\n"); + dev_dbg(drvdata->dev, "STM tracing enabled\n"); return 0; } @@ -274,7 +274,7 @@ static void stm_disable(struct coresight_device *csdev, pm_runtime_put(drvdata->dev); local_set(&drvdata->mode, CS_MODE_DISABLED); - dev_info(drvdata->dev, "STM tracing disabled\n"); + dev_dbg(drvdata->dev, "STM tracing disabled\n"); } } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 0549249f4b39..434003a43346 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -233,7 +233,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) if (ret) return ret; - dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); + dev_dbg(drvdata->dev, "TMC-ETB/ETF enabled\n"); return 0; } @@ -256,7 +256,7 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev) spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n"); + dev_dbg(drvdata->dev, "TMC-ETB/ETF disabled\n"); } static int tmc_enable_etf_link(struct coresight_device *csdev, @@ -275,7 +275,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, drvdata->mode = CS_MODE_SYSFS; spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETF enabled\n"); + dev_dbg(drvdata->dev, "TMC-ETF enabled\n"); return 0; } @@ -295,7 +295,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, drvdata->mode = CS_MODE_DISABLED; spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETF disabled\n"); + dev_dbg(drvdata->dev, "TMC-ETF disabled\n"); } static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index bafd73e71c4c..5e9bb2f0e9c0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1098,7 +1098,7 @@ out: tmc_etr_free_sysfs_buf(free_buf); if (!ret) - dev_info(drvdata->dev, "TMC-ETR enabled\n"); + dev_dbg(drvdata->dev, "TMC-ETR enabled\n"); return ret; } @@ -1141,7 +1141,7 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev) spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETR disabled\n"); + dev_dbg(drvdata->dev, "TMC-ETR disabled\n"); } static const struct coresight_ops_sink tmc_etr_sink_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 1b817ec1192c..ea249f0bcd73 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -81,7 +81,7 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) } if (!ret) - dev_info(drvdata->dev, "TMC read start\n"); + dev_dbg(drvdata->dev, "TMC read start\n"); return ret; } @@ -103,7 +103,7 @@ static int tmc_read_unprepare(struct tmc_drvdata *drvdata) } if (!ret) - dev_info(drvdata->dev, "TMC read end\n"); + dev_dbg(drvdata->dev, "TMC read end\n"); return ret; } diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 459ef930d98c..ce0b84583861 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -74,7 +74,7 @@ static int tpiu_enable(struct coresight_device *csdev, u32 mode) tpiu_enable_hw(drvdata); - dev_info(drvdata->dev, "TPIU enabled\n"); + dev_dbg(drvdata->dev, "TPIU enabled\n"); return 0; } @@ -100,7 +100,7 @@ static void tpiu_disable(struct coresight_device *csdev) tpiu_disable_hw(drvdata); - dev_info(drvdata->dev, "TPIU disabled\n"); + dev_dbg(drvdata->dev, "TPIU disabled\n"); } static const struct coresight_ops_sink tpiu_sink_ops = { -- cgit v1.2.3-58-ga151 From 7ec786ad193beb5579223174e119805569a7af3b Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:54 -0600 Subject: coresight: perf: Remove reset_buffer call back for sinks Right now we issue an update_buffer() and reset_buffer() call backs in succession when we stop tracing an event. The update_buffer is supposed to check the status of the buffer and make sure the ring buffer is updated with the trace data. And we store information about the size of the data collected only to be consumed by the reset_buffer callback which always follows the update_buffer. This was originally designed for handling future IPs which could trigger a buffer overflow interrupt. This patch gets rid of the reset_buffer callback altogether and performs the actions in update_buffer, making it return the size collected. We can always add the support for handling the overflow interrupt case later. This removes some not-so pretty hack (storing the new head in the size field for snapshot mode) and cleans it up a little bit. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 56 +++++------------------ drivers/hwtracing/coresight/coresight-etm-perf.c | 9 +--- drivers/hwtracing/coresight/coresight-tmc-etf.c | 58 +++++------------------- include/linux/coresight.h | 6 +-- 4 files changed, 26 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 3d4b6df32a06..dba75c905e57 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -319,37 +319,7 @@ static int etb_set_buffer(struct coresight_device *csdev, return ret; } -static unsigned long etb_reset_buffer(struct coresight_device *csdev, - struct perf_output_handle *handle, - void *sink_config) -{ - unsigned long size = 0; - struct cs_buffers *buf = sink_config; - - if (buf) { - /* - * In snapshot mode ->data_size holds the new address of the - * ring buffer's head. The size itself is the whole address - * range since we want the latest information. - */ - if (buf->snapshot) - handle->head = local_xchg(&buf->data_size, - buf->nr_pages << PAGE_SHIFT); - - /* - * Tell the tracer PMU how much we got in this run and if - * something went wrong along the way. Nobody else can use - * this cs_buffers instance until we are done. As such - * resetting parameters here and squaring off with the ring - * buffer API in the tracer PMU is fine. - */ - size = local_xchg(&buf->data_size, 0); - } - - return size; -} - -static void etb_update_buffer(struct coresight_device *csdev, +static unsigned long etb_update_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { @@ -358,13 +328,13 @@ static void etb_update_buffer(struct coresight_device *csdev, u8 *buf_ptr; const u32 *barrier; u32 read_ptr, write_ptr, capacity; - u32 status, read_data, to_read; - unsigned long offset; + u32 status, read_data; + unsigned long offset, to_read; struct cs_buffers *buf = sink_config; struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); if (!buf) - return; + return 0; capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; @@ -469,18 +439,17 @@ static void etb_update_buffer(struct coresight_device *csdev, writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); /* - * In snapshot mode all we have to do is communicate to - * perf_aux_output_end() the address of the current head. In full - * trace mode the same function expects a size to move rb->aux_head - * forward. + * In snapshot mode we have to update the handle->head to point + * to the new location. */ - if (buf->snapshot) - local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); - else - local_add(to_read, &buf->data_size); - + if (buf->snapshot) { + handle->head = (cur * PAGE_SIZE) + offset; + to_read = buf->nr_pages << PAGE_SHIFT; + } etb_enable_hw(drvdata); CS_LOCK(drvdata->base); + + return to_read; } static const struct coresight_ops_sink etb_sink_ops = { @@ -489,7 +458,6 @@ static const struct coresight_ops_sink etb_sink_ops = { .alloc_buffer = etb_alloc_buffer, .free_buffer = etb_free_buffer, .set_buffer = etb_set_buffer, - .reset_buffer = etb_reset_buffer, .update_buffer = etb_update_buffer, }; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 6db76ce6ba5f..ad87441f65d7 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -369,15 +369,8 @@ static void etm_event_stop(struct perf_event *event, int mode) if (!sink_ops(sink)->update_buffer) return; - sink_ops(sink)->update_buffer(sink, handle, + size = sink_ops(sink)->update_buffer(sink, handle, event_data->snk_config); - - if (!sink_ops(sink)->reset_buffer) - return; - - size = sink_ops(sink)->reset_buffer(sink, handle, - event_data->snk_config); - perf_aux_output_end(handle, size); } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 434003a43346..31a98f915641 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -349,36 +349,7 @@ static int tmc_set_etf_buffer(struct coresight_device *csdev, return ret; } -static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev, - struct perf_output_handle *handle, - void *sink_config) -{ - long size = 0; - struct cs_buffers *buf = sink_config; - - if (buf) { - /* - * In snapshot mode ->data_size holds the new address of the - * ring buffer's head. The size itself is the whole address - * range since we want the latest information. - */ - if (buf->snapshot) - handle->head = local_xchg(&buf->data_size, - buf->nr_pages << PAGE_SHIFT); - /* - * Tell the tracer PMU how much we got in this run and if - * something went wrong along the way. Nobody else can use - * this cs_buffers instance until we are done. As such - * resetting parameters here and squaring off with the ring - * buffer API in the tracer PMU is fine. - */ - size = local_xchg(&buf->data_size, 0); - } - - return size; -} - -static void tmc_update_etf_buffer(struct coresight_device *csdev, +static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { @@ -387,17 +358,17 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, const u32 *barrier; u32 *buf_ptr; u64 read_ptr, write_ptr; - u32 status, to_read; - unsigned long offset; + u32 status; + unsigned long offset, to_read; struct cs_buffers *buf = sink_config; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); if (!buf) - return; + return 0; /* This shouldn't happen */ if (WARN_ON_ONCE(drvdata->mode != CS_MODE_PERF)) - return; + return 0; CS_UNLOCK(drvdata->base); @@ -486,18 +457,14 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, } } - /* - * In snapshot mode all we have to do is communicate to - * perf_aux_output_end() the address of the current head. In full - * trace mode the same function expects a size to move rb->aux_head - * forward. - */ - if (buf->snapshot) - local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); - else - local_add(to_read, &buf->data_size); - + /* In snapshot mode we have to update the head */ + if (buf->snapshot) { + handle->head = (cur * PAGE_SIZE) + offset; + to_read = buf->nr_pages << PAGE_SHIFT; + } CS_LOCK(drvdata->base); + + return to_read; } static const struct coresight_ops_sink tmc_etf_sink_ops = { @@ -506,7 +473,6 @@ static const struct coresight_ops_sink tmc_etf_sink_ops = { .alloc_buffer = tmc_alloc_etf_buffer, .free_buffer = tmc_free_etf_buffer, .set_buffer = tmc_set_etf_buffer, - .reset_buffer = tmc_reset_etf_buffer, .update_buffer = tmc_update_etf_buffer, }; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 41e1f4333bf2..8e52682b1e90 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -186,7 +186,6 @@ struct coresight_device { * @alloc_buffer: initialises perf's ring buffer for trace collection. * @free_buffer: release memory allocated in @get_config. * @set_buffer: initialises buffer mechanic before a trace session. - * @reset_buffer: finalises buffer mechanic after a trace session. * @update_buffer: update buffer pointers after a trace session. */ struct coresight_ops_sink { @@ -198,10 +197,7 @@ struct coresight_ops_sink { int (*set_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config); - unsigned long (*reset_buffer)(struct coresight_device *csdev, - struct perf_output_handle *handle, - void *sink_config); - void (*update_buffer)(struct coresight_device *csdev, + unsigned long (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config); }; -- cgit v1.2.3-58-ga151 From d25054ee8d18c937058a1b69b35fa5bfdef471f3 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:55 -0600 Subject: coresight: perf: Add helper to retrieve sink configuration We can always find the sink configuration for a given perf_output_handle. Add a helper to retrieve the sink configuration for a given perf_output_handle. This will be used to get rid of the set_buffer() call back. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm-perf.c | 14 ------------- drivers/hwtracing/coresight/coresight-etm-perf.h | 26 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index ad87441f65d7..16b83d8b2ac2 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -23,20 +23,6 @@ static struct pmu etm_pmu; static bool etm_perf_up; -/** - * struct etm_event_data - Coresight specifics associated to an event - * @work: Handle to free allocated memory outside IRQ context. - * @mask: Hold the CPU(s) this event was set for. - * @snk_config: The sink configuration. - * @path: An array of path, each slot for one CPU. - */ -struct etm_event_data { - struct work_struct work; - cpumask_t mask; - void *snk_config; - struct list_head * __percpu *path; -}; - static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle); static DEFINE_PER_CPU(struct coresight_device *, csdev_src); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h index 4197df4faf5e..da7d9336a15c 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.h +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -7,6 +7,7 @@ #ifndef _CORESIGHT_ETM_PERF_H #define _CORESIGHT_ETM_PERF_H +#include #include "coresight-priv.h" struct coresight_device; @@ -42,14 +43,39 @@ struct etm_filters { bool ssstatus; }; +/** + * struct etm_event_data - Coresight specifics associated to an event + * @work: Handle to free allocated memory outside IRQ context. + * @mask: Hold the CPU(s) this event was set for. + * @snk_config: The sink configuration. + * @path: An array of path, each slot for one CPU. + */ +struct etm_event_data { + struct work_struct work; + cpumask_t mask; + void *snk_config; + struct list_head * __percpu *path; +}; #ifdef CONFIG_CORESIGHT int etm_perf_symlink(struct coresight_device *csdev, bool link); +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{ + struct etm_event_data *data = perf_get_aux(handle); + if (data) + return data->snk_config; + return NULL; +} #else static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) { return -EINVAL; } +static inline void *etm_perf_sink_config(struct perf_output_handle *handle) +{ + return NULL; +} + #endif /* CONFIG_CORESIGHT */ #endif -- cgit v1.2.3-58-ga151 From 3d6e8935758392179645e1b105789b3da329ad38 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:56 -0600 Subject: coresight: perf: Remove set_buffer call back In coresight perf mode, we need to prepare the sink before starting a session, which is done via set_buffer call back. We then proceed to enable the tracing. If we fail to start the session successfully, we leave the sink configuration unchanged. In order to make the operation atomic and to avoid yet another call back to clear the buffer, we get rid of the "set_buffer" call back and pass the buffer details via enable() call back to the sink. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 32 ++++++++++++++++++------ drivers/hwtracing/coresight/coresight-etm-perf.c | 9 ++----- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-tmc-etf.c | 28 ++++++++++++++------- drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 +++--- drivers/hwtracing/coresight/coresight-tpiu.c | 2 +- drivers/hwtracing/coresight/coresight.c | 11 ++++---- include/linux/coresight.h | 6 +---- 8 files changed, 59 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index dba75c905e57..9fd77fdc1244 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -28,6 +28,7 @@ #include "coresight-priv.h" +#include "coresight-etm-perf.h" #define ETB_RAM_DEPTH_REG 0x004 #define ETB_STATUS_REG 0x00c @@ -90,6 +91,9 @@ struct etb_drvdata { u32 trigger_cntr; }; +static int etb_set_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle); + static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) { u32 depth = 0; @@ -131,12 +135,24 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int etb_enable(struct coresight_device *csdev, u32 mode) +static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) { + int ret = 0; u32 val; unsigned long flags; struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + /* + * We don't have an internal state to clean up if we fail to setup + * the perf buffer. So we can perform the step before we turn the + * ETB on and leave without cleaning up. + */ + if (mode == CS_MODE_PERF) { + ret = etb_set_buffer(csdev, (struct perf_output_handle *)data); + if (ret) + goto out; + } + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); /* @@ -160,8 +176,9 @@ static int etb_enable(struct coresight_device *csdev, u32 mode) spin_unlock_irqrestore(&drvdata->spinlock, flags); out: - dev_dbg(drvdata->dev, "ETB enabled\n"); - return 0; + if (!ret) + dev_dbg(drvdata->dev, "ETB enabled\n"); + return ret; } static void etb_disable_hw(struct etb_drvdata *drvdata) @@ -298,12 +315,14 @@ static void etb_free_buffer(void *config) } static int etb_set_buffer(struct coresight_device *csdev, - struct perf_output_handle *handle, - void *sink_config) + struct perf_output_handle *handle) { int ret = 0; unsigned long head; - struct cs_buffers *buf = sink_config; + struct cs_buffers *buf = etm_perf_sink_config(handle); + + if (!buf) + return -EINVAL; /* wrap head around to the amount of space we have */ head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); @@ -457,7 +476,6 @@ static const struct coresight_ops_sink etb_sink_ops = { .disable = etb_disable, .alloc_buffer = etb_alloc_buffer, .free_buffer = etb_free_buffer, - .set_buffer = etb_set_buffer, .update_buffer = etb_update_buffer, }; diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 16b83d8b2ac2..abe8249b893b 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -286,16 +286,11 @@ static void etm_event_start(struct perf_event *event, int flags) path = etm_event_cpu_path(event_data, cpu); /* We need a sink, no need to continue without one */ sink = coresight_get_sink(path); - if (WARN_ON_ONCE(!sink || !sink_ops(sink)->set_buffer)) - goto fail_end_stop; - - /* Configure the sink */ - if (sink_ops(sink)->set_buffer(sink, handle, - event_data->snk_config)) + if (WARN_ON_ONCE(!sink)) goto fail_end_stop; /* Nothing will happen without a path */ - if (coresight_enable_path(path, CS_MODE_PERF)) + if (coresight_enable_path(path, CS_MODE_PERF, handle)) goto fail_end_stop; /* Tell the perf core the event is alive */ diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 1a6cf3589866..c11da5564a67 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -137,7 +137,7 @@ static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, } void coresight_disable_path(struct list_head *path); -int coresight_enable_path(struct list_head *path, u32 mode); +int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data); struct coresight_device *coresight_get_sink(struct list_head *path); struct coresight_device *coresight_get_enabled_sink(bool reset); struct list_head *coresight_build_path(struct coresight_device *csdev, diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 31a98f915641..4156c95ce1bb 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -10,6 +10,10 @@ #include #include "coresight-priv.h" #include "coresight-tmc.h" +#include "coresight-etm-perf.h" + +static int tmc_set_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle); static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { @@ -182,11 +186,12 @@ out: return ret; } -static int tmc_enable_etf_sink_perf(struct coresight_device *csdev) +static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) { int ret = 0; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct perf_output_handle *handle = data; spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { @@ -204,15 +209,19 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev) goto out; } - drvdata->mode = CS_MODE_PERF; - tmc_etb_enable_hw(drvdata); + ret = tmc_set_etf_buffer(csdev, handle); + if (!ret) { + drvdata->mode = CS_MODE_PERF; + tmc_etb_enable_hw(drvdata); + } out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; } -static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etf_sink(struct coresight_device *csdev, + u32 mode, void *data) { int ret; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -222,7 +231,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) ret = tmc_enable_etf_sink_sysfs(csdev); break; case CS_MODE_PERF: - ret = tmc_enable_etf_sink_perf(csdev); + ret = tmc_enable_etf_sink_perf(csdev, data); break; /* We shouldn't be here */ default: @@ -328,12 +337,14 @@ static void tmc_free_etf_buffer(void *config) } static int tmc_set_etf_buffer(struct coresight_device *csdev, - struct perf_output_handle *handle, - void *sink_config) + struct perf_output_handle *handle) { int ret = 0; unsigned long head; - struct cs_buffers *buf = sink_config; + struct cs_buffers *buf = etm_perf_sink_config(handle); + + if (!buf) + return -EINVAL; /* wrap head around to the amount of space we have */ head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); @@ -472,7 +483,6 @@ static const struct coresight_ops_sink tmc_etf_sink_ops = { .disable = tmc_disable_etf_sink, .alloc_buffer = tmc_alloc_etf_buffer, .free_buffer = tmc_free_etf_buffer, - .set_buffer = tmc_set_etf_buffer, .update_buffer = tmc_update_etf_buffer, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 5e9bb2f0e9c0..1aedfc3629c0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -1103,19 +1103,20 @@ out: return ret; } -static int tmc_enable_etr_sink_perf(struct coresight_device *csdev) +static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) { /* We don't support perf mode yet ! */ return -EINVAL; } -static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etr_sink(struct coresight_device *csdev, + u32 mode, void *data) { switch (mode) { case CS_MODE_SYSFS: return tmc_enable_etr_sink_sysfs(csdev); case CS_MODE_PERF: - return tmc_enable_etr_sink_perf(csdev); + return tmc_enable_etr_sink_perf(csdev, data); } /* We shouldn't be here */ diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index ce0b84583861..b2f72a1fa402 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -68,7 +68,7 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tpiu_enable(struct coresight_device *csdev, u32 mode) +static int tpiu_enable(struct coresight_device *csdev, u32 mode, void *__unused) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 07382c55b31d..e73ca6af4765 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -128,7 +128,8 @@ static int coresight_find_link_outport(struct coresight_device *csdev, return -ENODEV; } -static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) +static int coresight_enable_sink(struct coresight_device *csdev, + u32 mode, void *data) { int ret; @@ -137,7 +138,7 @@ static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) * existing "mode" of operation. */ if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev, mode); + ret = sink_ops(csdev)->enable(csdev, mode, data); if (ret) return ret; csdev->enable = true; @@ -315,7 +316,7 @@ void coresight_disable_path(struct list_head *path) } } -int coresight_enable_path(struct list_head *path, u32 mode) +int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data) { int ret = 0; @@ -340,7 +341,7 @@ int coresight_enable_path(struct list_head *path, u32 mode) switch (type) { case CORESIGHT_DEV_TYPE_SINK: - ret = coresight_enable_sink(csdev, mode); + ret = coresight_enable_sink(csdev, mode, sink_data); /* * Sink is the first component turned on. If we * failed to enable the sink, there are no components @@ -643,7 +644,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - ret = coresight_enable_path(path, CS_MODE_SYSFS); + ret = coresight_enable_path(path, CS_MODE_SYSFS, NULL); if (ret) goto err_path; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 8e52682b1e90..53535821dc25 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -185,18 +185,14 @@ struct coresight_device { * @disable: disables the sink. * @alloc_buffer: initialises perf's ring buffer for trace collection. * @free_buffer: release memory allocated in @get_config. - * @set_buffer: initialises buffer mechanic before a trace session. * @update_buffer: update buffer pointers after a trace session. */ struct coresight_ops_sink { - int (*enable)(struct coresight_device *csdev, u32 mode); + int (*enable)(struct coresight_device *csdev, u32 mode, void *data); void (*disable)(struct coresight_device *csdev); void *(*alloc_buffer)(struct coresight_device *csdev, int cpu, void **pages, int nr_pages, bool overwrite); void (*free_buffer)(void *config); - int (*set_buffer)(struct coresight_device *csdev, - struct perf_output_handle *handle, - void *sink_config); unsigned long (*update_buffer)(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config); -- cgit v1.2.3-58-ga151 From 22f429f19c4135d51e9dcaf360c0920e32aac7f9 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:17:57 -0600 Subject: coresight: etm-perf: Add support for ETR backend Add support for using TMC-ETR as backend for ETM perf tracing. We use software double buffering at the moment. i.e, the TMC-ETR uses a separate buffer than the perf ring buffer. The data is copied to the perf ring buffer once a session completes. The TMC-ETR would try to match the larger of perf ring buffer or the ETR buffer size configured via sysfs, scaling down to a minimum limit of 1MB. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 248 +++++++++++++++++++++++- drivers/hwtracing/coresight/coresight-tmc.h | 2 + 2 files changed, 248 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 1aedfc3629c0..56fea4ff947e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -10,6 +10,7 @@ #include #include #include "coresight-catu.h" +#include "coresight-etm-perf.h" #include "coresight-priv.h" #include "coresight-tmc.h" @@ -20,6 +21,28 @@ struct etr_flat_buf { size_t size; }; +/* + * etr_perf_buffer - Perf buffer used for ETR + * @etr_buf - Actual buffer used by the ETR + * @snaphost - Perf session mode + * @head - handle->head at the beginning of the session. + * @nr_pages - Number of pages in the ring buffer. + * @pages - Array of Pages in the ring buffer. + */ +struct etr_perf_buffer { + struct etr_buf *etr_buf; + bool snapshot; + unsigned long head; + int nr_pages; + void **pages; +}; + +/* Convert the perf index to an offset within the ETR buffer */ +#define PERF_IDX2OFF(idx, buf) ((idx) % ((buf)->nr_pages << PAGE_SHIFT)) + +/* Lower limit for ETR hardware buffer */ +#define TMC_ETR_PERF_MIN_BUF_SIZE SZ_1M + /* * The TMC ETR SG has a page size of 4K. The SG table contains pointers * to 4KB buffers. However, the OS may use a PAGE_SIZE different from @@ -1103,10 +1126,228 @@ out: return ret; } +/* + * tmc_etr_setup_perf_buf: Allocate ETR buffer for use by perf. + * The size of the hardware buffer is dependent on the size configured + * via sysfs and the perf ring buffer size. We prefer to allocate the + * largest possible size, scaling down the size by half until it + * reaches a minimum limit (1M), beyond which we give up. + */ +static struct etr_perf_buffer * +tmc_etr_setup_perf_buf(struct tmc_drvdata *drvdata, int node, int nr_pages, + void **pages, bool snapshot) +{ + struct etr_buf *etr_buf; + struct etr_perf_buffer *etr_perf; + unsigned long size; + + etr_perf = kzalloc_node(sizeof(*etr_perf), GFP_KERNEL, node); + if (!etr_perf) + return ERR_PTR(-ENOMEM); + + /* + * Try to match the perf ring buffer size if it is larger + * than the size requested via sysfs. + */ + if ((nr_pages << PAGE_SHIFT) > drvdata->size) { + etr_buf = tmc_alloc_etr_buf(drvdata, (nr_pages << PAGE_SHIFT), + 0, node, NULL); + if (!IS_ERR(etr_buf)) + goto done; + } + + /* + * Else switch to configured size for this ETR + * and scale down until we hit the minimum limit. + */ + size = drvdata->size; + do { + etr_buf = tmc_alloc_etr_buf(drvdata, size, 0, node, NULL); + if (!IS_ERR(etr_buf)) + goto done; + size /= 2; + } while (size >= TMC_ETR_PERF_MIN_BUF_SIZE); + + kfree(etr_perf); + return ERR_PTR(-ENOMEM); + +done: + etr_perf->etr_buf = etr_buf; + return etr_perf; +} + + +static void *tmc_alloc_etr_buffer(struct coresight_device *csdev, + int cpu, void **pages, int nr_pages, + bool snapshot) +{ + struct etr_perf_buffer *etr_perf; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (cpu == -1) + cpu = smp_processor_id(); + + etr_perf = tmc_etr_setup_perf_buf(drvdata, cpu_to_node(cpu), + nr_pages, pages, snapshot); + if (IS_ERR(etr_perf)) { + dev_dbg(drvdata->dev, "Unable to allocate ETR buffer\n"); + return NULL; + } + + etr_perf->snapshot = snapshot; + etr_perf->nr_pages = nr_pages; + etr_perf->pages = pages; + + return etr_perf; +} + +static void tmc_free_etr_buffer(void *config) +{ + struct etr_perf_buffer *etr_perf = config; + + if (etr_perf->etr_buf) + tmc_free_etr_buf(etr_perf->etr_buf); + kfree(etr_perf); +} + +/* + * tmc_etr_sync_perf_buffer: Copy the actual trace data from the hardware + * buffer to the perf ring buffer. + */ +static void tmc_etr_sync_perf_buffer(struct etr_perf_buffer *etr_perf) +{ + long bytes, to_copy; + long pg_idx, pg_offset, src_offset; + unsigned long head = etr_perf->head; + char **dst_pages, *src_buf; + struct etr_buf *etr_buf = etr_perf->etr_buf; + + head = etr_perf->head; + pg_idx = head >> PAGE_SHIFT; + pg_offset = head & (PAGE_SIZE - 1); + dst_pages = (char **)etr_perf->pages; + src_offset = etr_buf->offset; + to_copy = etr_buf->len; + + while (to_copy > 0) { + /* + * In one iteration, we can copy minimum of : + * 1) what is available in the source buffer, + * 2) what is available in the source buffer, before it + * wraps around. + * 3) what is available in the destination page. + * in one iteration. + */ + bytes = tmc_etr_buf_get_data(etr_buf, src_offset, to_copy, + &src_buf); + if (WARN_ON_ONCE(bytes <= 0)) + break; + bytes = min(bytes, (long)(PAGE_SIZE - pg_offset)); + + memcpy(dst_pages[pg_idx] + pg_offset, src_buf, bytes); + + to_copy -= bytes; + + /* Move destination pointers */ + pg_offset += bytes; + if (pg_offset == PAGE_SIZE) { + pg_offset = 0; + if (++pg_idx == etr_perf->nr_pages) + pg_idx = 0; + } + + /* Move source pointers */ + src_offset += bytes; + if (src_offset >= etr_buf->size) + src_offset -= etr_buf->size; + } +} + +/* + * tmc_update_etr_buffer : Update the perf ring buffer with the + * available trace data. We use software double buffering at the moment. + * + * TODO: Add support for reusing the perf ring buffer. + */ +static unsigned long +tmc_update_etr_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *config) +{ + bool lost = false; + unsigned long flags, size = 0; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct etr_perf_buffer *etr_perf = config; + struct etr_buf *etr_buf = etr_perf->etr_buf; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (WARN_ON(drvdata->perf_data != etr_perf)) { + lost = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + goto out; + } + + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_sync_etr_buf(drvdata); + + CS_LOCK(drvdata->base); + /* Reset perf specific data */ + drvdata->perf_data = NULL; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + size = etr_buf->len; + tmc_etr_sync_perf_buffer(etr_perf); + + /* + * Update handle->head in snapshot mode. Also update the size to the + * hardware buffer size if there was an overflow. + */ + if (etr_perf->snapshot) { + handle->head += size; + if (etr_buf->full) + size = etr_buf->size; + } + + lost |= etr_buf->full; +out: + if (lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + return size; +} + static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) { - /* We don't support perf mode yet ! */ - return -EINVAL; + int rc = 0; + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct perf_output_handle *handle = data; + struct etr_perf_buffer *etr_perf = etm_perf_sink_config(handle); + + spin_lock_irqsave(&drvdata->spinlock, flags); + /* + * There can be only one writer per sink in perf mode. If the sink + * is already open in SYSFS mode, we can't use it. + */ + if (drvdata->mode != CS_MODE_DISABLED || WARN_ON(drvdata->perf_data)) { + rc = -EBUSY; + goto unlock_out; + } + + if (WARN_ON(!etr_perf || !etr_perf->etr_buf)) { + rc = -EINVAL; + goto unlock_out; + } + + etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf); + drvdata->perf_data = etr_perf; + drvdata->mode = CS_MODE_PERF; + tmc_etr_enable_hw(drvdata, etr_perf->etr_buf); + +unlock_out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return rc; } static int tmc_enable_etr_sink(struct coresight_device *csdev, @@ -1148,6 +1389,9 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev) static const struct coresight_ops_sink tmc_etr_sink_ops = { .enable = tmc_enable_etr_sink, .disable = tmc_disable_etr_sink, + .alloc_buffer = tmc_alloc_etr_buffer, + .update_buffer = tmc_update_etr_buffer, + .free_buffer = tmc_free_etr_buffer, }; const struct coresight_ops tmc_etr_cs_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 872f63e3651b..487c53701e9c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -170,6 +170,7 @@ struct etr_buf { * @trigger_cntr: amount of words to store after a trigger. * @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the * device configuration register (DEVID) + * @perf_data: PERF buffer for ETR. * @sysfs_data: SYSFS buffer for ETR. */ struct tmc_drvdata { @@ -191,6 +192,7 @@ struct tmc_drvdata { u32 trigger_cntr; u32 etr_caps; struct etr_buf *sysfs_buf; + void *perf_data; }; struct etr_buf_operations { -- cgit v1.2.3-58-ga151 From d43b8ec599f90c1f07b1bdd29b0c4b6306726ef2 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 20 Sep 2018 13:17:58 -0600 Subject: coresight: etb10: Refactor etb_drvdata::mode handling This patch moves the etb_drvdata::mode from a locat_t to a simple u32, as it is for the ETF and ETR drivers. This streamlines the code and adds commonality with the other drivers when dealing with similar operations. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 62 +++++++++++++++------------ 1 file changed, 34 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 9fd77fdc1244..69287163ce4e 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -5,7 +5,6 @@ * Description: CoreSight Embedded Trace Buffer driver */ -#include #include #include #include @@ -72,8 +71,8 @@ * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. * @reading: synchronise user space access to etb buffer. - * @mode: this ETB is being used. * @buf: area of memory where ETB buffer content gets sent. + * @mode: this ETB is being used. * @buffer_depth: size of @buf. * @trigger_cntr: amount of words to store after a trigger. */ @@ -85,8 +84,8 @@ struct etb_drvdata { struct miscdevice miscdev; spinlock_t spinlock; local_t reading; - local_t mode; u8 *buf; + u32 mode; u32 buffer_depth; u32 trigger_cntr; }; @@ -138,44 +137,48 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) { int ret = 0; - u32 val; unsigned long flags; struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - /* - * We don't have an internal state to clean up if we fail to setup - * the perf buffer. So we can perform the step before we turn the - * ETB on and leave without cleaning up. - */ - if (mode == CS_MODE_PERF) { - ret = etb_set_buffer(csdev, (struct perf_output_handle *)data); - if (ret) - goto out; - } + spin_lock_irqsave(&drvdata->spinlock, flags); - val = local_cmpxchg(&drvdata->mode, - CS_MODE_DISABLED, mode); /* * When accessing from Perf, a HW buffer can be handled * by a single trace entity. In sysFS mode many tracers * can be logging to the same HW buffer. */ - if (val == CS_MODE_PERF) - return -EBUSY; + if (drvdata->mode == CS_MODE_PERF) { + ret = -EBUSY; + goto out; + } /* Don't let perf disturb sysFS sessions */ - if (val == CS_MODE_SYSFS && mode == CS_MODE_PERF) - return -EBUSY; + if (drvdata->mode == CS_MODE_SYSFS && mode == CS_MODE_PERF) { + ret = -EBUSY; + goto out; + } /* Nothing to do, the tracer is already enabled. */ - if (val == CS_MODE_SYSFS) + if (drvdata->mode == CS_MODE_SYSFS && mode == CS_MODE_SYSFS) goto out; - spin_lock_irqsave(&drvdata->spinlock, flags); + /* + * We don't have an internal state to clean up if we fail to setup + * the perf buffer. So we can perform the step before we turn the + * ETB on and leave without cleaning up. + */ + if (mode == CS_MODE_PERF) { + ret = etb_set_buffer(csdev, (struct perf_output_handle *)data); + if (ret) + goto out; + } + + drvdata->mode = mode; etb_enable_hw(drvdata); - spin_unlock_irqrestore(&drvdata->spinlock, flags); out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (!ret) dev_dbg(drvdata->dev, "ETB enabled\n"); return ret; @@ -277,11 +280,14 @@ static void etb_disable(struct coresight_device *csdev) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - etb_disable_hw(drvdata); - etb_dump_hw(drvdata); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - local_set(&drvdata->mode, CS_MODE_DISABLED); + /* Disable the ETB only if it needs to */ + if (drvdata->mode != CS_MODE_DISABLED) { + etb_disable_hw(drvdata); + etb_dump_hw(drvdata); + drvdata->mode = CS_MODE_DISABLED; + } + spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_dbg(drvdata->dev, "ETB disabled\n"); } @@ -488,7 +494,7 @@ static void etb_dump(struct etb_drvdata *drvdata) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { + if (drvdata->mode == CS_MODE_SYSFS) { etb_disable_hw(drvdata); etb_dump_hw(drvdata); etb_enable_hw(drvdata); -- cgit v1.2.3-58-ga151 From d4989fe88603367e5998af70ee638ae6790d42d1 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 20 Sep 2018 13:17:59 -0600 Subject: coresight: etb10: Splitting function etb_enable() Up until now the relative simplicity of enabling the ETB made it possible to accommodate processing for both sysFS and perf methods. But work on claimtags and CPU-wide trace scenarios is adding some complexity, making the current code messy and hard to maintain. As such follow what has been done for ETF and ETR components and split function etb_enable() so that processing for both API can be done cleanly. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 73 +++++++++++++++++++-------- 1 file changed, 52 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 69287163ce4e..08fa660098f8 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -134,7 +134,7 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) +static int etb_enable_sysfs(struct coresight_device *csdev) { int ret = 0; unsigned long flags; @@ -142,48 +142,79 @@ static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) spin_lock_irqsave(&drvdata->spinlock, flags); - /* - * When accessing from Perf, a HW buffer can be handled - * by a single trace entity. In sysFS mode many tracers - * can be logging to the same HW buffer. - */ + /* Don't messup with perf sessions. */ if (drvdata->mode == CS_MODE_PERF) { ret = -EBUSY; goto out; } - /* Don't let perf disturb sysFS sessions */ - if (drvdata->mode == CS_MODE_SYSFS && mode == CS_MODE_PERF) { - ret = -EBUSY; + /* Nothing to do, the tracer is already enabled. */ + if (drvdata->mode == CS_MODE_SYSFS) goto out; - } - /* Nothing to do, the tracer is already enabled. */ - if (drvdata->mode == CS_MODE_SYSFS && mode == CS_MODE_SYSFS) + drvdata->mode = CS_MODE_SYSFS; + etb_enable_hw(drvdata); + +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return ret; +} + +static int etb_enable_perf(struct coresight_device *csdev, void *data) +{ + int ret = 0; + unsigned long flags; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* No need to continue if the component is already in use. */ + if (drvdata->mode != CS_MODE_DISABLED) { + ret = -EBUSY; goto out; + } /* * We don't have an internal state to clean up if we fail to setup * the perf buffer. So we can perform the step before we turn the * ETB on and leave without cleaning up. */ - if (mode == CS_MODE_PERF) { - ret = etb_set_buffer(csdev, (struct perf_output_handle *)data); - if (ret) - goto out; - } + ret = etb_set_buffer(csdev, (struct perf_output_handle *)data); + if (ret) + goto out; - drvdata->mode = mode; + drvdata->mode = CS_MODE_PERF; etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - - if (!ret) - dev_dbg(drvdata->dev, "ETB enabled\n"); return ret; } +static int etb_enable(struct coresight_device *csdev, u32 mode, void *data) +{ + int ret; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + switch (mode) { + case CS_MODE_SYSFS: + ret = etb_enable_sysfs(csdev); + break; + case CS_MODE_PERF: + ret = etb_enable_perf(csdev, data); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + return ret; + + dev_dbg(drvdata->dev, "ETB enabled\n"); + return 0; +} + static void etb_disable_hw(struct etb_drvdata *drvdata) { u32 ffcr; -- cgit v1.2.3-58-ga151 From b860801e3237ec4c74cf8de0be4816996757ae5c Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Thu, 20 Sep 2018 13:18:00 -0600 Subject: coresight: etm4x: Configure EL2 exception level when kernel is running in HYP For non-VHE systems host kernel runs at EL1 and jumps to EL2 whenever hypervisor code should be executed. In this case ETM4x driver must restrict configuration to EL1 when it setups kernel tracing. However, there is no separate hypervisor privilege level when VHE is enabled, the host kernel runs at EL2. This patch fixes configuration of TRCACATRn register for VHE systems so that ETM_EXLEVEL_NS_HYP bit is used instead of ETM_EXLEVEL_NS_OS to on/off kernel tracing. At the same time, it moves common code to new helper. Signed-off-by: Tomasz Nowicki Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 40 +++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c1dcc7c289a5..b7379e9cfb30 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "coresight-etm4x.h" #include "coresight-etm-perf.h" @@ -605,7 +606,7 @@ static void etm4_set_default_config(struct etmv4_config *config) config->vinst_ctrl |= BIT(0); } -static u64 etm4_get_access_type(struct etmv4_config *config) +static u64 etm4_get_ns_access_type(struct etmv4_config *config) { u64 access_type = 0; @@ -616,17 +617,26 @@ static u64 etm4_get_access_type(struct etmv4_config *config) * Bit[13] Exception level 1 - OS * Bit[14] Exception level 2 - Hypervisor * Bit[15] Never implemented - * - * Always stay away from hypervisor mode. */ - access_type = ETM_EXLEVEL_NS_HYP; - - if (config->mode & ETM_MODE_EXCL_KERN) - access_type |= ETM_EXLEVEL_NS_OS; + if (!is_kernel_in_hyp_mode()) { + /* Stay away from hypervisor mode for non-VHE */ + access_type = ETM_EXLEVEL_NS_HYP; + if (config->mode & ETM_MODE_EXCL_KERN) + access_type |= ETM_EXLEVEL_NS_OS; + } else if (config->mode & ETM_MODE_EXCL_KERN) { + access_type = ETM_EXLEVEL_NS_HYP; + } if (config->mode & ETM_MODE_EXCL_USER) access_type |= ETM_EXLEVEL_NS_APP; + return access_type; +} + +static u64 etm4_get_access_type(struct etmv4_config *config) +{ + u64 access_type = etm4_get_ns_access_type(config); + /* * EXLEVEL_S, bits[11:8], don't trace anything happening * in secure state. @@ -880,20 +890,10 @@ void etm4_config_trace_mode(struct etmv4_config *config) addr_acc = config->addr_acc[ETM_DEFAULT_ADDR_COMP]; /* clear default config */ - addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS); + addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS | + ETM_EXLEVEL_NS_HYP); - /* - * EXLEVEL_NS, bits[15:12] - * The Exception levels are: - * Bit[12] Exception level 0 - Application - * Bit[13] Exception level 1 - OS - * Bit[14] Exception level 2 - Hypervisor - * Bit[15] Never implemented - */ - if (mode & ETM_MODE_EXCL_KERN) - addr_acc |= ETM_EXLEVEL_NS_OS; - else - addr_acc |= ETM_EXLEVEL_NS_APP; + addr_acc |= etm4_get_ns_access_type(config); config->addr_acc[ETM_DEFAULT_ADDR_COMP] = addr_acc; config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = addr_acc; -- cgit v1.2.3-58-ga151 From b3bee19e93e7fe9df01e0a90cec025781b638ad4 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 20 Sep 2018 13:18:01 -0600 Subject: coresight: tmc: Refactor loops in etb dump In ETB dump function tmc_etb_dump_hw() it has nested loops. The second level loop is to iterate index in the range [0 .. drvdata->memwidth); but the index isn't really used in the code, thus the second level loop is useless. This patch is to remove the second level loop; the refactor also reduces indentation and we can use 'break' to replace 'goto' tag. Cc: Mathieu Poirier Signed-off-by: Leo Yan Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 4156c95ce1bb..4bf3bfd7c078 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -38,23 +38,20 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) { char *bufp; u32 read_data, lost; - int i; /* Check if the buffer wrapped around. */ lost = readl_relaxed(drvdata->base + TMC_STS) & TMC_STS_FULL; bufp = drvdata->buf; drvdata->len = 0; while (1) { - for (i = 0; i < drvdata->memwidth; i++) { - read_data = readl_relaxed(drvdata->base + TMC_RRD); - if (read_data == 0xFFFFFFFF) - goto done; - memcpy(bufp, &read_data, 4); - bufp += 4; - drvdata->len += 4; - } + read_data = readl_relaxed(drvdata->base + TMC_RRD); + if (read_data == 0xFFFFFFFF) + break; + memcpy(bufp, &read_data, 4); + bufp += 4; + drvdata->len += 4; } -done: + if (lost) coresight_insert_barrier_packet(drvdata->buf); return; -- cgit v1.2.3-58-ga151 From e7753f3937610633a540f2be81be87531f96ff04 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 20 Sep 2018 13:18:02 -0600 Subject: coresight: tmc: Fix byte-address alignment for RRP >From the comment in the code, it claims the requirement for byte-address alignment for RRP register: 'for 32-bit, 64-bit and 128-bit wide trace memory, the four LSBs must be 0s. For 256-bit wide trace memory, the five LSBs must be 0s'. This isn't consistent with the program, the program sets five LSBs as zeros for 32/64/128-bit wide trace memory and set six LSBs zeros for 256-bit wide trace memory. After checking with the CoreSight Trace Memory Controller technical reference manual (ARM DDI 0461B, section 3.3.4 RAM Read Pointer Register), it proves the comment is right and the program does wrong setting. This patch fixes byte-address alignment for RRP by following correct definition in the technical reference manual. Cc: Mathieu Poirier Cc: Mike Leach Signed-off-by: Leo Yan Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 4bf3bfd7c078..b54a3db13fee 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -417,10 +417,10 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev, case TMC_MEM_INTF_WIDTH_32BITS: case TMC_MEM_INTF_WIDTH_64BITS: case TMC_MEM_INTF_WIDTH_128BITS: - mask = GENMASK(31, 5); + mask = GENMASK(31, 4); break; case TMC_MEM_INTF_WIDTH_256BITS: - mask = GENMASK(31, 6); + mask = GENMASK(31, 5); break; } -- cgit v1.2.3-58-ga151 From b9866bb16882e89b57e2dc826114316357263fb7 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:03 -0600 Subject: coresight: Handle failures in enabling a trace path coresight_enable_path() enables the components in a trace path from a given source to a sink, excluding the source. The operation is performed in the reverse order; the sink first and then backwards in the list. However, if we encounter an error in enabling any of the component, we simply disable all the components in the given path irrespective of whether we enabled some of the components in the enable iteration. This could interfere with another trace session if one of the link devices is turned off (e.g, TMC-ETF). So, we need to make sure that we only disable those components which were actually enabled from the iteration. This patch achieves the same by refactoring the coresight_disable_path to accept a "node" to start from in the forward order, which can then be used from the error path of coresight_enable_path(). With this change, we don't issue a disable call back for a component which didn't get enabled. This change of behavior triggers a bug in coresight_enable_link(), where we leave the refcount on the device and will prevent the device from being enabled forever. So, we also drop the refcount in the coresight_enable_link() if the operation failed. Also, with the refactoring, we always start after the first node (which is the "SOURCE" device) for disabling the entire path. This implies, we must not find a "SOURCE" in the middle of the path. Hence, added a WARN_ON() to make sure the paths we get are sane, rather than simply ignoring them. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index e73ca6af4765..f4f50753cf75 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -187,8 +187,10 @@ static int coresight_enable_link(struct coresight_device *csdev, if (atomic_inc_return(&csdev->refcnt[refport]) == 1) { if (link_ops(csdev)->enable) { ret = link_ops(csdev)->enable(csdev, inport, outport); - if (ret) + if (ret) { + atomic_dec(&csdev->refcnt[refport]); return ret; + } } } @@ -277,13 +279,21 @@ static bool coresight_disable_source(struct coresight_device *csdev) return !csdev->enable; } -void coresight_disable_path(struct list_head *path) +/* + * coresight_disable_path_from : Disable components in the given path beyond + * @nd in the list. If @nd is NULL, all the components, except the SOURCE are + * disabled. + */ +static void coresight_disable_path_from(struct list_head *path, + struct coresight_node *nd) { u32 type; - struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; - list_for_each_entry(nd, path, link) { + if (!nd) + nd = list_first_entry(path, struct coresight_node, link); + + list_for_each_entry_continue(nd, path, link) { csdev = nd->csdev; type = csdev->type; @@ -303,7 +313,12 @@ void coresight_disable_path(struct list_head *path) coresight_disable_sink(csdev); break; case CORESIGHT_DEV_TYPE_SOURCE: - /* sources are disabled from either sysFS or Perf */ + /* + * We skip the first node in the path assuming that it + * is the source. So we don't expect a source device in + * the middle of a path. + */ + WARN_ON(1); break; case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; @@ -316,6 +331,11 @@ void coresight_disable_path(struct list_head *path) } } +void coresight_disable_path(struct list_head *path) +{ + coresight_disable_path_from(path, NULL); +} + int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data) { @@ -369,7 +389,7 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data) out: return ret; err: - coresight_disable_path(path); + coresight_disable_path_from(path, nd); goto out; } -- cgit v1.2.3-58-ga151 From 6276f9cba50f77c5b51b581c11b74a51a3f9e040 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:04 -0600 Subject: coresight: tmc-etr: Refactor for handling errors Refactor the tmc-etr enable operation to make it easier to handle errors in enabling the hardware. We need to make sure that the buffer is compatible with the ETR. This patch re-arranges to make the error handling easier, by deferring the hardware enablement until all the errors are checked. This also avoids turning the CATU on/off during a sysfs read session. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 67 ++++++++++++++++--------- 1 file changed, 43 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 56fea4ff947e..c42693542ec8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -918,21 +918,10 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata) tmc_etr_buf_insert_barrier_packet(etr_buf, etr_buf->offset); } -static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata, - struct etr_buf *etr_buf) +static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) { u32 axictl, sts; - - /* Callers should provide an appropriate buffer for use */ - if (WARN_ON(!etr_buf || drvdata->etr_buf)) - return; - drvdata->etr_buf = etr_buf; - - /* - * If this ETR is connected to a CATU, enable it before we turn - * this on - */ - tmc_etr_enable_catu(drvdata); + struct etr_buf *etr_buf = drvdata->etr_buf; CS_UNLOCK(drvdata->base); @@ -952,11 +941,8 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata, axictl |= TMC_AXICTL_ARCACHE_OS; } - if (etr_buf->mode == ETR_MODE_ETR_SG) { - if (WARN_ON(!tmc_etr_has_cap(drvdata, TMC_ETR_SG))) - return; + if (etr_buf->mode == ETR_MODE_ETR_SG) axictl |= TMC_AXICTL_SCT_GAT_MODE; - } writel_relaxed(axictl, drvdata->base + TMC_AXICTL); tmc_write_dba(drvdata, etr_buf->hwaddr); @@ -982,6 +968,32 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata, CS_LOCK(drvdata->base); } +static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, + struct etr_buf *etr_buf) +{ + /* Callers should provide an appropriate buffer for use */ + if (WARN_ON(!etr_buf)) + return -EINVAL; + + if ((etr_buf->mode == ETR_MODE_ETR_SG) && + WARN_ON(!tmc_etr_has_cap(drvdata, TMC_ETR_SG))) + return -EINVAL; + + if (WARN_ON(drvdata->etr_buf)) + return -EBUSY; + + /* Set the buffer for the session */ + drvdata->etr_buf = etr_buf; + /* + * If this ETR is connected to a CATU, enable it before we turn + * this on. + */ + tmc_etr_enable_catu(drvdata); + + __tmc_etr_enable_hw(drvdata); + return 0; +} + /* * Return the available trace data in the buffer (starts at etr_buf->offset, * limited by etr_buf->len) from @pos, with a maximum limit of @len, @@ -1037,7 +1049,7 @@ static void tmc_etr_sync_sysfs_buf(struct tmc_drvdata *drvdata) } } -static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +static void __tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -1053,6 +1065,11 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); +} + +static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +{ + __tmc_etr_disable_hw(drvdata); /* Disable CATU device if this ETR is connected to one */ tmc_etr_disable_catu(drvdata); /* Reset the ETR buf used by hardware */ @@ -1111,8 +1128,9 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev) drvdata->sysfs_buf = new_buf; } - drvdata->mode = CS_MODE_SYSFS; - tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf); + ret = tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf); + if (!ret) + drvdata->mode = CS_MODE_SYSFS; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -1342,8 +1360,9 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data) etr_perf->head = PERF_IDX2OFF(handle->head, etr_perf); drvdata->perf_data = etr_perf; - drvdata->mode = CS_MODE_PERF; - tmc_etr_enable_hw(drvdata, etr_perf->etr_buf); + rc = tmc_etr_enable_hw(drvdata, etr_perf->etr_buf); + if (!rc) + drvdata->mode = CS_MODE_PERF; unlock_out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -1425,7 +1444,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) /* Disable the TMC if we are trying to read from a running session. */ if (drvdata->mode == CS_MODE_SYSFS) - tmc_etr_disable_hw(drvdata); + __tmc_etr_disable_hw(drvdata); drvdata->reading = true; out: @@ -1452,7 +1471,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) * buffer. Since the tracer is still enabled drvdata::buf can't * be NULL. */ - tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf); + __tmc_etr_enable_hw(drvdata); } else { /* * The ETR is not tracing and the buffer was just read. -- cgit v1.2.3-58-ga151 From 1c7995e11cd3252d0db63dad948fc96f05d75b77 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:05 -0600 Subject: coresight: tmc-etr: Handle errors enabling CATU Make sure we honor the errors in CATU device and abort the operation. While at it, delay setting the etr_buf for the session until we are sure that we are indeed enabling the ETR. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etr.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index c42693542ec8..daad52146140 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -751,12 +751,14 @@ tmc_etr_get_catu_device(struct tmc_drvdata *drvdata) return NULL; } -static inline void tmc_etr_enable_catu(struct tmc_drvdata *drvdata) +static inline int tmc_etr_enable_catu(struct tmc_drvdata *drvdata, + struct etr_buf *etr_buf) { struct coresight_device *catu = tmc_etr_get_catu_device(drvdata); if (catu && helper_ops(catu)->enable) - helper_ops(catu)->enable(catu, drvdata->etr_buf); + return helper_ops(catu)->enable(catu, etr_buf); + return 0; } static inline void tmc_etr_disable_catu(struct tmc_drvdata *drvdata) @@ -971,6 +973,8 @@ static void __tmc_etr_enable_hw(struct tmc_drvdata *drvdata) static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, struct etr_buf *etr_buf) { + int rc; + /* Callers should provide an appropriate buffer for use */ if (WARN_ON(!etr_buf)) return -EINVAL; @@ -982,16 +986,17 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, if (WARN_ON(drvdata->etr_buf)) return -EBUSY; - /* Set the buffer for the session */ - drvdata->etr_buf = etr_buf; /* * If this ETR is connected to a CATU, enable it before we turn * this on. */ - tmc_etr_enable_catu(drvdata); + rc = tmc_etr_enable_catu(drvdata, etr_buf); + if (!rc) { + drvdata->etr_buf = etr_buf; + __tmc_etr_enable_hw(drvdata); + } - __tmc_etr_enable_hw(drvdata); - return 0; + return rc; } /* -- cgit v1.2.3-58-ga151 From 1d364034aaf2fcc7bc23cac2ffde52747376337e Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:06 -0600 Subject: coresight: tmc-etb/etf: Prepare to handle errors enabling Prepare to handle errors in enabling the hardware and report it back to the core driver. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 73 +++++++++++++++---------- 1 file changed, 45 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index b54a3db13fee..36af23d2c0f8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -15,7 +15,7 @@ static int tmc_set_etf_buffer(struct coresight_device *csdev, struct perf_output_handle *handle); -static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) +static void __tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -34,6 +34,12 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } +static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata) +{ + __tmc_etb_enable_hw(drvdata); + return 0; +} + static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) { char *bufp; @@ -73,7 +79,7 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } -static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) +static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -89,6 +95,12 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } +static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata) +{ + __tmc_etf_enable_hw(drvdata); + return 0; +} + static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -171,8 +183,12 @@ static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev) drvdata->buf = buf; } - drvdata->mode = CS_MODE_SYSFS; - tmc_etb_enable_hw(drvdata); + ret = tmc_etb_enable_hw(drvdata); + if (!ret) + drvdata->mode = CS_MODE_SYSFS; + else + /* Free up the buffer if we failed to enable */ + used = false; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -191,27 +207,25 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) struct perf_output_handle *handle = data; spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { - ret = -EINVAL; - goto out; - } - - /* - * In Perf mode there can be only one writer per sink. There - * is also no need to continue if the ETB/ETR is already operated - * from sysFS. - */ - if (drvdata->mode != CS_MODE_DISABLED) { + do { ret = -EINVAL; - goto out; - } + if (drvdata->reading) + break; + /* + * In Perf mode there can be only one writer per sink. There + * is also no need to continue if the ETB/ETF is already + * operated from sysFS. + */ + if (drvdata->mode != CS_MODE_DISABLED) + break; - ret = tmc_set_etf_buffer(csdev, handle); - if (!ret) { - drvdata->mode = CS_MODE_PERF; - tmc_etb_enable_hw(drvdata); - } -out: + ret = tmc_set_etf_buffer(csdev, handle); + if (ret) + break; + ret = tmc_etb_enable_hw(drvdata); + if (!ret) + drvdata->mode = CS_MODE_PERF; + } while (0); spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; @@ -268,6 +282,7 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev) static int tmc_enable_etf_link(struct coresight_device *csdev, int inport, int outport) { + int ret; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -277,12 +292,14 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, return -EBUSY; } - tmc_etf_enable_hw(drvdata); - drvdata->mode = CS_MODE_SYSFS; + ret = tmc_etf_enable_hw(drvdata); + if (!ret) + drvdata->mode = CS_MODE_SYSFS; spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_dbg(drvdata->dev, "TMC-ETF enabled\n"); - return 0; + if (!ret) + dev_dbg(drvdata->dev, "TMC-ETF enabled\n"); + return ret; } static void tmc_disable_etf_link(struct coresight_device *csdev, @@ -576,7 +593,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) * can't be NULL. */ memset(drvdata->buf, 0, drvdata->size); - tmc_etb_enable_hw(drvdata); + __tmc_etb_enable_hw(drvdata); } else { /* * The ETB/ETF is not tracing and the buffer was just read. -- cgit v1.2.3-58-ga151 From e006d89abedd5d04e6033c5614e1bf160b252615 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:07 -0600 Subject: coresight: etm4x: Add support for handling errors Add support for handling errors in enabling the component. The ETM is enabled via cross call to owner CPU. Make necessary changes to report the error back from the cross call. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm4x.c | 39 ++++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index b7379e9cfb30..064e0bfaefd0 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -78,10 +78,14 @@ static int etm4_trace_id(struct coresight_device *csdev) return drvdata->trcid; } -static void etm4_enable_hw(void *info) +struct etm4_enable_arg { + struct etmv4_drvdata *drvdata; + int rc; +}; + +static int etm4_enable_hw(struct etmv4_drvdata *drvdata) { int i; - struct etmv4_drvdata *drvdata = info; struct etmv4_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -178,6 +182,16 @@ static void etm4_enable_hw(void *info) CS_LOCK(drvdata->base); dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); + return 0; +} + +static void etm4_enable_hw_smp_call(void *info) +{ + struct etm4_enable_arg *arg = info; + + if (WARN_ON(!arg)) + return; + arg->rc = etm4_enable_hw(arg->drvdata); } static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, @@ -243,7 +257,7 @@ static int etm4_enable_perf(struct coresight_device *csdev, if (ret) goto out; /* And enable it */ - etm4_enable_hw(drvdata); + ret = etm4_enable_hw(drvdata); out: return ret; @@ -252,6 +266,7 @@ out: static int etm4_enable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct etm4_enable_arg arg = { 0 }; int ret; spin_lock(&drvdata->spinlock); @@ -260,19 +275,17 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) * Executing etm4_enable_hw on the cpu whose ETM is being enabled * ensures that register writes occur when cpu is powered. */ + arg.drvdata = drvdata; ret = smp_call_function_single(drvdata->cpu, - etm4_enable_hw, drvdata, 1); - if (ret) - goto err; - - drvdata->sticky_enable = true; + etm4_enable_hw_smp_call, &arg, 1); + if (!ret) + ret = arg.rc; + if (!ret) + drvdata->sticky_enable = true; spin_unlock(&drvdata->spinlock); - dev_dbg(drvdata->dev, "ETM tracing enabled\n"); - return 0; - -err: - spin_unlock(&drvdata->spinlock); + if (!ret) + dev_dbg(drvdata->dev, "ETM tracing enabled\n"); return ret; } -- cgit v1.2.3-58-ga151 From e2a1551a881f27d1914f182e06b423cc242b43b6 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:08 -0600 Subject: coresight: etm3: Add support for handling errors Add support for reporting errors back from the SMP cross function call for enabling ETM. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 42 ++++++++++++++++++--------- 1 file changed, 28 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 9ce8fba20b0f..206c2381a11a 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -355,11 +355,10 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata, return 0; } -static void etm_enable_hw(void *info) +static int etm_enable_hw(struct etm_drvdata *drvdata) { int i; u32 etmcr; - struct etm_drvdata *drvdata = info; struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -421,6 +420,21 @@ static void etm_enable_hw(void *info) CS_LOCK(drvdata->base); dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); + return 0; +} + +struct etm_enable_arg { + struct etm_drvdata *drvdata; + int rc; +}; + +static void etm_enable_hw_smp_call(void *info) +{ + struct etm_enable_arg *arg = info; + + if (WARN_ON(!arg)) + return; + arg->rc = etm_enable_hw(arg->drvdata); } static int etm_cpu_id(struct coresight_device *csdev) @@ -475,14 +489,13 @@ static int etm_enable_perf(struct coresight_device *csdev, /* Configure the tracer based on the session's specifics */ etm_parse_event_config(drvdata, event); /* And enable it */ - etm_enable_hw(drvdata); - - return 0; + return etm_enable_hw(drvdata); } static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + struct etm_enable_arg arg = { 0 }; int ret; spin_lock(&drvdata->spinlock); @@ -492,20 +505,21 @@ static int etm_enable_sysfs(struct coresight_device *csdev) * hw configuration will take place on the local CPU during bring up. */ if (cpu_online(drvdata->cpu)) { + arg.drvdata = drvdata; ret = smp_call_function_single(drvdata->cpu, - etm_enable_hw, drvdata, 1); - if (ret) - goto err; + etm_enable_hw_smp_call, &arg, 1); + if (!ret) + ret = arg.rc; + if (!ret) + drvdata->sticky_enable = true; + } else { + ret = -ENODEV; } - drvdata->sticky_enable = true; spin_unlock(&drvdata->spinlock); - dev_dbg(drvdata->dev, "ETM tracing enabled\n"); - return 0; - -err: - spin_unlock(&drvdata->spinlock); + if (!ret) + dev_dbg(drvdata->dev, "ETM tracing enabled\n"); return ret; } -- cgit v1.2.3-58-ga151 From 62563e84a8c9c682e07b8cce8678e583a24be504 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:09 -0600 Subject: coresight: etb10: Handle errors enabling the device Prepare the etb10 driver to return errors in enabling the device. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etb10.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 08fa660098f8..824be0c5f592 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -106,7 +106,7 @@ static unsigned int etb_get_buffer_depth(struct etb_drvdata *drvdata) return depth; } -static void etb_enable_hw(struct etb_drvdata *drvdata) +static void __etb_enable_hw(struct etb_drvdata *drvdata) { int i; u32 depth; @@ -134,6 +134,12 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) CS_LOCK(drvdata->base); } +static int etb_enable_hw(struct etb_drvdata *drvdata) +{ + __etb_enable_hw(drvdata); + return 0; +} + static int etb_enable_sysfs(struct coresight_device *csdev) { int ret = 0; @@ -152,8 +158,9 @@ static int etb_enable_sysfs(struct coresight_device *csdev) if (drvdata->mode == CS_MODE_SYSFS) goto out; - drvdata->mode = CS_MODE_SYSFS; - etb_enable_hw(drvdata); + ret = etb_enable_hw(drvdata); + if (!ret) + drvdata->mode = CS_MODE_SYSFS; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -183,8 +190,9 @@ static int etb_enable_perf(struct coresight_device *csdev, void *data) if (ret) goto out; - drvdata->mode = CS_MODE_PERF; - etb_enable_hw(drvdata); + ret = etb_enable_hw(drvdata); + if (!ret) + drvdata->mode = CS_MODE_PERF; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); -- cgit v1.2.3-58-ga151 From 30af4fb619e5126cb3152072e687b377fc9398d6 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:10 -0600 Subject: coresight: dynamic-replicator: Handle multiple connections When a replicator port is enabled, we block the traffic on the other port and route all traffic to the new enabled port. If there are two active trace sessions each targeting the two different paths from the replicator, the second session will disable the first session and route all the data to the second path. ETR / e.g, replicator \ ETB If CPU0 is operated in sysfs mode to ETR and CPU1 is operated in perf mode to ETB, depending on the order in which the replicator is enabled one device is blocked. Ideally we need trace-id for the session to make the right choice. That implies we need a trace-id allocation logic for the coresight subsystem and use that to route the traffic. The short term solution is to only manage the "target port" and leave the other port untouched. That leaves both the paths unaffected, except that some unwanted traffic may be pushed to the paths (if the Trace-IDs are not far enough), which is still fine and can be filtered out while processing rather than silently blocking the data. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-dynamic-replicator.c | 64 ++++++++++++++++------ 1 file changed, 47 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index ebb80438f6a5..97f4673452cb 100644 --- a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c @@ -34,26 +34,42 @@ struct replicator_state { struct coresight_device *csdev; }; +/* + * replicator_reset : Reset the replicator configuration to sane values. + */ +static void replicator_reset(struct replicator_state *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); + writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); + + CS_LOCK(drvdata->base); +} + static int replicator_enable(struct coresight_device *csdev, int inport, int outport) { + u32 reg; struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); + switch (outport) { + case 0: + reg = REPLICATOR_IDFILTER0; + break; + case 1: + reg = REPLICATOR_IDFILTER1; + break; + default: + WARN_ON(1); + return -EINVAL; + } + CS_UNLOCK(drvdata->base); - /* - * Ensure that the other port is disabled - * 0x00 - passing through the replicator unimpeded - * 0xff - disable (or impede) the flow of ATB data - */ - if (outport == 0) { - writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER0); - writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); - } else { - writel_relaxed(0x00, drvdata->base + REPLICATOR_IDFILTER1); - writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); - } + /* Ensure that the outport is enabled. */ + writel_relaxed(0x00, drvdata->base + reg); CS_LOCK(drvdata->base); dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); @@ -63,15 +79,25 @@ static int replicator_enable(struct coresight_device *csdev, int inport, static void replicator_disable(struct coresight_device *csdev, int inport, int outport) { + u32 reg; struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); + switch (outport) { + case 0: + reg = REPLICATOR_IDFILTER0; + break; + case 1: + reg = REPLICATOR_IDFILTER1; + break; + default: + WARN_ON(1); + return; + } + CS_UNLOCK(drvdata->base); /* disable the flow of ATB data through port */ - if (outport == 0) - writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); - else - writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); + writel_relaxed(0xff, drvdata->base + reg); CS_LOCK(drvdata->base); @@ -156,7 +182,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) desc.groups = replicator_groups; drvdata->csdev = coresight_register(&desc); - return PTR_ERR_OR_ZERO(drvdata->csdev); + if (!IS_ERR(drvdata->csdev)) { + replicator_reset(drvdata); + return 0; + } + return PTR_ERR(drvdata->csdev); } #ifdef CONFIG_PM -- cgit v1.2.3-58-ga151 From 2478a6ae4a6a4c8e3f7e9f6f849dffe92e5238e1 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:11 -0600 Subject: coresight: Add support for CLAIM tag protocol Coresight architecture defines CLAIM tags for a device to negotiate control of the components (external agent vs self-hosted). Each device has a pair of registers (CLAIMSET & CLAIMCLR) for managing the CLAIM tags. However, the protocol for the CLAIM tags is IMPLEMENTATION DEFINED. PSCI has recommendations for the use of the CLAIM tags to negotiate controls for external agent vs self-hosted use. This patch implements the recommended protocol by PSCI. The claim/disclaim operations are performed from the device specific drivers. The disadvantage is that the calls are sprinkled in each driver, but this makes the operation much simpler. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-priv.h | 7 +++ drivers/hwtracing/coresight/coresight.c | 86 ++++++++++++++++++++++++++++ include/linux/coresight.h | 20 +++++++ 3 files changed, 113 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index c11da5564a67..579f34943bf1 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -25,6 +25,13 @@ #define CORESIGHT_DEVID 0xfc8 #define CORESIGHT_DEVTYPE 0xfcc + +/* + * Coresight device CLAIM protocol. + * See PSCI - ARM DEN 0022D, Section: 6.8.1 Debug and Trace save and restore. + */ +#define CORESIGHT_CLAIM_SELF_HOSTED BIT(1) + #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f4f50753cf75..2b0df1a0a8df 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -128,6 +128,92 @@ static int coresight_find_link_outport(struct coresight_device *csdev, return -ENODEV; } +static inline u32 coresight_read_claim_tags(void __iomem *base) +{ + return readl_relaxed(base + CORESIGHT_CLAIMCLR); +} + +static inline bool coresight_is_claimed_self_hosted(void __iomem *base) +{ + return coresight_read_claim_tags(base) == CORESIGHT_CLAIM_SELF_HOSTED; +} + +static inline bool coresight_is_claimed_any(void __iomem *base) +{ + return coresight_read_claim_tags(base) != 0; +} + +static inline void coresight_set_claim_tags(void __iomem *base) +{ + writel_relaxed(CORESIGHT_CLAIM_SELF_HOSTED, base + CORESIGHT_CLAIMSET); + isb(); +} + +static inline void coresight_clear_claim_tags(void __iomem *base) +{ + writel_relaxed(CORESIGHT_CLAIM_SELF_HOSTED, base + CORESIGHT_CLAIMCLR); + isb(); +} + +/* + * coresight_claim_device_unlocked : Claim the device for self-hosted usage + * to prevent an external tool from touching this device. As per PSCI + * standards, section "Preserving the execution context" => "Debug and Trace + * save and Restore", DBGCLAIM[1] is reserved for Self-hosted debug/trace and + * DBGCLAIM[0] is reserved for external tools. + * + * Called with CS_UNLOCKed for the component. + * Returns : 0 on success + */ +int coresight_claim_device_unlocked(void __iomem *base) +{ + if (coresight_is_claimed_any(base)) + return -EBUSY; + + coresight_set_claim_tags(base); + if (coresight_is_claimed_self_hosted(base)) + return 0; + /* There was a race setting the tags, clean up and fail */ + coresight_clear_claim_tags(base); + return -EBUSY; +} + +int coresight_claim_device(void __iomem *base) +{ + int rc; + + CS_UNLOCK(base); + rc = coresight_claim_device_unlocked(base); + CS_LOCK(base); + + return rc; +} + +/* + * coresight_disclaim_device_unlocked : Clear the claim tags for the device. + * Called with CS_UNLOCKed for the component. + */ +void coresight_disclaim_device_unlocked(void __iomem *base) +{ + + if (coresight_is_claimed_self_hosted(base)) + coresight_clear_claim_tags(base); + else + /* + * The external agent may have not honoured our claim + * and has manipulated it. Or something else has seriously + * gone wrong in our driver. + */ + WARN_ON_ONCE(1); +} + +void coresight_disclaim_device(void __iomem *base) +{ + CS_UNLOCK(base); + coresight_disclaim_device_unlocked(base); + CS_LOCK(base); +} + static int coresight_enable_sink(struct coresight_device *csdev, u32 mode, void *data) { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 53535821dc25..46c67a764877 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -257,6 +257,13 @@ extern int coresight_enable(struct coresight_device *csdev); extern void coresight_disable(struct coresight_device *csdev); extern int coresight_timeout(void __iomem *addr, u32 offset, int position, int value); + +extern int coresight_claim_device(void __iomem *base); +extern int coresight_claim_device_unlocked(void __iomem *base); + +extern void coresight_disclaim_device(void __iomem *base); +extern void coresight_disclaim_device_unlocked(void __iomem *base); + #else static inline struct coresight_device * coresight_register(struct coresight_desc *desc) { return NULL; } @@ -266,6 +273,19 @@ coresight_enable(struct coresight_device *csdev) { return -ENOSYS; } static inline void coresight_disable(struct coresight_device *csdev) {} static inline int coresight_timeout(void __iomem *addr, u32 offset, int position, int value) { return 1; } +static inline int coresight_claim_device_unlocked(void __iomem *base) +{ + return -EINVAL; +} + +static inline int coresight_claim_device(void __iomem *base) +{ + return -EINVAL; +} + +static inline void coresight_disclaim_device(void __iomem *base) {} +static inline void coresight_disclaim_device_unlocked(void __iomem *base) {} + #endif #ifdef CONFIG_OF -- cgit v1.2.3-58-ga151 From 68a147752d04da73e2786890d78317fc37e6a6a8 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:12 -0600 Subject: coresight: etmx: Claim devices before use Use the CLAIM tags to grab the device for self-hosted usage. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-etm3x.c | 16 +++++++++++++--- drivers/hwtracing/coresight/coresight-etm4x.c | 14 +++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 206c2381a11a..fd5c4cca7db5 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -357,7 +357,7 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata, static int etm_enable_hw(struct etm_drvdata *drvdata) { - int i; + int i, rc; u32 etmcr; struct etm_config *config = &drvdata->config; @@ -369,6 +369,9 @@ static int etm_enable_hw(struct etm_drvdata *drvdata) etm_set_pwrup(drvdata); /* Make sure all registers are accessible */ etm_os_unlock(drvdata); + rc = coresight_claim_device_unlocked(drvdata->base); + if (rc) + goto done; etm_set_prog(drvdata); @@ -417,10 +420,15 @@ static int etm_enable_hw(struct etm_drvdata *drvdata) etm_writel(drvdata, 0x0, ETMVMIDCVR); etm_clr_prog(drvdata); + +done: + if (rc) + etm_set_pwrdwn(drvdata); CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); - return 0; + dev_dbg(drvdata->dev, "cpu: %d enable smp call done: %d\n", + drvdata->cpu, rc); + return rc; } struct etm_enable_arg { @@ -569,6 +577,8 @@ static void etm_disable_hw(void *info) for (i = 0; i < drvdata->nr_cntr; i++) config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); + coresight_disclaim_device_unlocked(drvdata->base); + etm_set_pwrdwn(drvdata); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 064e0bfaefd0..53e2fb6e86f6 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -85,13 +85,17 @@ struct etm4_enable_arg { static int etm4_enable_hw(struct etmv4_drvdata *drvdata) { - int i; + int i, rc; struct etmv4_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); etm4_os_unlock(drvdata); + rc = coresight_claim_device_unlocked(drvdata->base); + if (rc) + goto done; + /* Disable the trace unit before programming trace registers */ writel_relaxed(0, drvdata->base + TRCPRGCTLR); @@ -179,10 +183,12 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata) dev_err(drvdata->dev, "timeout while waiting for Idle Trace Status\n"); +done: CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); - return 0; + dev_dbg(drvdata->dev, "cpu: %d enable smp call done: %d\n", + drvdata->cpu, rc); + return rc; } static void etm4_enable_hw_smp_call(void *info) @@ -342,6 +348,8 @@ static void etm4_disable_hw(void *info) isb(); writel_relaxed(control, drvdata->base + TRCPRGCTLR); + coresight_disclaim_device_unlocked(drvdata->base); + CS_LOCK(drvdata->base); dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); -- cgit v1.2.3-58-ga151 From f13d7c0835c383e318e28cc9f34b002fc8c85f61 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:13 -0600 Subject: coresight: funnel: Claim devices before use Use the CLAIM protocol to grab the ownership of the component. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-funnel.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index ee7a30bf9480..927925151509 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -25,6 +25,7 @@ #define FUNNEL_HOLDTIME_MASK 0xf00 #define FUNNEL_HOLDTIME_SHFT 0x8 #define FUNNEL_HOLDTIME (0x7 << FUNNEL_HOLDTIME_SHFT) +#define FUNNEL_ENSx_MASK 0xff /** * struct funnel_drvdata - specifics associated to a funnel component @@ -42,31 +43,42 @@ struct funnel_drvdata { unsigned long priority; }; -static void funnel_enable_hw(struct funnel_drvdata *drvdata, int port) +static int funnel_enable_hw(struct funnel_drvdata *drvdata, int port) { u32 functl; + int rc = 0; CS_UNLOCK(drvdata->base); functl = readl_relaxed(drvdata->base + FUNNEL_FUNCTL); + /* Claim the device only when we enable the first slave */ + if (!(functl & FUNNEL_ENSx_MASK)) { + rc = coresight_claim_device_unlocked(drvdata->base); + if (rc) + goto done; + } + functl &= ~FUNNEL_HOLDTIME_MASK; functl |= FUNNEL_HOLDTIME; functl |= (1 << port); writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); writel_relaxed(drvdata->priority, drvdata->base + FUNNEL_PRICTL); - +done: CS_LOCK(drvdata->base); + return rc; } static int funnel_enable(struct coresight_device *csdev, int inport, int outport) { + int rc; struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - funnel_enable_hw(drvdata, inport); + rc = funnel_enable_hw(drvdata, inport); - dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport); - return 0; + if (!rc) + dev_dbg(drvdata->dev, "FUNNEL inport %d enabled\n", inport); + return rc; } static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) @@ -79,6 +91,10 @@ static void funnel_disable_hw(struct funnel_drvdata *drvdata, int inport) functl &= ~(1 << inport); writel_relaxed(functl, drvdata->base + FUNNEL_FUNCTL); + /* Disclaim the device if none of the slaves are now active */ + if (!(functl & FUNNEL_ENSx_MASK)) + coresight_disclaim_device_unlocked(drvdata->base); + CS_LOCK(drvdata->base); } -- cgit v1.2.3-58-ga151 From f92201b1ab95b3f17c3fa9df8f88dc521e0b604f Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:14 -0600 Subject: coresight: catu: Claim device before use Use the CLAIM protocol to grab the ownership of the component when in use. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-catu.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-catu.c b/drivers/hwtracing/coresight/coresight-catu.c index ff94e58845b7..170fbb66bda2 100644 --- a/drivers/hwtracing/coresight/coresight-catu.c +++ b/drivers/hwtracing/coresight/coresight-catu.c @@ -406,6 +406,7 @@ static inline int catu_wait_for_ready(struct catu_drvdata *drvdata) static int catu_enable_hw(struct catu_drvdata *drvdata, void *data) { + int rc; u32 control, mode; struct etr_buf *etr_buf = data; @@ -418,6 +419,10 @@ static int catu_enable_hw(struct catu_drvdata *drvdata, void *data) return -EBUSY; } + rc = coresight_claim_device_unlocked(drvdata->base); + if (rc) + return rc; + control |= BIT(CATU_CONTROL_ENABLE); if (etr_buf && etr_buf->mode == ETR_MODE_CATU) { @@ -459,6 +464,7 @@ static int catu_disable_hw(struct catu_drvdata *drvdata) int rc = 0; catu_write_control(drvdata, 0); + coresight_disclaim_device_unlocked(drvdata->base); if (catu_wait_for_ready(drvdata)) { dev_info(drvdata->dev, "Timeout while waiting for READY\n"); rc = -EAGAIN; -- cgit v1.2.3-58-ga151 From 4e33d694376e7f57d788e734dc96fdbd3c408768 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:15 -0600 Subject: coresight: dynamic-replicator: Claim device for use Use CLAIM protocol to make sure the device is available for use. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- .../coresight/coresight-dynamic-replicator.c | 23 +++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index 97f4673452cb..299667b887fc 100644 --- a/drivers/hwtracing/coresight/coresight-dynamic-replicator.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c @@ -41,8 +41,11 @@ static void replicator_reset(struct replicator_state *drvdata) { CS_UNLOCK(drvdata->base); - writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); - writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); + if (!coresight_claim_device_unlocked(drvdata->base)) { + writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER0); + writel_relaxed(0xff, drvdata->base + REPLICATOR_IDFILTER1); + coresight_disclaim_device_unlocked(drvdata->base); + } CS_LOCK(drvdata->base); } @@ -50,6 +53,7 @@ static void replicator_reset(struct replicator_state *drvdata) static int replicator_enable(struct coresight_device *csdev, int inport, int outport) { + int rc = 0; u32 reg; struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -67,13 +71,19 @@ static int replicator_enable(struct coresight_device *csdev, int inport, CS_UNLOCK(drvdata->base); + if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) && + (readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff)) + rc = coresight_claim_device_unlocked(drvdata->base); /* Ensure that the outport is enabled. */ - writel_relaxed(0x00, drvdata->base + reg); + if (!rc) { + writel_relaxed(0x00, drvdata->base + reg); + dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); + } + CS_LOCK(drvdata->base); - dev_dbg(drvdata->dev, "REPLICATOR enabled\n"); - return 0; + return rc; } static void replicator_disable(struct coresight_device *csdev, int inport, @@ -99,6 +109,9 @@ static void replicator_disable(struct coresight_device *csdev, int inport, /* disable the flow of ATB data through port */ writel_relaxed(0xff, drvdata->base + reg); + if ((readl_relaxed(drvdata->base + REPLICATOR_IDFILTER0) == 0xff) && + (readl_relaxed(drvdata->base + REPLICATOR_IDFILTER1) == 0xff)) + coresight_disclaim_device_unlocked(drvdata->base); CS_LOCK(drvdata->base); dev_dbg(drvdata->dev, "REPLICATOR disabled\n"); -- cgit v1.2.3-58-ga151 From bbbecc644a4fff76e8c89b9859701bdcde198d9c Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Thu, 20 Sep 2018 13:18:19 -0600 Subject: coresight: Remove redundant null pointer check before of_node_put and put_device of_node_put and put_device has taken the null pointer check into account. So it is safe to remove the duplicated check. Signed-off-by: zhong jiang Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/of_coresight.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index da71c975e3f7..89092f83567e 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -219,12 +219,9 @@ static int of_coresight_parse_endpoint(struct device *dev, ret = 1; } while (0); - if (rparent) - of_node_put(rparent); - if (rep) - of_node_put(rep); - if (rdev) - put_device(rdev); + of_node_put(rparent); + of_node_put(rep); + put_device(rdev); return ret; } -- cgit v1.2.3-58-ga151 From 10a95d5d86e1f65a1075ea2b5469cc8ef1f424c5 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:07 -0700 Subject: vmw_balloon: handle commands in a single function. By inlining the hypercall interface, we can unify several operations into one central point in the code: - Updating the target. - Updating when a reset is needed. - Update statistics (which will be done later in the patch-set). - Print debug-messages (although they cannot be enabled as selectively). Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 223 +++++++++++++++++++++++---------------------- 1 file changed, 116 insertions(+), 107 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 2543ef1ece17..0a4d5501f805 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -138,6 +138,15 @@ enum vmwballoon_capabilities { #define VMW_BALLOON_BATCH_STATUS_MASK ((1UL << 5) - 1) #define VMW_BALLOON_BATCH_PAGE_MASK (~((1UL << PAGE_SHIFT) - 1)) +#define VMW_BALLOON_CMD_WITH_TARGET_MASK \ + ((1UL << VMW_BALLOON_CMD_GET_TARGET) | \ + (1UL << VMW_BALLOON_CMD_LOCK) | \ + (1UL << VMW_BALLOON_CMD_UNLOCK) | \ + (1UL << VMW_BALLOON_CMD_BATCHED_LOCK) | \ + (1UL << VMW_BALLOON_CMD_BATCHED_UNLOCK) | \ + (1UL << VMW_BALLOON_CMD_BATCHED_2M_LOCK) | \ + (1UL << VMW_BALLOON_CMD_BATCHED_2M_UNLOCK)) + struct vmballoon_batch_page { u64 pages[VMW_BALLOON_BATCH_MAX_PAGES]; }; @@ -159,28 +168,6 @@ static void vmballoon_batch_set_pa(struct vmballoon_batch_page *batch, int idx, batch->pages[idx] = pa; } - -#define VMWARE_BALLOON_CMD(cmd, arg1, arg2, result) \ -({ \ - unsigned long __status, __dummy1, __dummy2, __dummy3; \ - __asm__ __volatile__ ("inl %%dx" : \ - "=a"(__status), \ - "=c"(__dummy1), \ - "=d"(__dummy2), \ - "=b"(result), \ - "=S" (__dummy3) : \ - "0"(VMW_BALLOON_HV_MAGIC), \ - "1"(VMW_BALLOON_CMD_##cmd), \ - "2"(VMW_BALLOON_HV_PORT), \ - "3"(arg1), \ - "4" (arg2) : \ - "memory"); \ - if (VMW_BALLOON_CMD_##cmd == VMW_BALLOON_CMD_START) \ - result = __dummy1; \ - result &= -1UL; \ - __status & -1UL; \ -}) - #ifdef CONFIG_DEBUG_FS struct vmballoon_stats { unsigned int timer; @@ -220,9 +207,9 @@ struct vmballoon; struct vmballoon_ops { void (*add_page)(struct vmballoon *b, int idx, struct page *p); int (*lock)(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages, unsigned int *target); + bool is_2m_pages); int (*unlock)(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages, unsigned int *target); + bool is_2m_pages); }; struct vmballoon_page_size { @@ -272,18 +259,64 @@ struct vmballoon { static struct vmballoon balloon; +static inline unsigned long +__vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, + unsigned long arg2, unsigned long *result) +{ + unsigned long status, dummy1, dummy2, dummy3, local_result; + + asm volatile ("inl %%dx" : + "=a"(status), + "=c"(dummy1), + "=d"(dummy2), + "=b"(local_result), + "=S"(dummy3) : + "0"(VMW_BALLOON_HV_MAGIC), + "1"(cmd), + "2"(VMW_BALLOON_HV_PORT), + "3"(arg1), + "4"(arg2) : + "memory"); + + /* update the result if needed */ + if (result) + *result = (cmd == VMW_BALLOON_CMD_START) ? dummy1 : + local_result; + + /* update target when applicable */ + if (status == VMW_BALLOON_SUCCESS && + ((1ul << cmd) & VMW_BALLOON_CMD_WITH_TARGET_MASK)) + b->target = local_result; + + /* mark reset required accordingly */ + if (status == VMW_BALLOON_ERROR_RESET) + b->reset_required = true; + + return status; +} + +static __always_inline unsigned long +vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, + unsigned long arg2) +{ + unsigned long dummy; + + return __vmballoon_cmd(b, cmd, arg1, arg2, &dummy); +} + /* * Send "start" command to the host, communicating supported version * of the protocol. */ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) { - unsigned long status, capabilities, dummy = 0; + unsigned long status, capabilities; bool success; STATS_INC(b->stats.start); - status = VMWARE_BALLOON_CMD(START, req_caps, dummy, capabilities); + status = __vmballoon_cmd(b, VMW_BALLOON_CMD_START, req_caps, 0, + &capabilities); switch (status) { case VMW_BALLOON_SUCCESS_WITH_CAPABILITIES: @@ -316,21 +349,6 @@ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) return success; } -static bool vmballoon_check_status(struct vmballoon *b, unsigned long status) -{ - switch (status) { - case VMW_BALLOON_SUCCESS: - return true; - - case VMW_BALLOON_ERROR_RESET: - b->reset_required = true; - /* fall through */ - - default: - return false; - } -} - /* * Communicate guest type to the host so that it can adjust ballooning * algorithm to the one most appropriate for the guest. This command @@ -339,14 +357,14 @@ static bool vmballoon_check_status(struct vmballoon *b, unsigned long status) */ static bool vmballoon_send_guest_id(struct vmballoon *b) { - unsigned long status, dummy = 0; + unsigned long status; - status = VMWARE_BALLOON_CMD(GUEST_ID, VMW_BALLOON_GUEST_ID, dummy, - dummy); + status = vmballoon_cmd(b, VMW_BALLOON_CMD_GUEST_ID, + VMW_BALLOON_GUEST_ID, 0); STATS_INC(b->stats.guest_type); - if (vmballoon_check_status(b, status)) + if (status == VMW_BALLOON_SUCCESS) return true; pr_debug("%s - failed, hv returns %ld\n", __func__, status); @@ -365,12 +383,10 @@ static u16 vmballoon_page_size(bool is_2m_page) /* * Retrieve desired balloon size from the host. */ -static bool vmballoon_send_get_target(struct vmballoon *b, u32 *new_target) +static bool vmballoon_send_get_target(struct vmballoon *b) { unsigned long status; - unsigned long target; unsigned long limit; - unsigned long dummy = 0; u32 limit32; /* @@ -389,11 +405,10 @@ static bool vmballoon_send_get_target(struct vmballoon *b, u32 *new_target) /* update stats */ STATS_INC(b->stats.target); - status = VMWARE_BALLOON_CMD(GET_TARGET, limit, dummy, target); - if (vmballoon_check_status(b, status)) { - *new_target = target; + status = vmballoon_cmd(b, VMW_BALLOON_CMD_GET_TARGET, limit, 0); + + if (status == VMW_BALLOON_SUCCESS) return true; - } pr_debug("%s - failed, hv returns %ld\n", __func__, status); STATS_INC(b->stats.target_fail); @@ -406,9 +421,9 @@ static bool vmballoon_send_get_target(struct vmballoon *b, u32 *new_target) * check the return value and maybe submit a different page. */ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, - unsigned int *hv_status, unsigned int *target) + unsigned int *hv_status) { - unsigned long status, dummy = 0; + unsigned long status; u32 pfn32; pfn32 = (u32)pfn; @@ -417,8 +432,9 @@ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, STATS_INC(b->stats.lock[false]); - *hv_status = status = VMWARE_BALLOON_CMD(LOCK, pfn, dummy, *target); - if (vmballoon_check_status(b, status)) + *hv_status = status = vmballoon_cmd(b, VMW_BALLOON_CMD_LOCK, pfn, 0); + + if (status == VMW_BALLOON_SUCCESS) return 0; pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); @@ -427,21 +443,19 @@ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, } static int vmballoon_send_batched_lock(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages, unsigned int *target) + unsigned int num_pages, bool is_2m_pages) { - unsigned long status; unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); + unsigned long status, cmd; STATS_INC(b->stats.lock[is_2m_pages]); - if (is_2m_pages) - status = VMWARE_BALLOON_CMD(BATCHED_2M_LOCK, pfn, num_pages, - *target); - else - status = VMWARE_BALLOON_CMD(BATCHED_LOCK, pfn, num_pages, - *target); + cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : + VMW_BALLOON_CMD_BATCHED_LOCK; - if (vmballoon_check_status(b, status)) + status = vmballoon_cmd(b, cmd, pfn, num_pages); + + if (status == VMW_BALLOON_SUCCESS) return 0; pr_debug("%s - batch ppn %lx, hv returns %ld\n", __func__, pfn, status); @@ -453,10 +467,9 @@ static int vmballoon_send_batched_lock(struct vmballoon *b, * Notify the host that guest intends to release given page back into * the pool of available (to the guest) pages. */ -static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn, - unsigned int *target) +static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn) { - unsigned long status, dummy = 0; + unsigned long status; u32 pfn32; pfn32 = (u32)pfn; @@ -465,8 +478,8 @@ static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn, STATS_INC(b->stats.unlock[false]); - status = VMWARE_BALLOON_CMD(UNLOCK, pfn, dummy, *target); - if (vmballoon_check_status(b, status)) + status = vmballoon_cmd(b, VMW_BALLOON_CMD_UNLOCK, pfn, 0); + if (status == VMW_BALLOON_SUCCESS) return true; pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); @@ -475,21 +488,19 @@ static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn, } static bool vmballoon_send_batched_unlock(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages, unsigned int *target) + unsigned int num_pages, bool is_2m_pages) { - unsigned long status; unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); + unsigned long status, cmd; STATS_INC(b->stats.unlock[is_2m_pages]); - if (is_2m_pages) - status = VMWARE_BALLOON_CMD(BATCHED_2M_UNLOCK, pfn, num_pages, - *target); - else - status = VMWARE_BALLOON_CMD(BATCHED_UNLOCK, pfn, num_pages, - *target); + cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : + VMW_BALLOON_CMD_BATCHED_UNLOCK; + + status = vmballoon_cmd(b, cmd, pfn, num_pages); - if (vmballoon_check_status(b, status)) + if (status == VMW_BALLOON_SUCCESS) return true; pr_debug("%s - batch ppn %lx, hv returns %ld\n", __func__, pfn, status); @@ -550,7 +561,7 @@ static void vmballoon_pop(struct vmballoon *b) * inflation cycle. */ static int vmballoon_lock_page(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages, unsigned int *target) + bool is_2m_pages) { int locked, hv_status; struct page *page = b->page; @@ -558,8 +569,8 @@ static int vmballoon_lock_page(struct vmballoon *b, unsigned int num_pages, /* is_2m_pages can never happen as 2m pages support implies batching */ - locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status, - target); + locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status); + if (locked) { STATS_INC(b->stats.refused_alloc[false]); @@ -594,13 +605,13 @@ static int vmballoon_lock_page(struct vmballoon *b, unsigned int num_pages, } static int vmballoon_lock_batched_page(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages, unsigned int *target) + unsigned int num_pages, bool is_2m_pages) { int locked, i; u16 size_per_page = vmballoon_page_size(is_2m_pages); - locked = vmballoon_send_batched_lock(b, num_pages, is_2m_pages, - target); + locked = vmballoon_send_batched_lock(b, num_pages, is_2m_pages); + if (locked > 0) { for (i = 0; i < num_pages; i++) { u64 pa = vmballoon_batch_get_pa(b->batch_page, i); @@ -653,14 +664,14 @@ static int vmballoon_lock_batched_page(struct vmballoon *b, * to use, if needed. */ static int vmballoon_unlock_page(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages, unsigned int *target) + bool is_2m_pages) { struct page *page = b->page; struct vmballoon_page_size *page_size = &b->page_sizes[false]; /* is_2m_pages can never happen as 2m pages support implies batching */ - if (!vmballoon_send_unlock_page(b, page_to_pfn(page), target)) { + if (!vmballoon_send_unlock_page(b, page_to_pfn(page))) { list_add(&page->lru, &page_size->pages); return -EIO; } @@ -676,15 +687,14 @@ static int vmballoon_unlock_page(struct vmballoon *b, unsigned int num_pages, } static int vmballoon_unlock_batched_page(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages, - unsigned int *target) + unsigned int num_pages, bool is_2m_pages) { int locked, i, ret = 0; bool hv_success; u16 size_per_page = vmballoon_page_size(is_2m_pages); - hv_success = vmballoon_send_batched_unlock(b, num_pages, is_2m_pages, - target); + hv_success = vmballoon_send_batched_unlock(b, num_pages, is_2m_pages); + if (!hv_success) ret = -EIO; @@ -799,7 +809,7 @@ static void vmballoon_inflate(struct vmballoon *b) STATS_INC(b->stats.alloc_fail[is_2m_pages]); if (is_2m_pages) { - b->ops->lock(b, num_pages, true, &b->target); + b->ops->lock(b, num_pages, true); /* * ignore errors from locking as we now switch @@ -838,8 +848,8 @@ static void vmballoon_inflate(struct vmballoon *b) b->ops->add_page(b, num_pages++, page); if (num_pages == b->batch_max_pages) { - error = b->ops->lock(b, num_pages, is_2m_pages, - &b->target); + error = b->ops->lock(b, num_pages, is_2m_pages); + num_pages = 0; if (error) break; @@ -849,7 +859,7 @@ static void vmballoon_inflate(struct vmballoon *b) } if (num_pages > 0) - b->ops->lock(b, num_pages, is_2m_pages, &b->target); + b->ops->lock(b, num_pages, is_2m_pages); vmballoon_release_refused_pages(b, true); vmballoon_release_refused_pages(b, false); @@ -887,7 +897,7 @@ static void vmballoon_deflate(struct vmballoon *b) int error; error = b->ops->unlock(b, num_pages, - is_2m_pages, &b->target); + is_2m_pages); num_pages = 0; if (error) return; @@ -897,7 +907,7 @@ static void vmballoon_deflate(struct vmballoon *b) } if (num_pages > 0) - b->ops->unlock(b, num_pages, is_2m_pages, &b->target); + b->ops->unlock(b, num_pages, is_2m_pages); } } @@ -942,10 +952,9 @@ static void vmballoon_doorbell(void *client_data) */ static void vmballoon_vmci_cleanup(struct vmballoon *b) { - int error; + vmballoon_cmd(b, VMW_BALLOON_CMD_VMCI_DOORBELL_SET, + VMCI_INVALID_ID, VMCI_INVALID_ID); - VMWARE_BALLOON_CMD(VMCI_DOORBELL_SET, VMCI_INVALID_ID, - VMCI_INVALID_ID, error); STATS_INC(b->stats.doorbell_unset); if (!vmci_handle_is_invalid(b->vmci_doorbell)) { @@ -959,7 +968,7 @@ static void vmballoon_vmci_cleanup(struct vmballoon *b) */ static int vmballoon_vmci_init(struct vmballoon *b) { - unsigned long error, dummy; + unsigned long error; if ((b->capabilities & VMW_BALLOON_SIGNALLED_WAKEUP_CMD) == 0) return 0; @@ -971,8 +980,9 @@ static int vmballoon_vmci_init(struct vmballoon *b) if (error != VMCI_SUCCESS) goto fail; - error = VMWARE_BALLOON_CMD(VMCI_DOORBELL_SET, b->vmci_doorbell.context, - b->vmci_doorbell.resource, dummy); + error = __vmballoon_cmd(b, VMW_BALLOON_CMD_VMCI_DOORBELL_SET, + b->vmci_doorbell.context, + b->vmci_doorbell.resource, NULL); STATS_INC(b->stats.doorbell_set); @@ -1038,17 +1048,16 @@ static void vmballoon_work(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct vmballoon *b = container_of(dwork, struct vmballoon, dwork); - unsigned int target; STATS_INC(b->stats.timer); if (b->reset_required) vmballoon_reset(b); - if (!b->reset_required && vmballoon_send_get_target(b, &target)) { - /* update target, adjust size */ - b->target = target; + if (!b->reset_required && vmballoon_send_get_target(b)) { + unsigned long target = b->target; + /* update target, adjust size */ if (b->size < target) vmballoon_inflate(b); else if (target == 0 || -- cgit v1.2.3-58-ga151 From 681311848c8fea6a3025296d88f3876920d577a0 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:08 -0700 Subject: vmw_balloon: unify commands tracing and stats Now that we have a single point, unify the tracing and collecting the statistics for commands and their failure. While it might somewhat reduce the control over debugging, it cleans the code a lot. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 116 ++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 75 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 0a4d5501f805..2ed4875319c8 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -105,6 +105,7 @@ enum vmwballoon_capabilities { #define VMW_BALLOON_CMD_BATCHED_2M_UNLOCK 9 #define VMW_BALLOON_CMD_VMCI_DOORBELL_SET 10 +#define VMW_BALLOON_CMD_NUM 11 /* error codes */ #define VMW_BALLOON_SUCCESS 0 @@ -147,6 +148,19 @@ enum vmwballoon_capabilities { (1UL << VMW_BALLOON_CMD_BATCHED_2M_LOCK) | \ (1UL << VMW_BALLOON_CMD_BATCHED_2M_UNLOCK)) +static const char * const vmballoon_cmd_names[] = { + [VMW_BALLOON_CMD_START] = "start", + [VMW_BALLOON_CMD_GET_TARGET] = "target", + [VMW_BALLOON_CMD_LOCK] = "lock", + [VMW_BALLOON_CMD_UNLOCK] = "unlock", + [VMW_BALLOON_CMD_GUEST_ID] = "guestType", + [VMW_BALLOON_CMD_BATCHED_LOCK] = "batchLock", + [VMW_BALLOON_CMD_BATCHED_UNLOCK] = "batchUnlock", + [VMW_BALLOON_CMD_BATCHED_2M_LOCK] = "2m-lock", + [VMW_BALLOON_CMD_BATCHED_2M_UNLOCK] = "2m-unlock", + [VMW_BALLOON_CMD_VMCI_DOORBELL_SET] = "doorbellSet" +}; + struct vmballoon_batch_page { u64 pages[VMW_BALLOON_BATCH_MAX_PAGES]; }; @@ -182,19 +196,9 @@ struct vmballoon_stats { unsigned int refused_free[VMW_BALLOON_NUM_PAGE_SIZES]; unsigned int free[VMW_BALLOON_NUM_PAGE_SIZES]; - /* monitor operations */ - unsigned int lock[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int lock_fail[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int unlock[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int unlock_fail[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int target; - unsigned int target_fail; - unsigned int start; - unsigned int start_fail; - unsigned int guest_type; - unsigned int guest_type_fail; - unsigned int doorbell_set; - unsigned int doorbell_unset; + /* Monitor operations. */ + unsigned long ops[VMW_BALLOON_CMD_NUM]; + unsigned long ops_fail[VMW_BALLOON_CMD_NUM]; }; #define STATS_INC(stat) (stat)++ @@ -265,6 +269,8 @@ __vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, { unsigned long status, dummy1, dummy2, dummy3, local_result; + STATS_INC(b->stats.ops[cmd]); + asm volatile ("inl %%dx" : "=a"(status), "=c"(dummy1), @@ -288,6 +294,14 @@ __vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, ((1ul << cmd) & VMW_BALLOON_CMD_WITH_TARGET_MASK)) b->target = local_result; + if (status != VMW_BALLOON_SUCCESS && + status != VMW_BALLOON_SUCCESS_WITH_CAPABILITIES) { + STATS_INC(b->stats.ops_fail[cmd]); + pr_debug("%s: %s [0x%lx,0x%lx) failed, returned %ld\n", + __func__, vmballoon_cmd_names[cmd], arg1, arg2, + status); + } + /* mark reset required accordingly */ if (status == VMW_BALLOON_ERROR_RESET) b->reset_required = true; @@ -313,8 +327,6 @@ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) unsigned long status, capabilities; bool success; - STATS_INC(b->stats.start); - status = __vmballoon_cmd(b, VMW_BALLOON_CMD_START, req_caps, 0, &capabilities); @@ -342,10 +354,6 @@ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) else b->supported_page_sizes = 1; - if (!success) { - pr_debug("%s - failed, hv returns %ld\n", __func__, status); - STATS_INC(b->stats.start_fail); - } return success; } @@ -362,13 +370,9 @@ static bool vmballoon_send_guest_id(struct vmballoon *b) status = vmballoon_cmd(b, VMW_BALLOON_CMD_GUEST_ID, VMW_BALLOON_GUEST_ID, 0); - STATS_INC(b->stats.guest_type); - if (status == VMW_BALLOON_SUCCESS) return true; - pr_debug("%s - failed, hv returns %ld\n", __func__, status); - STATS_INC(b->stats.guest_type_fail); return false; } @@ -402,16 +406,11 @@ static bool vmballoon_send_get_target(struct vmballoon *b) if (limit != limit32) return false; - /* update stats */ - STATS_INC(b->stats.target); - status = vmballoon_cmd(b, VMW_BALLOON_CMD_GET_TARGET, limit, 0); if (status == VMW_BALLOON_SUCCESS) return true; - pr_debug("%s - failed, hv returns %ld\n", __func__, status); - STATS_INC(b->stats.target_fail); return false; } @@ -430,15 +429,11 @@ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, if (pfn32 != pfn) return -EINVAL; - STATS_INC(b->stats.lock[false]); - *hv_status = status = vmballoon_cmd(b, VMW_BALLOON_CMD_LOCK, pfn, 0); if (status == VMW_BALLOON_SUCCESS) return 0; - pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); - STATS_INC(b->stats.lock_fail[false]); return -EIO; } @@ -448,8 +443,6 @@ static int vmballoon_send_batched_lock(struct vmballoon *b, unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); unsigned long status, cmd; - STATS_INC(b->stats.lock[is_2m_pages]); - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : VMW_BALLOON_CMD_BATCHED_LOCK; @@ -458,8 +451,6 @@ static int vmballoon_send_batched_lock(struct vmballoon *b, if (status == VMW_BALLOON_SUCCESS) return 0; - pr_debug("%s - batch ppn %lx, hv returns %ld\n", __func__, pfn, status); - STATS_INC(b->stats.lock_fail[is_2m_pages]); return 1; } @@ -476,15 +467,8 @@ static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn) if (pfn32 != pfn) return false; - STATS_INC(b->stats.unlock[false]); - status = vmballoon_cmd(b, VMW_BALLOON_CMD_UNLOCK, pfn, 0); - if (status == VMW_BALLOON_SUCCESS) - return true; - - pr_debug("%s - ppn %lx, hv returns %ld\n", __func__, pfn, status); - STATS_INC(b->stats.unlock_fail[false]); - return false; + return status == VMW_BALLOON_SUCCESS; } static bool vmballoon_send_batched_unlock(struct vmballoon *b, @@ -493,19 +477,12 @@ static bool vmballoon_send_batched_unlock(struct vmballoon *b, unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); unsigned long status, cmd; - STATS_INC(b->stats.unlock[is_2m_pages]); - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : VMW_BALLOON_CMD_BATCHED_UNLOCK; status = vmballoon_cmd(b, cmd, pfn, num_pages); - if (status == VMW_BALLOON_SUCCESS) - return true; - - pr_debug("%s - batch ppn %lx, hv returns %ld\n", __func__, pfn, status); - STATS_INC(b->stats.unlock_fail[is_2m_pages]); - return false; + return status == VMW_BALLOON_SUCCESS; } static struct page *vmballoon_alloc_page(gfp_t flags, bool is_2m_page) @@ -955,8 +932,6 @@ static void vmballoon_vmci_cleanup(struct vmballoon *b) vmballoon_cmd(b, VMW_BALLOON_CMD_VMCI_DOORBELL_SET, VMCI_INVALID_ID, VMCI_INVALID_ID); - STATS_INC(b->stats.doorbell_unset); - if (!vmci_handle_is_invalid(b->vmci_doorbell)) { vmci_doorbell_destroy(b->vmci_doorbell); b->vmci_doorbell = VMCI_INVALID_HANDLE; @@ -984,8 +959,6 @@ static int vmballoon_vmci_init(struct vmballoon *b) b->vmci_doorbell.context, b->vmci_doorbell.resource, NULL); - STATS_INC(b->stats.doorbell_set); - if (error != VMW_BALLOON_SUCCESS) goto fail; @@ -1082,6 +1055,7 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset) { struct vmballoon *b = f->private; struct vmballoon_stats *stats = &b->stats; + int i; /* format capabilities info */ seq_printf(f, @@ -1097,17 +1071,19 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset) "current: %8d pages\n", b->target, b->size); + for (i = 0; i < VMW_BALLOON_CMD_NUM; i++) { + if (vmballoon_cmd_names[i] == NULL) + continue; + + seq_printf(f, "%-22s: %16lu (%lu failed)\n", + vmballoon_cmd_names[i], stats->ops[i], + stats->ops_fail[i]); + } + seq_printf(f, "\n" "timer: %8u\n" "doorbell: %8u\n" - "start: %8u (%4u failed)\n" - "guestType: %8u (%4u failed)\n" - "2m-lock: %8u (%4u failed)\n" - "lock: %8u (%4u failed)\n" - "2m-unlock: %8u (%4u failed)\n" - "unlock: %8u (%4u failed)\n" - "target: %8u (%4u failed)\n" "prim2mAlloc: %8u (%4u failed)\n" "primNoSleepAlloc: %8u (%4u failed)\n" "primCanSleepAlloc: %8u (%4u failed)\n" @@ -1116,26 +1092,16 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset) "err2mAlloc: %8u\n" "errAlloc: %8u\n" "err2mFree: %8u\n" - "errFree: %8u\n" - "doorbellSet: %8u\n" - "doorbellUnset: %8u\n", + "errFree: %8u\n", stats->timer, stats->doorbell, - stats->start, stats->start_fail, - stats->guest_type, stats->guest_type_fail, - stats->lock[true], stats->lock_fail[true], - stats->lock[false], stats->lock_fail[false], - stats->unlock[true], stats->unlock_fail[true], - stats->unlock[false], stats->unlock_fail[false], - stats->target, stats->target_fail, stats->alloc[true], stats->alloc_fail[true], stats->alloc[false], stats->alloc_fail[false], stats->sleep_alloc, stats->sleep_alloc_fail, stats->free[true], stats->free[false], stats->refused_alloc[true], stats->refused_alloc[false], - stats->refused_free[true], stats->refused_free[false], - stats->doorbell_set, stats->doorbell_unset); + stats->refused_free[true], stats->refused_free[false]); return 0; } -- cgit v1.2.3-58-ga151 From 4c9a7d6a771d6e9038049a49e26b350a6ee4ffd2 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:09 -0700 Subject: vmw_balloon: merge send_lock and send_unlock path The lock and unlock code paths are very similar, so avoid the duplicate code by merging them together. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 62 ++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 2ed4875319c8..5e5f61f207f4 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -420,16 +420,18 @@ static bool vmballoon_send_get_target(struct vmballoon *b) * check the return value and maybe submit a different page. */ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, - unsigned int *hv_status) + unsigned int *hv_status, bool lock) { - unsigned long status; + unsigned long status, cmd; u32 pfn32; pfn32 = (u32)pfn; if (pfn32 != pfn) return -EINVAL; - *hv_status = status = vmballoon_cmd(b, VMW_BALLOON_CMD_LOCK, pfn, 0); + cmd = lock ? VMW_BALLOON_CMD_LOCK : VMW_BALLOON_CMD_UNLOCK; + + *hv_status = status = vmballoon_cmd(b, cmd, pfn, 0); if (status == VMW_BALLOON_SUCCESS) return 0; @@ -438,13 +440,18 @@ static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, } static int vmballoon_send_batched_lock(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages) + unsigned int num_pages, bool is_2m_pages, + bool lock) { unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); unsigned long status, cmd; - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : - VMW_BALLOON_CMD_BATCHED_LOCK; + if (lock) + cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : + VMW_BALLOON_CMD_BATCHED_LOCK; + else + cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : + VMW_BALLOON_CMD_BATCHED_UNLOCK; status = vmballoon_cmd(b, cmd, pfn, num_pages); @@ -454,37 +461,6 @@ static int vmballoon_send_batched_lock(struct vmballoon *b, return 1; } -/* - * Notify the host that guest intends to release given page back into - * the pool of available (to the guest) pages. - */ -static bool vmballoon_send_unlock_page(struct vmballoon *b, unsigned long pfn) -{ - unsigned long status; - u32 pfn32; - - pfn32 = (u32)pfn; - if (pfn32 != pfn) - return false; - - status = vmballoon_cmd(b, VMW_BALLOON_CMD_UNLOCK, pfn, 0); - return status == VMW_BALLOON_SUCCESS; -} - -static bool vmballoon_send_batched_unlock(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages) -{ - unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); - unsigned long status, cmd; - - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : - VMW_BALLOON_CMD_BATCHED_UNLOCK; - - status = vmballoon_cmd(b, cmd, pfn, num_pages); - - return status == VMW_BALLOON_SUCCESS; -} - static struct page *vmballoon_alloc_page(gfp_t flags, bool is_2m_page) { if (is_2m_page) @@ -546,7 +522,8 @@ static int vmballoon_lock_page(struct vmballoon *b, unsigned int num_pages, /* is_2m_pages can never happen as 2m pages support implies batching */ - locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status); + locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status, + true); if (locked) { STATS_INC(b->stats.refused_alloc[false]); @@ -587,7 +564,7 @@ static int vmballoon_lock_batched_page(struct vmballoon *b, int locked, i; u16 size_per_page = vmballoon_page_size(is_2m_pages); - locked = vmballoon_send_batched_lock(b, num_pages, is_2m_pages); + locked = vmballoon_send_batched_lock(b, num_pages, is_2m_pages, true); if (locked > 0) { for (i = 0; i < num_pages; i++) { @@ -645,10 +622,12 @@ static int vmballoon_unlock_page(struct vmballoon *b, unsigned int num_pages, { struct page *page = b->page; struct vmballoon_page_size *page_size = &b->page_sizes[false]; + unsigned int hv_status; /* is_2m_pages can never happen as 2m pages support implies batching */ - if (!vmballoon_send_unlock_page(b, page_to_pfn(page))) { + if (!vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status, + false)) { list_add(&page->lru, &page_size->pages); return -EIO; } @@ -670,7 +649,8 @@ static int vmballoon_unlock_batched_page(struct vmballoon *b, bool hv_success; u16 size_per_page = vmballoon_page_size(is_2m_pages); - hv_success = vmballoon_send_batched_unlock(b, num_pages, is_2m_pages); + hv_success = vmballoon_send_batched_lock(b, num_pages, is_2m_pages, + false); if (!hv_success) ret = -EIO; -- cgit v1.2.3-58-ga151 From 6c94875799eaf99bfdbb0efce21d75e1c56e96d5 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:10 -0700 Subject: vmw_balloon: simplifying batch access The use of accessors for batch entries complicates the code and makes it less readable. Remove it an instead use bit-fields. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 81 +++++++++++++++++----------------------------- 1 file changed, 30 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 5e5f61f207f4..7af70fc988b9 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -121,24 +121,6 @@ enum vmwballoon_capabilities { #define VMW_BALLOON_SUCCESS_WITH_CAPABILITIES (0x03000000) -/* Batch page description */ - -/* - * Layout of a page in the batch page: - * - * +-------------+----------+--------+ - * | | | | - * | Page number | Reserved | Status | - * | | | | - * +-------------+----------+--------+ - * 64 PAGE_SHIFT 6 0 - * - * The reserved field should be set to 0. - */ -#define VMW_BALLOON_BATCH_MAX_PAGES (PAGE_SIZE / sizeof(u64)) -#define VMW_BALLOON_BATCH_STATUS_MASK ((1UL << 5) - 1) -#define VMW_BALLOON_BATCH_PAGE_MASK (~((1UL << PAGE_SHIFT) - 1)) - #define VMW_BALLOON_CMD_WITH_TARGET_MASK \ ((1UL << VMW_BALLOON_CMD_GET_TARGET) | \ (1UL << VMW_BALLOON_CMD_LOCK) | \ @@ -161,27 +143,6 @@ static const char * const vmballoon_cmd_names[] = { [VMW_BALLOON_CMD_VMCI_DOORBELL_SET] = "doorbellSet" }; -struct vmballoon_batch_page { - u64 pages[VMW_BALLOON_BATCH_MAX_PAGES]; -}; - -static u64 vmballoon_batch_get_pa(struct vmballoon_batch_page *batch, int idx) -{ - return batch->pages[idx] & VMW_BALLOON_BATCH_PAGE_MASK; -} - -static int vmballoon_batch_get_status(struct vmballoon_batch_page *batch, - int idx) -{ - return (int)(batch->pages[idx] & VMW_BALLOON_BATCH_STATUS_MASK); -} - -static void vmballoon_batch_set_pa(struct vmballoon_batch_page *batch, int idx, - u64 pa) -{ - batch->pages[idx] = pa; -} - #ifdef CONFIG_DEBUG_FS struct vmballoon_stats { unsigned int timer; @@ -225,6 +186,19 @@ struct vmballoon_page_size { unsigned int n_refused_pages; }; +/** + * struct vmballoon_batch_entry - a batch entry for lock or unlock. + * + * @status: the status of the operation, which is written by the hypervisor. + * @reserved: reserved for future use. Must be set to zero. + * @pfn: the physical frame number of the page to be locked or unlocked. + */ +struct vmballoon_batch_entry { + u64 status : 5; + u64 reserved : PAGE_SHIFT - 5; + u64 pfn : 52; +} __packed; + struct vmballoon { struct vmballoon_page_size page_sizes[VMW_BALLOON_NUM_PAGE_SIZES]; @@ -240,7 +214,14 @@ struct vmballoon { unsigned long capabilities; - struct vmballoon_batch_page *batch_page; + /** + * @batch_page: pointer to communication batch page. + * + * When batching is used, batch_page points to a page, which holds up to + * %VMW_BALLOON_BATCH_MAX_PAGES entries for locking or unlocking. + */ + struct vmballoon_batch_entry *batch_page; + unsigned int batch_max_pages; struct page *page; @@ -568,8 +549,7 @@ static int vmballoon_lock_batched_page(struct vmballoon *b, if (locked > 0) { for (i = 0; i < num_pages; i++) { - u64 pa = vmballoon_batch_get_pa(b->batch_page, i); - struct page *p = pfn_to_page(pa >> PAGE_SHIFT); + struct page *p = pfn_to_page(b->batch_page[i].pfn); vmballoon_free_page(p, is_2m_pages); } @@ -578,12 +558,11 @@ static int vmballoon_lock_batched_page(struct vmballoon *b, } for (i = 0; i < num_pages; i++) { - u64 pa = vmballoon_batch_get_pa(b->batch_page, i); - struct page *p = pfn_to_page(pa >> PAGE_SHIFT); + struct page *p = pfn_to_page(b->batch_page[i].pfn); struct vmballoon_page_size *page_size = &b->page_sizes[is_2m_pages]; - locked = vmballoon_batch_get_status(b->batch_page, i); + locked = b->batch_page[i].status; switch (locked) { case VMW_BALLOON_SUCCESS: @@ -656,12 +635,11 @@ static int vmballoon_unlock_batched_page(struct vmballoon *b, ret = -EIO; for (i = 0; i < num_pages; i++) { - u64 pa = vmballoon_batch_get_pa(b->batch_page, i); - struct page *p = pfn_to_page(pa >> PAGE_SHIFT); + struct page *p = pfn_to_page(b->batch_page[i].pfn); struct vmballoon_page_size *page_size = &b->page_sizes[is_2m_pages]; - locked = vmballoon_batch_get_status(b->batch_page, i); + locked = b->batch_page[i].status; if (!hv_success || locked != VMW_BALLOON_SUCCESS) { /* * That page wasn't successfully unlocked by the @@ -710,8 +688,8 @@ static void vmballoon_add_page(struct vmballoon *b, int idx, struct page *p) static void vmballoon_add_batched_page(struct vmballoon *b, int idx, struct page *p) { - vmballoon_batch_set_pa(b->batch_page, idx, - (u64)page_to_pfn(p) << PAGE_SHIFT); + b->batch_page[idx] = (struct vmballoon_batch_entry) + { .pfn = page_to_pfn(p) }; } /* @@ -967,7 +945,8 @@ static void vmballoon_reset(struct vmballoon *b) if ((b->capabilities & VMW_BALLOON_BATCHED_CMDS) != 0) { b->ops = &vmballoon_batched_ops; - b->batch_max_pages = VMW_BALLOON_BATCH_MAX_PAGES; + b->batch_max_pages = PAGE_SIZE / sizeof(struct + vmballoon_batch_entry); if (!vmballoon_init_batching(b)) { /* * We failed to initialize batching, inform the monitor -- cgit v1.2.3-58-ga151 From 622074a9f6f7ab07547c71eb998404a0e5c931e1 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:11 -0700 Subject: vmw_balloon: remove sleeping allocations Splitting the allocations between sleeping and non-sleeping made some sort of sense as long as rate-limiting was enabled. Now that it is removed, we need to decide - either we want sleeping allocations or not. Since no other Linux balloon driver (hv, Xen, virtio) uses sleeping allocations, use the same approach. We do distinguish, however, between 2MB allocations and 4kB allocations and prevent reclamation on 2MB. In both cases, we avoid using emergency low-memory pools, as it may cause undesired effects. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 67 +++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 7af70fc988b9..952308997499 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -37,20 +37,20 @@ MODULE_ALIAS("vmware_vmmemctl"); MODULE_LICENSE("GPL"); /* - * Use __GFP_HIGHMEM to allow pages from HIGHMEM zone. We don't - * allow wait (__GFP_RECLAIM) for NOSLEEP page allocations. Use - * __GFP_NOWARN, to suppress page allocation failure warnings. + * Use __GFP_HIGHMEM to allow pages from HIGHMEM zone. We don't allow wait + * (__GFP_RECLAIM) for huge page allocations. Use __GFP_NOWARN, to suppress page + * allocation failure warnings. Disallow access to emergency low-memory pools. */ -#define VMW_PAGE_ALLOC_NOSLEEP (__GFP_HIGHMEM|__GFP_NOWARN) +#define VMW_HUGE_PAGE_ALLOC_FLAGS (__GFP_HIGHMEM|__GFP_NOWARN| \ + __GFP_NOMEMALLOC) /* - * Use GFP_HIGHUSER when executing in a separate kernel thread - * context and allocation can sleep. This is less stressful to - * the guest memory system, since it allows the thread to block - * while memory is reclaimed, and won't take pages from emergency - * low-memory pools. + * Use __GFP_HIGHMEM to allow pages from HIGHMEM zone. We allow lightweight + * reclamation (__GFP_NORETRY). Use __GFP_NOWARN, to suppress page allocation + * failure warnings. Disallow access to emergency low-memory pools. */ -#define VMW_PAGE_ALLOC_CANSLEEP (GFP_HIGHUSER) +#define VMW_PAGE_ALLOC_FLAGS (__GFP_HIGHMEM|__GFP_NOWARN| \ + __GFP_NOMEMALLOC|__GFP_NORETRY) /* Maximum number of refused pages we accumulate during inflation cycle */ #define VMW_BALLOON_MAX_REFUSED 16 @@ -151,8 +151,6 @@ struct vmballoon_stats { /* allocation statistics */ unsigned int alloc[VMW_BALLOON_NUM_PAGE_SIZES]; unsigned int alloc_fail[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int sleep_alloc; - unsigned int sleep_alloc_fail; unsigned int refused_alloc[VMW_BALLOON_NUM_PAGE_SIZES]; unsigned int refused_free[VMW_BALLOON_NUM_PAGE_SIZES]; unsigned int free[VMW_BALLOON_NUM_PAGE_SIZES]; @@ -442,12 +440,13 @@ static int vmballoon_send_batched_lock(struct vmballoon *b, return 1; } -static struct page *vmballoon_alloc_page(gfp_t flags, bool is_2m_page) +static struct page *vmballoon_alloc_page(bool is_2m_page) { if (is_2m_page) - return alloc_pages(flags, VMW_BALLOON_2M_SHIFT); + return alloc_pages(VMW_HUGE_PAGE_ALLOC_FLAGS, + VMW_BALLOON_2M_SHIFT); - return alloc_page(flags); + return alloc_page(VMW_PAGE_ALLOC_FLAGS); } static void vmballoon_free_page(struct page *page, bool is_2m_page) @@ -701,7 +700,6 @@ static void vmballoon_inflate(struct vmballoon *b) { unsigned int num_pages = 0; int error = 0; - gfp_t flags = VMW_PAGE_ALLOC_NOSLEEP; bool is_2m_pages; pr_debug("%s - size: %d, target %d\n", __func__, b->size, b->target); @@ -734,15 +732,10 @@ static void vmballoon_inflate(struct vmballoon *b) < b->target) { struct page *page; - if (flags == VMW_PAGE_ALLOC_NOSLEEP) - STATS_INC(b->stats.alloc[is_2m_pages]); - else - STATS_INC(b->stats.sleep_alloc); - - page = vmballoon_alloc_page(flags, is_2m_pages); + STATS_INC(b->stats.alloc[is_2m_pages]); + page = vmballoon_alloc_page(is_2m_pages); if (!page) { STATS_INC(b->stats.alloc_fail[is_2m_pages]); - if (is_2m_pages) { b->ops->lock(b, num_pages, true); @@ -756,29 +749,7 @@ static void vmballoon_inflate(struct vmballoon *b) is_2m_pages = false; continue; } - - if (flags == VMW_PAGE_ALLOC_CANSLEEP) { - /* - * CANSLEEP page allocation failed, so guest - * is under severe memory pressure. We just log - * the event, but do not stop the inflation - * due to its negative impact on performance. - */ - STATS_INC(b->stats.sleep_alloc_fail); - break; - } - - /* - * NOSLEEP page allocation failed, so the guest is - * under memory pressure. Slowing down page alloctions - * seems to be reasonable, but doing so might actually - * cause the hypervisor to throttle us down, resulting - * in degraded performance. We will count on the - * scheduler and standard memory management mechanisms - * for now. - */ - flags = VMW_PAGE_ALLOC_CANSLEEP; - continue; + break; } b->ops->add_page(b, num_pages++, page); @@ -1044,8 +1015,7 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset) "timer: %8u\n" "doorbell: %8u\n" "prim2mAlloc: %8u (%4u failed)\n" - "primNoSleepAlloc: %8u (%4u failed)\n" - "primCanSleepAlloc: %8u (%4u failed)\n" + "prim4kAlloc: %8u (%4u failed)\n" "prim2mFree: %8u\n" "primFree: %8u\n" "err2mAlloc: %8u\n" @@ -1056,7 +1026,6 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset) stats->doorbell, stats->alloc[true], stats->alloc_fail[true], stats->alloc[false], stats->alloc_fail[false], - stats->sleep_alloc, stats->sleep_alloc_fail, stats->free[true], stats->free[false], stats->refused_alloc[true], stats->refused_alloc[false], -- cgit v1.2.3-58-ga151 From df8d0d42afe853bb1e15e7543ee710b241bb3b35 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:12 -0700 Subject: vmw_balloon: change batch/single lock abstractions The current abstractions for batch vs single operations seem suboptimal and complicate the implementation of additional features (OOM, compaction). The immediate problem of the current abstractions is that they cause differences in how operations are handled when batching is on or off. For example, the refused_alloc counter is not updated when batching is on. These discrepancies are caused by code redundancies. Instead, this patch presents three type of operations, according to whether batching is on or off: (1) add page, (2) communication with the hypervisor and (3) retrieving the status of a page. To avoid the overhead of virtual functions, and since we do not expect additional interfaces for communication with the hypervisor, we use static keys instead of virtual functions. Finally, while we are at it, change vmballoon_init_batching() to return int instead of bool, to be consistent in the return type and avoid potential coding errors. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 358 +++++++++++++++++++++------------------------ 1 file changed, 165 insertions(+), 193 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 952308997499..96dde120bbd5 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -165,15 +165,7 @@ struct vmballoon_stats { #define STATS_INC(stat) #endif -struct vmballoon; - -struct vmballoon_ops { - void (*add_page)(struct vmballoon *b, int idx, struct page *p); - int (*lock)(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages); - int (*unlock)(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages); -}; +static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching); struct vmballoon_page_size { /* list of reserved physical pages */ @@ -223,8 +215,6 @@ struct vmballoon { unsigned int batch_max_pages; struct page *page; - const struct vmballoon_ops *ops; - #ifdef CONFIG_DEBUG_FS /* statistics */ struct vmballoon_stats stats; @@ -393,53 +383,6 @@ static bool vmballoon_send_get_target(struct vmballoon *b) return false; } -/* - * Notify the host about allocated page so that host can use it without - * fear that guest will need it. Host may reject some pages, we need to - * check the return value and maybe submit a different page. - */ -static int vmballoon_send_lock_page(struct vmballoon *b, unsigned long pfn, - unsigned int *hv_status, bool lock) -{ - unsigned long status, cmd; - u32 pfn32; - - pfn32 = (u32)pfn; - if (pfn32 != pfn) - return -EINVAL; - - cmd = lock ? VMW_BALLOON_CMD_LOCK : VMW_BALLOON_CMD_UNLOCK; - - *hv_status = status = vmballoon_cmd(b, cmd, pfn, 0); - - if (status == VMW_BALLOON_SUCCESS) - return 0; - - return -EIO; -} - -static int vmballoon_send_batched_lock(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages, - bool lock) -{ - unsigned long pfn = PHYS_PFN(virt_to_phys(b->batch_page)); - unsigned long status, cmd; - - if (lock) - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : - VMW_BALLOON_CMD_BATCHED_LOCK; - else - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : - VMW_BALLOON_CMD_BATCHED_UNLOCK; - - status = vmballoon_cmd(b, cmd, pfn, num_pages); - - if (status == VMW_BALLOON_SUCCESS) - return 0; - - return 1; -} - static struct page *vmballoon_alloc_page(bool is_2m_page) { if (is_2m_page) @@ -488,88 +431,126 @@ static void vmballoon_pop(struct vmballoon *b) b->batch_page = NULL; } -/* - * Notify the host of a ballooned page. If host rejects the page put it on the - * refuse list, those refused page are then released at the end of the - * inflation cycle. +/** + * vmballoon_status_page - returns the status of (un)lock operation + * + * @b: pointer to the balloon. + * @idx: index for the page for which the operation is performed. + * @p: pointer to where the page struct is returned. + * + * Following a lock or unlock operation, returns the status of the operation for + * an individual page. Provides the page that the operation was performed on on + * the @page argument. + * + * Returns: The status of a lock or unlock operation for an individual page. */ -static int vmballoon_lock_page(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages) +static unsigned long vmballoon_status_page(struct vmballoon *b, int idx, + struct page **p) { - int locked, hv_status; - struct page *page = b->page; - struct vmballoon_page_size *page_size = &b->page_sizes[false]; - - /* is_2m_pages can never happen as 2m pages support implies batching */ - - locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status, - true); + if (static_branch_likely(&vmw_balloon_batching)) { + /* batching mode */ + *p = pfn_to_page(b->batch_page[idx].pfn); + return b->batch_page[idx].status; + } - if (locked) { - STATS_INC(b->stats.refused_alloc[false]); + /* non-batching mode */ + *p = b->page; - if (locked == -EIO && - (hv_status == VMW_BALLOON_ERROR_RESET || - hv_status == VMW_BALLOON_ERROR_PPN_NOTNEEDED)) { - vmballoon_free_page(page, false); - return -EIO; - } + /* + * If a failure occurs, the indication will be provided in the status + * of the entire operation, which is considered before the individual + * page status. So for non-batching mode, the indication is always of + * success. + */ + return VMW_BALLOON_SUCCESS; +} - /* - * Place page on the list of non-balloonable pages - * and retry allocation, unless we already accumulated - * too many of them, in which case take a breather. - */ - if (page_size->n_refused_pages < VMW_BALLOON_MAX_REFUSED) { - page_size->n_refused_pages++; - list_add(&page->lru, &page_size->refused_pages); - } else { - vmballoon_free_page(page, false); - } - return locked; +/** + * vmballoon_lock_op - notifies the host about inflated/deflated pages. + * @b: pointer to the balloon. + * @num_pages: number of inflated/deflated pages. + * @is_2m_pages: whether the page(s) are 2M (or 4k). + * @lock: whether the operation is lock (or unlock). + * + * Notify the host about page(s) that were ballooned (or removed from the + * balloon) so that host can use it without fear that guest will need it (or + * stop using them since the VM does). Host may reject some pages, we need to + * check the return value and maybe submit a different page. The pages that are + * inflated/deflated are pointed by @b->page. + * + * Return: result as provided by the hypervisor. + */ +static unsigned long vmballoon_lock_op(struct vmballoon *b, + unsigned int num_pages, + bool is_2m_pages, bool lock) +{ + unsigned long cmd, pfn; + + if (static_branch_likely(&vmw_balloon_batching)) { + if (lock) + cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : + VMW_BALLOON_CMD_BATCHED_LOCK; + else + cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : + VMW_BALLOON_CMD_BATCHED_UNLOCK; + + pfn = PHYS_PFN(virt_to_phys(b->batch_page)); + } else { + cmd = lock ? VMW_BALLOON_CMD_LOCK : VMW_BALLOON_CMD_UNLOCK; + pfn = page_to_pfn(b->page); + + /* In non-batching mode, PFNs must fit in 32-bit */ + if (unlikely(pfn != (u32)pfn)) + return VMW_BALLOON_ERROR_PPN_INVALID; } - /* track allocated page */ - list_add(&page->lru, &page_size->pages); - - /* update balloon size */ - b->size++; - - return 0; + return vmballoon_cmd(b, cmd, pfn, num_pages); } -static int vmballoon_lock_batched_page(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages) +static int vmballoon_lock(struct vmballoon *b, unsigned int num_pages, + bool is_2m_pages) { - int locked, i; + unsigned long batch_status; + int i; u16 size_per_page = vmballoon_page_size(is_2m_pages); - locked = vmballoon_send_batched_lock(b, num_pages, is_2m_pages, true); - - if (locked > 0) { - for (i = 0; i < num_pages; i++) { - struct page *p = pfn_to_page(b->batch_page[i].pfn); - - vmballoon_free_page(p, is_2m_pages); - } - - return -EIO; - } + batch_status = vmballoon_lock_op(b, num_pages, is_2m_pages, true); for (i = 0; i < num_pages; i++) { - struct page *p = pfn_to_page(b->batch_page[i].pfn); + unsigned long status; + struct page *p; struct vmballoon_page_size *page_size = &b->page_sizes[is_2m_pages]; - locked = b->batch_page[i].status; + status = vmballoon_status_page(b, i, &p); + + /* + * Failure of the whole batch overrides a single operation + * results. + */ + if (batch_status != VMW_BALLOON_SUCCESS) + status = batch_status; - switch (locked) { - case VMW_BALLOON_SUCCESS: + if (status == VMW_BALLOON_SUCCESS) { + /* track allocated page */ list_add(&p->lru, &page_size->pages); + + /* update balloon size */ b->size += size_per_page; - break; + continue; + } + + /* Error occurred */ + STATS_INC(b->stats.refused_alloc[is_2m_pages]); + + switch (status) { case VMW_BALLOON_ERROR_PPN_PINNED: case VMW_BALLOON_ERROR_PPN_INVALID: + /* + * Place page on the list of non-balloonable pages + * and retry allocation, unless we already accumulated + * too many of them, in which case take a breather. + */ if (page_size->n_refused_pages < VMW_BALLOON_MAX_REFUSED) { list_add(&p->lru, &page_size->refused_pages); @@ -587,7 +568,7 @@ static int vmballoon_lock_batched_page(struct vmballoon *b, } } - return 0; + return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO; } /* @@ -595,51 +576,31 @@ static int vmballoon_lock_batched_page(struct vmballoon *b, * the host so it can make sure the page will be available for the guest * to use, if needed. */ -static int vmballoon_unlock_page(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages) +static int vmballoon_unlock(struct vmballoon *b, unsigned int num_pages, + bool is_2m_pages) { - struct page *page = b->page; - struct vmballoon_page_size *page_size = &b->page_sizes[false]; - unsigned int hv_status; - - /* is_2m_pages can never happen as 2m pages support implies batching */ - - if (!vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status, - false)) { - list_add(&page->lru, &page_size->pages); - return -EIO; - } - - /* deallocate page */ - vmballoon_free_page(page, false); - STATS_INC(b->stats.free[false]); - - /* update balloon size */ - b->size--; - - return 0; -} - -static int vmballoon_unlock_batched_page(struct vmballoon *b, - unsigned int num_pages, bool is_2m_pages) -{ - int locked, i, ret = 0; - bool hv_success; + int i; + unsigned long batch_status; u16 size_per_page = vmballoon_page_size(is_2m_pages); - hv_success = vmballoon_send_batched_lock(b, num_pages, is_2m_pages, - false); - - if (!hv_success) - ret = -EIO; + batch_status = vmballoon_lock_op(b, num_pages, is_2m_pages, false); for (i = 0; i < num_pages; i++) { - struct page *p = pfn_to_page(b->batch_page[i].pfn); - struct vmballoon_page_size *page_size = - &b->page_sizes[is_2m_pages]; + struct vmballoon_page_size *page_size; + unsigned long status; + struct page *p; - locked = b->batch_page[i].status; - if (!hv_success || locked != VMW_BALLOON_SUCCESS) { + status = vmballoon_status_page(b, i, &p); + page_size = &b->page_sizes[is_2m_pages]; + + /* + * Failure of the whole batch overrides a single operation + * results. + */ + if (batch_status != VMW_BALLOON_SUCCESS) + status = batch_status; + + if (status != VMW_BALLOON_SUCCESS) { /* * That page wasn't successfully unlocked by the * hypervisor, re-add it to the list of pages owned by @@ -656,7 +617,7 @@ static int vmballoon_unlock_batched_page(struct vmballoon *b, } } - return ret; + return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO; } /* @@ -681,14 +642,11 @@ static void vmballoon_release_refused_pages(struct vmballoon *b, static void vmballoon_add_page(struct vmballoon *b, int idx, struct page *p) { - b->page = p; -} - -static void vmballoon_add_batched_page(struct vmballoon *b, int idx, - struct page *p) -{ - b->batch_page[idx] = (struct vmballoon_batch_entry) + if (static_branch_likely(&vmw_balloon_batching)) + b->batch_page[idx] = (struct vmballoon_batch_entry) { .pfn = page_to_pfn(p) }; + else + b->page = p; } /* @@ -737,7 +695,7 @@ static void vmballoon_inflate(struct vmballoon *b) if (!page) { STATS_INC(b->stats.alloc_fail[is_2m_pages]); if (is_2m_pages) { - b->ops->lock(b, num_pages, true); + vmballoon_lock(b, num_pages, true); /* * ignore errors from locking as we now switch @@ -752,9 +710,9 @@ static void vmballoon_inflate(struct vmballoon *b) break; } - b->ops->add_page(b, num_pages++, page); + vmballoon_add_page(b, num_pages++, page); if (num_pages == b->batch_max_pages) { - error = b->ops->lock(b, num_pages, is_2m_pages); + error = vmballoon_lock(b, num_pages, is_2m_pages); num_pages = 0; if (error) @@ -765,7 +723,7 @@ static void vmballoon_inflate(struct vmballoon *b) } if (num_pages > 0) - b->ops->lock(b, num_pages, is_2m_pages); + vmballoon_lock(b, num_pages, is_2m_pages); vmballoon_release_refused_pages(b, true); vmballoon_release_refused_pages(b, false); @@ -797,12 +755,12 @@ static void vmballoon_deflate(struct vmballoon *b) break; list_del(&page->lru); - b->ops->add_page(b, num_pages++, page); + vmballoon_add_page(b, num_pages++, page); if (num_pages == b->batch_max_pages) { int error; - error = b->ops->unlock(b, num_pages, + error = vmballoon_unlock(b, num_pages, is_2m_pages); num_pages = 0; if (error) @@ -813,32 +771,50 @@ static void vmballoon_deflate(struct vmballoon *b) } if (num_pages > 0) - b->ops->unlock(b, num_pages, is_2m_pages); + vmballoon_unlock(b, num_pages, is_2m_pages); } } -static const struct vmballoon_ops vmballoon_basic_ops = { - .add_page = vmballoon_add_page, - .lock = vmballoon_lock_page, - .unlock = vmballoon_unlock_page -}; - -static const struct vmballoon_ops vmballoon_batched_ops = { - .add_page = vmballoon_add_batched_page, - .lock = vmballoon_lock_batched_page, - .unlock = vmballoon_unlock_batched_page -}; +/** + * vmballoon_deinit_batching - disables batching mode. + * + * @b: pointer to &struct vmballoon. + * + * Disables batching, by deallocating the page for communication with the + * hypervisor and disabling the static key to indicate that batching is off. + */ +static void vmballoon_deinit_batching(struct vmballoon *b) +{ + free_page((unsigned long)b->batch_page); + b->batch_page = NULL; + static_branch_disable(&vmw_balloon_batching); + b->batch_max_pages = 1; +} -static bool vmballoon_init_batching(struct vmballoon *b) +/** + * vmballoon_init_batching - enable batching mode. + * + * @b: pointer to &struct vmballoon. + * + * Enables batching, by allocating a page for communication with the hypervisor + * and enabling the static_key to use batching. + * + * Return: zero on success or an appropriate error-code. + */ +static int vmballoon_init_batching(struct vmballoon *b) { struct page *page; page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!page) - return false; + return -ENOMEM; b->batch_page = page_address(page); - return true; + b->batch_max_pages = PAGE_SIZE / sizeof(struct vmballoon_batch_entry); + + static_branch_enable(&vmw_balloon_batching); + + return 0; } /* @@ -915,10 +891,7 @@ static void vmballoon_reset(struct vmballoon *b) return; if ((b->capabilities & VMW_BALLOON_BATCHED_CMDS) != 0) { - b->ops = &vmballoon_batched_ops; - b->batch_max_pages = PAGE_SIZE / sizeof(struct - vmballoon_batch_entry); - if (!vmballoon_init_batching(b)) { + if (vmballoon_init_batching(b)) { /* * We failed to initialize batching, inform the monitor * about it by sending a null capability. @@ -929,8 +902,7 @@ static void vmballoon_reset(struct vmballoon *b) return; } } else if ((b->capabilities & VMW_BALLOON_BASIC_CMDS) != 0) { - b->ops = &vmballoon_basic_ops; - b->batch_max_pages = 1; + vmballoon_deinit_batching(b); } b->reset_required = false; -- cgit v1.2.3-58-ga151 From 8fa3c61a79868ad3529f1dc61709a4c46adab467 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:13 -0700 Subject: vmw_balloon: treat all refused pages equally Currently, when the hypervisor rejects a page during lock operation, the VM treats pages differently according to the error-code: in certain cases the page is immediately freed, and in others it is put on a rejection list and only freed later. The behavior does not make too much sense. If the page is freed immediately it is very likely to be used again in the next batch of allocations, and be rejected again. In addition, for support of compaction and OOM notifiers, we wish to separate the logic that communicates with the hypervisor (as well as analyzes the status of each page) from the logic that allocates or free pages. Treat all errors the same way, queuing the pages on the refuse list. Move to the next allocation size (4k) when too many pages are refused. Free the refused pages when moving to the next size to avoid situations in which too much memory is waiting to be freed on the refused list. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 52 ++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 96dde120bbd5..4e067d269706 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -543,29 +543,13 @@ static int vmballoon_lock(struct vmballoon *b, unsigned int num_pages, /* Error occurred */ STATS_INC(b->stats.refused_alloc[is_2m_pages]); - switch (status) { - case VMW_BALLOON_ERROR_PPN_PINNED: - case VMW_BALLOON_ERROR_PPN_INVALID: - /* - * Place page on the list of non-balloonable pages - * and retry allocation, unless we already accumulated - * too many of them, in which case take a breather. - */ - if (page_size->n_refused_pages - < VMW_BALLOON_MAX_REFUSED) { - list_add(&p->lru, &page_size->refused_pages); - page_size->n_refused_pages++; - break; - } - /* Fallthrough */ - case VMW_BALLOON_ERROR_RESET: - case VMW_BALLOON_ERROR_PPN_NOTNEEDED: - vmballoon_free_page(p, is_2m_pages); - break; - default: - /* This should never happen */ - WARN_ON_ONCE(true); - } + /* + * Place page on the list of non-balloonable pages + * and retry allocation, unless we already accumulated + * too many of them, in which case take a breather. + */ + list_add(&p->lru, &page_size->refused_pages); + page_size->n_refused_pages++; } return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO; @@ -712,9 +696,31 @@ static void vmballoon_inflate(struct vmballoon *b) vmballoon_add_page(b, num_pages++, page); if (num_pages == b->batch_max_pages) { + struct vmballoon_page_size *page_size = + &b->page_sizes[is_2m_pages]; + error = vmballoon_lock(b, num_pages, is_2m_pages); num_pages = 0; + + /* + * Stop allocating this page size if we already + * accumulated too many pages that the hypervisor + * refused. + */ + if (page_size->n_refused_pages >= + VMW_BALLOON_MAX_REFUSED) { + if (!is_2m_pages) + break; + + /* + * Release the refused pages as we move to 4k + * pages. + */ + vmballoon_release_refused_pages(b, true); + is_2m_pages = true; + } + if (error) break; } -- cgit v1.2.3-58-ga151 From 25acbdd7e76158099dd9768cdd8ffdeaf2dcb021 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:14 -0700 Subject: vmw_balloon: rename VMW_BALLOON_2M_SHIFT to VMW_BALLOON_2M_ORDER The name of the macro'd VMW_BALLOON_2M_SHIFT is misleading. The value reflects 2M huge-page order. Unfortunately, we cannot use HPAGE_PMD_ORDER, since it is not defined when transparent huge-pages are off, so we need to define our own one. Rename it to VMW_BALLOON_2M_ORDER. No functional change. Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 4e067d269706..626abe65eddb 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -77,7 +77,7 @@ enum vmwballoon_capabilities { | VMW_BALLOON_BATCHED_2M_CMDS \ | VMW_BALLOON_SIGNALLED_WAKEUP_CMD) -#define VMW_BALLOON_2M_SHIFT (9) +#define VMW_BALLOON_2M_ORDER (PMD_SHIFT - PAGE_SHIFT) #define VMW_BALLOON_NUM_PAGE_SIZES (2) /* @@ -348,7 +348,7 @@ static bool vmballoon_send_guest_id(struct vmballoon *b) static u16 vmballoon_page_size(bool is_2m_page) { if (is_2m_page) - return 1 << VMW_BALLOON_2M_SHIFT; + return 1 << VMW_BALLOON_2M_ORDER; return 1; } @@ -387,7 +387,7 @@ static struct page *vmballoon_alloc_page(bool is_2m_page) { if (is_2m_page) return alloc_pages(VMW_HUGE_PAGE_ALLOC_FLAGS, - VMW_BALLOON_2M_SHIFT); + VMW_BALLOON_2M_ORDER); return alloc_page(VMW_PAGE_ALLOC_FLAGS); } @@ -395,7 +395,7 @@ static struct page *vmballoon_alloc_page(bool is_2m_page) static void vmballoon_free_page(struct page *page, bool is_2m_page) { if (is_2m_page) - __free_pages(page, VMW_BALLOON_2M_SHIFT); + __free_pages(page, VMW_BALLOON_2M_ORDER); else __free_page(page); } -- cgit v1.2.3-58-ga151 From 8b079cd00f70cf8ad2df00fb36674683d637fae0 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:15 -0700 Subject: vmw_balloon: refactor change size from vmballoon_work The required change in the balloon size is currently computed in vmballoon_work(), vmballoon_inflate() and vmballoon_deflate(). Refactor it to simplify the next patches. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 75 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 626abe65eddb..0d9f223d463e 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -633,6 +633,37 @@ static void vmballoon_add_page(struct vmballoon *b, int idx, struct page *p) b->page = p; } +/** + * vmballoon_change - retrieve the required balloon change + * + * @b: pointer for the balloon. + * + * Return: the required change for the balloon size. A positive number + * indicates inflation, a negative number indicates a deflation. + */ +static int64_t vmballoon_change(struct vmballoon *b) +{ + int64_t size, target; + + size = b->size; + target = b->target; + + /* + * We must cast first because of int sizes + * Otherwise we might get huge positives instead of negatives + */ + + if (b->reset_required) + return 0; + + /* consider a 2MB slack on deflate, unless the balloon is emptied */ + if (target < size && size - target < vmballoon_page_size(true) && + target != 0) + return 0; + + return target - size; +} + /* * Inflate the balloon towards its target size. Note that we try to limit * the rate of allocation to make sure we are not choking the rest of the @@ -644,8 +675,6 @@ static void vmballoon_inflate(struct vmballoon *b) int error = 0; bool is_2m_pages; - pr_debug("%s - size: %d, target %d\n", __func__, b->size, b->target); - /* * First try NOSLEEP page allocations to inflate balloon. * @@ -667,11 +696,8 @@ static void vmballoon_inflate(struct vmballoon *b) */ is_2m_pages = b->supported_page_sizes == VMW_BALLOON_NUM_PAGE_SIZES; - pr_debug("%s - goal: %d", __func__, b->target - b->size); - - while (!b->reset_required && - b->size + num_pages * vmballoon_page_size(is_2m_pages) - < b->target) { + while ((int64_t)(num_pages * vmballoon_page_size(is_2m_pages)) < + vmballoon_change(b)) { struct page *page; STATS_INC(b->stats.alloc[is_2m_pages]); @@ -742,8 +768,6 @@ static void vmballoon_deflate(struct vmballoon *b) { unsigned is_2m_pages; - pr_debug("%s - size: %d, target %d\n", __func__, b->size, b->target); - /* free pages to reach target */ for (is_2m_pages = 0; is_2m_pages < b->supported_page_sizes; is_2m_pages++) { @@ -753,11 +777,9 @@ static void vmballoon_deflate(struct vmballoon *b) &b->page_sizes[is_2m_pages]; list_for_each_entry_safe(page, next, &page_size->pages, lru) { - if (b->reset_required || - (b->target > 0 && - b->size - num_pages - * vmballoon_page_size(is_2m_pages) - < b->target + vmballoon_page_size(true))) + if ((int64_t)(num_pages * + vmballoon_page_size(is_2m_pages)) >= + -vmballoon_change(b)) break; list_del(&page->lru); @@ -921,28 +943,35 @@ static void vmballoon_reset(struct vmballoon *b) pr_err("failed to send guest ID to the host\n"); } -/* - * Balloon work function: reset protocol, if needed, get the new size and - * adjust balloon as needed. Repeat in 1 sec. +/** + * vmballoon_work - periodic balloon worker for reset, inflation and deflation. + * + * @work: pointer to the &work_struct which is provided by the workqueue. + * + * Resets the protocol if needed, gets the new size and adjusts balloon as + * needed. Repeat in 1 sec. */ static void vmballoon_work(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct vmballoon *b = container_of(dwork, struct vmballoon, dwork); + int64_t change = 0; STATS_INC(b->stats.timer); if (b->reset_required) vmballoon_reset(b); - if (!b->reset_required && vmballoon_send_get_target(b)) { - unsigned long target = b->target; + if (vmballoon_send_get_target(b)) + change = vmballoon_change(b); + + if (change != 0) { + pr_debug("%s - size: %u, target %u", __func__, + b->size, b->target); - /* update target, adjust size */ - if (b->size < target) + if (change > 0) vmballoon_inflate(b); - else if (target == 0 || - b->size > target + vmballoon_page_size(true)) + else /* (change < 0) */ vmballoon_deflate(b); } -- cgit v1.2.3-58-ga151 From 0395be3ece114cda496dafd1fb337288c8305e7b Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:16 -0700 Subject: vmw_balloon: simplify vmballoon_send_get_target() As we want to leave as little as possible on the global balloon structure, to avoid possible future races, we want to get rid sysinfo. We can actually get the total_ram directly, and simplify the logic of vmballoon_send_get_target() a little. While we are doing that, let's return int and avoid mistakes due to bool/int conversions. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 0d9f223d463e..3c80a21e0f91 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -223,8 +223,6 @@ struct vmballoon { struct dentry *dbg_entry; #endif - struct sysinfo sysinfo; - struct delayed_work dwork; struct vmci_handle vmci_doorbell; @@ -353,34 +351,29 @@ static u16 vmballoon_page_size(bool is_2m_page) return 1; } -/* - * Retrieve desired balloon size from the host. +/** + * vmballoon_send_get_target() - Retrieve desired balloon size from the host. + * + * @b: pointer to the balloon. + * + * Return: zero on success, EINVAL if limit does not fit in 32-bit, as required + * by the host-guest protocol and EIO if an error occurred in communicating with + * the host. */ -static bool vmballoon_send_get_target(struct vmballoon *b) +static int vmballoon_send_get_target(struct vmballoon *b) { unsigned long status; unsigned long limit; - u32 limit32; - /* - * si_meminfo() is cheap. Moreover, we want to provide dynamic - * max balloon size later. So let us call si_meminfo() every - * iteration. - */ - si_meminfo(&b->sysinfo); - limit = b->sysinfo.totalram; + limit = totalram_pages; /* Ensure limit fits in 32-bits */ - limit32 = (u32)limit; - if (limit != limit32) - return false; + if (limit != (u32)limit) + return -EINVAL; status = vmballoon_cmd(b, VMW_BALLOON_CMD_GET_TARGET, limit, 0); - if (status == VMW_BALLOON_SUCCESS) - return true; - - return false; + return status == VMW_BALLOON_SUCCESS ? 0 : -EIO; } static struct page *vmballoon_alloc_page(bool is_2m_page) @@ -962,7 +955,7 @@ static void vmballoon_work(struct work_struct *work) if (b->reset_required) vmballoon_reset(b); - if (vmballoon_send_get_target(b)) + if (!vmballoon_send_get_target(b)) change = vmballoon_change(b); if (change != 0) { -- cgit v1.2.3-58-ga151 From c7b3690fb1522b926557e59c6581ce849bcf9947 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:17 -0700 Subject: vmw_balloon: stats rework To allow the balloon statistics to be updated concurrently, we change the statistics to be held per core and aggregate it when needed. To avoid the memory overhead of keeping the statistics per core, and since it is likely not used by most users, we start updating the statistics only after the first use. A read-write semaphore is used to protect the statistics initialization and avoid races. This semaphore is (and will) be used to protect configuration changes during reset. While we are at it, address some other issues: change the statistics update to inline functions instead of define; use ulong for saving the statistics; and clean the statistics printouts. Note that this patch changes the format of the outputs. If there are any automatic tools that use the statistics, they might fail. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 384 +++++++++++++++++++++++++++++++++------------ 1 file changed, 281 insertions(+), 103 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 3c80a21e0f91..0a2bdaf5773b 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include @@ -78,46 +80,94 @@ enum vmwballoon_capabilities { | VMW_BALLOON_SIGNALLED_WAKEUP_CMD) #define VMW_BALLOON_2M_ORDER (PMD_SHIFT - PAGE_SHIFT) -#define VMW_BALLOON_NUM_PAGE_SIZES (2) -/* - * Backdoor commands availability: +enum vmballoon_page_size_type { + VMW_BALLOON_4K_PAGE, + VMW_BALLOON_2M_PAGE, + VMW_BALLOON_LAST_SIZE = VMW_BALLOON_2M_PAGE +}; + +#define VMW_BALLOON_NUM_PAGE_SIZES (VMW_BALLOON_LAST_SIZE + 1) + +enum vmballoon_op_stat_type { + VMW_BALLOON_OP_STAT, + VMW_BALLOON_OP_FAIL_STAT +}; + +#define VMW_BALLOON_OP_STAT_TYPES (VMW_BALLOON_OP_FAIL_STAT + 1) + +/** + * enum vmballoon_cmd_type - backdoor commands. + * + * Availability of the commands is as followed: + * + * %VMW_BALLOON_CMD_START, %VMW_BALLOON_CMD_GET_TARGET and + * %VMW_BALLOON_CMD_GUEST_ID are always available. + * + * If the host reports %VMW_BALLOON_BASIC_CMDS are supported then + * %VMW_BALLOON_CMD_LOCK and %VMW_BALLOON_CMD_UNLOCK commands are available. * - * START, GET_TARGET and GUEST_ID are always available, + * If the host reports %VMW_BALLOON_BATCHED_CMDS are supported then + * %VMW_BALLOON_CMD_BATCHED_LOCK and VMW_BALLOON_CMD_BATCHED_UNLOCK commands + * are available. * - * VMW_BALLOON_BASIC_CMDS: - * LOCK and UNLOCK commands, - * VMW_BALLOON_BATCHED_CMDS: - * BATCHED_LOCK and BATCHED_UNLOCK commands. - * VMW BALLOON_BATCHED_2M_CMDS: - * BATCHED_2M_LOCK and BATCHED_2M_UNLOCK commands, - * VMW VMW_BALLOON_SIGNALLED_WAKEUP_CMD: - * VMW_BALLOON_CMD_VMCI_DOORBELL_SET command. + * If the host reports %VMW_BALLOON_BATCHED_2M_CMDS are supported then + * %VMW_BALLOON_CMD_BATCHED_2M_LOCK and %VMW_BALLOON_CMD_BATCHED_2M_UNLOCK + * are supported. + * + * If the host reports VMW_BALLOON_SIGNALLED_WAKEUP_CMD is supported then + * VMW_BALLOON_CMD_VMCI_DOORBELL_SET command is supported. + * + * @VMW_BALLOON_CMD_START: Communicating supported version with the hypervisor. + * @VMW_BALLOON_CMD_GET_TARGET: Gets the balloon target size. + * @VMW_BALLOON_CMD_LOCK: Informs the hypervisor about a ballooned page. + * @VMW_BALLOON_CMD_UNLOCK: Informs the hypervisor about a page that is about + * to be deflated from the balloon. + * @VMW_BALLOON_CMD_GUEST_ID: Informs the hypervisor about the type of OS that + * runs in the VM. + * @VMW_BALLOON_CMD_BATCHED_LOCK: Inform the hypervisor about a batch of + * ballooned pages (up to 512). + * @VMW_BALLOON_CMD_BATCHED_UNLOCK: Inform the hypervisor about a batch of + * pages that are about to be deflated from the + * balloon (up to 512). + * @VMW_BALLOON_CMD_BATCHED_2M_LOCK: Similar to @VMW_BALLOON_CMD_BATCHED_LOCK + * for 2MB pages. + * @VMW_BALLOON_CMD_BATCHED_2M_UNLOCK: Similar to + * @VMW_BALLOON_CMD_BATCHED_UNLOCK for 2MB + * pages. + * @VMW_BALLOON_CMD_VMCI_DOORBELL_SET: A command to set doorbell notification + * that would be invoked when the balloon + * size changes. + * @VMW_BALLOON_CMD_LAST: Value of the last command. */ -#define VMW_BALLOON_CMD_START 0 -#define VMW_BALLOON_CMD_GET_TARGET 1 -#define VMW_BALLOON_CMD_LOCK 2 -#define VMW_BALLOON_CMD_UNLOCK 3 -#define VMW_BALLOON_CMD_GUEST_ID 4 -#define VMW_BALLOON_CMD_BATCHED_LOCK 6 -#define VMW_BALLOON_CMD_BATCHED_UNLOCK 7 -#define VMW_BALLOON_CMD_BATCHED_2M_LOCK 8 -#define VMW_BALLOON_CMD_BATCHED_2M_UNLOCK 9 -#define VMW_BALLOON_CMD_VMCI_DOORBELL_SET 10 - -#define VMW_BALLOON_CMD_NUM 11 - -/* error codes */ -#define VMW_BALLOON_SUCCESS 0 -#define VMW_BALLOON_FAILURE -1 -#define VMW_BALLOON_ERROR_CMD_INVALID 1 -#define VMW_BALLOON_ERROR_PPN_INVALID 2 -#define VMW_BALLOON_ERROR_PPN_LOCKED 3 -#define VMW_BALLOON_ERROR_PPN_UNLOCKED 4 -#define VMW_BALLOON_ERROR_PPN_PINNED 5 -#define VMW_BALLOON_ERROR_PPN_NOTNEEDED 6 -#define VMW_BALLOON_ERROR_RESET 7 -#define VMW_BALLOON_ERROR_BUSY 8 +enum vmballoon_cmd_type { + VMW_BALLOON_CMD_START, + VMW_BALLOON_CMD_GET_TARGET, + VMW_BALLOON_CMD_LOCK, + VMW_BALLOON_CMD_UNLOCK, + VMW_BALLOON_CMD_GUEST_ID, + /* No command 5 */ + VMW_BALLOON_CMD_BATCHED_LOCK = 6, + VMW_BALLOON_CMD_BATCHED_UNLOCK, + VMW_BALLOON_CMD_BATCHED_2M_LOCK, + VMW_BALLOON_CMD_BATCHED_2M_UNLOCK, + VMW_BALLOON_CMD_VMCI_DOORBELL_SET, + VMW_BALLOON_CMD_LAST = VMW_BALLOON_CMD_VMCI_DOORBELL_SET, +}; + +#define VMW_BALLOON_CMD_NUM (VMW_BALLOON_CMD_LAST + 1) + +enum vmballoon_error_codes { + VMW_BALLOON_SUCCESS, + VMW_BALLOON_ERROR_CMD_INVALID, + VMW_BALLOON_ERROR_PPN_INVALID, + VMW_BALLOON_ERROR_PPN_LOCKED, + VMW_BALLOON_ERROR_PPN_UNLOCKED, + VMW_BALLOON_ERROR_PPN_PINNED, + VMW_BALLOON_ERROR_PPN_NOTNEEDED, + VMW_BALLOON_ERROR_RESET, + VMW_BALLOON_ERROR_BUSY +}; #define VMW_BALLOON_SUCCESS_WITH_CAPABILITIES (0x03000000) @@ -143,29 +193,28 @@ static const char * const vmballoon_cmd_names[] = { [VMW_BALLOON_CMD_VMCI_DOORBELL_SET] = "doorbellSet" }; -#ifdef CONFIG_DEBUG_FS -struct vmballoon_stats { - unsigned int timer; - unsigned int doorbell; - - /* allocation statistics */ - unsigned int alloc[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int alloc_fail[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int refused_alloc[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int refused_free[VMW_BALLOON_NUM_PAGE_SIZES]; - unsigned int free[VMW_BALLOON_NUM_PAGE_SIZES]; - - /* Monitor operations. */ - unsigned long ops[VMW_BALLOON_CMD_NUM]; - unsigned long ops_fail[VMW_BALLOON_CMD_NUM]; +enum vmballoon_stat_page { + VMW_BALLOON_PAGE_STAT_ALLOC, + VMW_BALLOON_PAGE_STAT_ALLOC_FAIL, + VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC, + VMW_BALLOON_PAGE_STAT_REFUSED_FREE, + VMW_BALLOON_PAGE_STAT_FREE, + VMW_BALLOON_PAGE_STAT_LAST = VMW_BALLOON_PAGE_STAT_FREE }; -#define STATS_INC(stat) (stat)++ -#else -#define STATS_INC(stat) -#endif +#define VMW_BALLOON_PAGE_STAT_NUM (VMW_BALLOON_PAGE_STAT_LAST + 1) + +enum vmballoon_stat_general { + VMW_BALLOON_STAT_TIMER, + VMW_BALLOON_STAT_DOORBELL, + VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_DOORBELL +}; + +#define VMW_BALLOON_STAT_NUM (VMW_BALLOON_STAT_LAST + 1) + static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching); +static DEFINE_STATIC_KEY_FALSE(balloon_stat_enabled); struct vmballoon_page_size { /* list of reserved physical pages */ @@ -215,10 +264,10 @@ struct vmballoon { unsigned int batch_max_pages; struct page *page; -#ifdef CONFIG_DEBUG_FS /* statistics */ - struct vmballoon_stats stats; + struct vmballoon_stats *stats; +#ifdef CONFIG_DEBUG_FS /* debugfs file exporting statistics */ struct dentry *dbg_entry; #endif @@ -226,17 +275,70 @@ struct vmballoon { struct delayed_work dwork; struct vmci_handle vmci_doorbell; + + /** + * @conf_sem: semaphore to protect the configuration and the statistics. + */ + struct rw_semaphore conf_sem; }; static struct vmballoon balloon; +struct vmballoon_stats { + /* timer / doorbell operations */ + atomic64_t general_stat[VMW_BALLOON_STAT_NUM]; + + /* allocation statistics for huge and small pages */ + atomic64_t + page_stat[VMW_BALLOON_PAGE_STAT_NUM][VMW_BALLOON_NUM_PAGE_SIZES]; + + /* Monitor operations: total operations, and failures */ + atomic64_t ops[VMW_BALLOON_CMD_NUM][VMW_BALLOON_OP_STAT_TYPES]; +}; + +static inline bool is_vmballoon_stats_on(void) +{ + return IS_ENABLED(CONFIG_DEBUG_FS) && + static_branch_unlikely(&balloon_stat_enabled); +} + +static inline void vmballoon_stats_op_inc(struct vmballoon *b, unsigned int op, + enum vmballoon_op_stat_type type) +{ + if (is_vmballoon_stats_on()) + atomic64_inc(&b->stats->ops[op][type]); +} + +static inline void vmballoon_stats_gen_inc(struct vmballoon *b, + enum vmballoon_stat_general stat) +{ + if (is_vmballoon_stats_on()) + atomic64_inc(&b->stats->general_stat[stat]); +} + +static inline void vmballoon_stats_gen_add(struct vmballoon *b, + enum vmballoon_stat_general stat, + unsigned int val) +{ + if (is_vmballoon_stats_on()) + atomic64_add(val, &b->stats->general_stat[stat]); +} + +static inline void vmballoon_stats_page_inc(struct vmballoon *b, + enum vmballoon_stat_page stat, + bool is_2m_page) +{ + if (is_vmballoon_stats_on()) + atomic64_inc(&b->stats->page_stat[stat][is_2m_page]); +} + static inline unsigned long __vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, unsigned long arg2, unsigned long *result) { unsigned long status, dummy1, dummy2, dummy3, local_result; - STATS_INC(b->stats.ops[cmd]); + vmballoon_stats_op_inc(b, cmd, VMW_BALLOON_OP_STAT); asm volatile ("inl %%dx" : "=a"(status), @@ -263,7 +365,7 @@ __vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, if (status != VMW_BALLOON_SUCCESS && status != VMW_BALLOON_SUCCESS_WITH_CAPABILITIES) { - STATS_INC(b->stats.ops_fail[cmd]); + vmballoon_stats_op_inc(b, cmd, VMW_BALLOON_OP_FAIL_STAT); pr_debug("%s: %s [0x%lx,0x%lx) failed, returned %ld\n", __func__, vmballoon_cmd_names[cmd], arg1, arg2, status); @@ -413,7 +515,8 @@ static void vmballoon_pop(struct vmballoon *b) list_for_each_entry_safe(page, next, &page_size->pages, lru) { list_del(&page->lru); vmballoon_free_page(page, is_2m_pages); - STATS_INC(b->stats.free[is_2m_pages]); + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_FREE, + is_2m_pages); b->size -= size_per_page; cond_resched(); } @@ -534,7 +637,8 @@ static int vmballoon_lock(struct vmballoon *b, unsigned int num_pages, } /* Error occurred */ - STATS_INC(b->stats.refused_alloc[is_2m_pages]); + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC, + is_2m_pages); /* * Place page on the list of non-balloonable pages @@ -587,7 +691,8 @@ static int vmballoon_unlock(struct vmballoon *b, unsigned int num_pages, } else { /* deallocate page */ vmballoon_free_page(p, is_2m_pages); - STATS_INC(b->stats.free[is_2m_pages]); + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_FREE, + is_2m_pages); /* update balloon size */ b->size -= size_per_page; @@ -611,7 +716,8 @@ static void vmballoon_release_refused_pages(struct vmballoon *b, list_for_each_entry_safe(page, next, &page_size->refused_pages, lru) { list_del(&page->lru); vmballoon_free_page(page, is_2m_pages); - STATS_INC(b->stats.refused_free[is_2m_pages]); + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_REFUSED_FREE, + is_2m_pages); } page_size->n_refused_pages = 0; @@ -693,10 +799,14 @@ static void vmballoon_inflate(struct vmballoon *b) vmballoon_change(b)) { struct page *page; - STATS_INC(b->stats.alloc[is_2m_pages]); + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC, + is_2m_pages); + page = vmballoon_alloc_page(is_2m_pages); if (!page) { - STATS_INC(b->stats.alloc_fail[is_2m_pages]); + vmballoon_stats_page_inc(b, + VMW_BALLOON_PAGE_STAT_ALLOC_FAIL, is_2m_pages); + if (is_2m_pages) { vmballoon_lock(b, num_pages, true); @@ -845,7 +955,7 @@ static void vmballoon_doorbell(void *client_data) { struct vmballoon *b = client_data; - STATS_INC(b->stats.doorbell); + vmballoon_stats_gen_inc(b, VMW_BALLOON_STAT_DOORBELL); mod_delayed_work(system_freezable_wq, &b->dwork, 0); } @@ -903,6 +1013,8 @@ static void vmballoon_reset(struct vmballoon *b) { int error; + down_write(&b->conf_sem); + vmballoon_vmci_cleanup(b); /* free all pages, skipping monitor unlock */ @@ -934,6 +1046,8 @@ static void vmballoon_reset(struct vmballoon *b) if (!vmballoon_send_guest_id(b)) pr_err("failed to send guest ID to the host\n"); + + up_write(&b->conf_sem); } /** @@ -950,11 +1064,18 @@ static void vmballoon_work(struct work_struct *work) struct vmballoon *b = container_of(dwork, struct vmballoon, dwork); int64_t change = 0; - STATS_INC(b->stats.timer); - if (b->reset_required) vmballoon_reset(b); + down_read(&b->conf_sem); + + /* + * Update the stats while holding the semaphore to ensure that + * @stats_enabled is consistent with whether the stats are actually + * enabled + */ + vmballoon_stats_gen_inc(b, VMW_BALLOON_STAT_TIMER); + if (!vmballoon_send_get_target(b)) change = vmballoon_change(b); @@ -968,12 +1089,15 @@ static void vmballoon_work(struct work_struct *work) vmballoon_deflate(b); } + up_read(&b->conf_sem); + /* * We are using a freezable workqueue so that balloon operations are * stopped while the system transitions to/from sleep/hibernation. */ queue_delayed_work(system_freezable_wq, dwork, round_jiffies_relative(HZ)); + } /* @@ -981,55 +1105,105 @@ static void vmballoon_work(struct work_struct *work) */ #ifdef CONFIG_DEBUG_FS +static const char * const vmballoon_stat_page_names[] = { + [VMW_BALLOON_PAGE_STAT_ALLOC] = "alloc", + [VMW_BALLOON_PAGE_STAT_ALLOC_FAIL] = "allocFail", + [VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC] = "errAlloc", + [VMW_BALLOON_PAGE_STAT_REFUSED_FREE] = "errFree", + [VMW_BALLOON_PAGE_STAT_FREE] = "free" +}; + +static const char * const vmballoon_stat_names[] = { + [VMW_BALLOON_STAT_TIMER] = "timer", + [VMW_BALLOON_STAT_DOORBELL] = "doorbell" +}; + +static const char * const vmballoon_page_size_names[] = { + [VMW_BALLOON_4K_PAGE] = "4k", + [VMW_BALLOON_2M_PAGE] = "2M" +}; + +static int vmballoon_enable_stats(struct vmballoon *b) +{ + int r = 0; + + down_write(&b->conf_sem); + + /* did we somehow race with another reader which enabled stats? */ + if (b->stats) + goto out; + + b->stats = kzalloc(sizeof(*b->stats), GFP_KERNEL); + + if (!b->stats) { + /* allocation failed */ + r = -ENOMEM; + goto out; + } + static_key_enable(&balloon_stat_enabled.key); +out: + up_write(&b->conf_sem); + return r; +} + +/** + * vmballoon_debug_show - shows statistics of balloon operations. + * @f: pointer to the &struct seq_file. + * @offset: ignored. + * + * Provides the statistics that can be accessed in vmmemctl in the debugfs. + * To avoid the overhead - mainly that of memory - of collecting the statistics, + * we only collect statistics after the first time the counters are read. + * + * Return: zero on success or an error code. + */ static int vmballoon_debug_show(struct seq_file *f, void *offset) { struct vmballoon *b = f->private; - struct vmballoon_stats *stats = &b->stats; - int i; + int i, j; + + /* enables stats if they are disabled */ + if (!b->stats) { + int r = vmballoon_enable_stats(b); + + if (r) + return r; + } /* format capabilities info */ - seq_printf(f, - "balloon capabilities: %#4x\n" - "used capabilities: %#4lx\n" - "is resetting: %c\n", - VMW_BALLOON_CAPABILITIES, b->capabilities, - b->reset_required ? 'y' : 'n'); + seq_printf(f, "%-22s: %#4x\n", "balloon capabilities", + VMW_BALLOON_CAPABILITIES); + seq_printf(f, "%-22s: %#4lx\n", "used capabilities", + b->capabilities); + seq_printf(f, "%-22s: %16s\n", "is resetting", + b->reset_required ? "y" : "n"); /* format size info */ - seq_printf(f, - "target: %8d pages\n" - "current: %8d pages\n", - b->target, b->size); + seq_printf(f, "%-22s: %16u\n", "target", b->target); + seq_printf(f, "%-22s: %16u\n", "current", b->size); for (i = 0; i < VMW_BALLOON_CMD_NUM; i++) { if (vmballoon_cmd_names[i] == NULL) continue; - seq_printf(f, "%-22s: %16lu (%lu failed)\n", - vmballoon_cmd_names[i], stats->ops[i], - stats->ops_fail[i]); + seq_printf(f, "%-22s: %16llu (%llu failed)\n", + vmballoon_cmd_names[i], + atomic64_read(&b->stats->ops[i][VMW_BALLOON_OP_STAT]), + atomic64_read(&b->stats->ops[i][VMW_BALLOON_OP_FAIL_STAT])); } - seq_printf(f, - "\n" - "timer: %8u\n" - "doorbell: %8u\n" - "prim2mAlloc: %8u (%4u failed)\n" - "prim4kAlloc: %8u (%4u failed)\n" - "prim2mFree: %8u\n" - "primFree: %8u\n" - "err2mAlloc: %8u\n" - "errAlloc: %8u\n" - "err2mFree: %8u\n" - "errFree: %8u\n", - stats->timer, - stats->doorbell, - stats->alloc[true], stats->alloc_fail[true], - stats->alloc[false], stats->alloc_fail[false], - stats->free[true], - stats->free[false], - stats->refused_alloc[true], stats->refused_alloc[false], - stats->refused_free[true], stats->refused_free[false]); + for (i = 0; i < VMW_BALLOON_STAT_NUM; i++) + seq_printf(f, "%-22s: %16llu\n", + vmballoon_stat_names[i], + atomic64_read(&b->stats->general_stat[i])); + + for (i = 0; i < VMW_BALLOON_PAGE_STAT_NUM; i++) { + for (j = 0; j < VMW_BALLOON_NUM_PAGE_SIZES; j++) + seq_printf(f, "%-18s(%s): %16llu\n", + vmballoon_stat_page_names[i], + vmballoon_page_size_names[j], + atomic64_read(&b->stats->page_stat[i][j])); + } return 0; } @@ -1064,7 +1238,10 @@ static int __init vmballoon_debugfs_init(struct vmballoon *b) static void __exit vmballoon_debugfs_exit(struct vmballoon *b) { + static_key_disable(&balloon_stat_enabled.key); debugfs_remove(b->dbg_entry); + kfree(b->stats); + b->stats = NULL; } #else @@ -1103,6 +1280,7 @@ static int __init vmballoon_init(void) if (error) return error; + init_rwsem(&balloon.conf_sem); balloon.vmci_doorbell = VMCI_INVALID_HANDLE; balloon.batch_page = NULL; balloon.page = NULL; -- cgit v1.2.3-58-ga151 From 6e4453b3211d144a152d88f634a691adf651cfc2 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:18 -0700 Subject: vmw_balloon: rework the inflate and deflate loops In preparation for supporting compaction and OOM notification, this patch reworks the inflate/deflate loops. The main idea is to separate the allocation, communication with the hypervisor, and the handling of errors from each other. Doing will allow us to perform concurrent inflation and deflation, excluding the actual communication with the hypervisor. To do so, we need to get rid of the remaining global state that is kept in the balloon struct, specifically the refuse_list. When the VM communicates with the hypervisor, it does not free or put back pages to the balloon list and instead only moves the pages whose status indicated failure into a refuse_list on the stack. Once the operation completes, the inflation or deflation functions handle the list appropriately. As we do that, we can consolidate the communication with the hypervisor for both the lock and unlock operations into a single function. We also reuse the deflation function for popping the balloon. As a preparation for preventing races, we hold a spinlock when the communication actually takes place, and use atomic operations for updating the balloon size. The balloon page list is still racy and will be handled in the next patch. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 829 +++++++++++++++++++++++++++++---------------- 1 file changed, 541 insertions(+), 288 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 0a2bdaf5773b..cb363db34b4a 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -89,6 +90,16 @@ enum vmballoon_page_size_type { #define VMW_BALLOON_NUM_PAGE_SIZES (VMW_BALLOON_LAST_SIZE + 1) +static const char * const vmballoon_page_size_names[] = { + [VMW_BALLOON_4K_PAGE] = "4k", + [VMW_BALLOON_2M_PAGE] = "2M" +}; + +enum vmballoon_op { + VMW_BALLOON_INFLATE, + VMW_BALLOON_DEFLATE +}; + enum vmballoon_op_stat_type { VMW_BALLOON_OP_STAT, VMW_BALLOON_OP_FAIL_STAT @@ -216,13 +227,18 @@ enum vmballoon_stat_general { static DEFINE_STATIC_KEY_TRUE(vmw_balloon_batching); static DEFINE_STATIC_KEY_FALSE(balloon_stat_enabled); -struct vmballoon_page_size { - /* list of reserved physical pages */ +struct vmballoon_ctl { struct list_head pages; - - /* transient list of non-balloonable pages */ struct list_head refused_pages; unsigned int n_refused_pages; + unsigned int n_pages; + enum vmballoon_page_size_type page_size; + enum vmballoon_op op; +}; + +struct vmballoon_page_size { + /* list of reserved physical pages */ + struct list_head pages; }; /** @@ -241,16 +257,47 @@ struct vmballoon_batch_entry { struct vmballoon { struct vmballoon_page_size page_sizes[VMW_BALLOON_NUM_PAGE_SIZES]; - /* supported page sizes. 1 == 4k pages only, 2 == 4k and 2m pages */ - unsigned supported_page_sizes; + /** + * @max_page_size: maximum supported page size for ballooning. + * + * Protected by @conf_sem + */ + enum vmballoon_page_size_type max_page_size; + + /** + * @size: balloon actual size in basic page size (frames). + * + * While we currently do not support size which is bigger than 32-bit, + * in preparation for future support, use 64-bits. + */ + atomic64_t size; - /* balloon size in pages */ - unsigned int size; - unsigned int target; + /** + * @target: balloon target size in basic page size (frames). + * + * We do not protect the target under the assumption that setting the + * value is always done through a single write. If this assumption ever + * breaks, we would have to use X_ONCE for accesses, and suffer the less + * optimized code. Although we may read stale target value if multiple + * accesses happen at once, the performance impact should be minor. + */ + unsigned long target; - /* reset flag */ + /** + * @reset_required: reset flag + * + * Setting this flag may introduce races, but the code is expected to + * handle them gracefully. In the worst case, another operation will + * fail as reset did not take place. Clearing the flag is done while + * holding @conf_sem for write. + */ bool reset_required; + /** + * @capabilities: hypervisor balloon capabilities. + * + * Protected by @conf_sem. + */ unsigned long capabilities; /** @@ -261,7 +308,25 @@ struct vmballoon { */ struct vmballoon_batch_entry *batch_page; + /** + * @batch_max_pages: maximum pages that can be locked/unlocked. + * + * Indicates the number of pages that the hypervisor can lock or unlock + * at once, according to whether batching is enabled. If batching is + * disabled, only a single page can be locked/unlock on each operation. + * + * Protected by @conf_sem. + */ unsigned int batch_max_pages; + + /** + * @page: page to be locked/unlocked by the hypervisor + * + * @page is only used when batching is disabled and a single page is + * reclaimed on each iteration. + * + * Protected by @comm_lock. + */ struct page *page; /* statistics */ @@ -274,12 +339,24 @@ struct vmballoon { struct delayed_work dwork; + /** + * @vmci_doorbell. + * + * Protected by @conf_sem. + */ struct vmci_handle vmci_doorbell; /** * @conf_sem: semaphore to protect the configuration and the statistics. */ struct rw_semaphore conf_sem; + + /** + * @comm_lock: lock to protect the communication with the host. + * + * Lock ordering: @conf_sem -> @comm_lock . + */ + spinlock_t comm_lock; }; static struct vmballoon balloon; @@ -326,10 +403,19 @@ static inline void vmballoon_stats_gen_add(struct vmballoon *b, static inline void vmballoon_stats_page_inc(struct vmballoon *b, enum vmballoon_stat_page stat, - bool is_2m_page) + enum vmballoon_page_size_type size) { if (is_vmballoon_stats_on()) - atomic64_inc(&b->stats->page_stat[stat][is_2m_page]); + atomic64_inc(&b->stats->page_stat[stat][size]); +} + +static inline void vmballoon_stats_page_add(struct vmballoon *b, + enum vmballoon_stat_page stat, + enum vmballoon_page_size_type size, + unsigned int val) +{ + if (is_vmballoon_stats_on()) + atomic64_add(val, &b->stats->page_stat[stat][size]); } static inline unsigned long @@ -361,7 +447,7 @@ __vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, /* update target when applicable */ if (status == VMW_BALLOON_SUCCESS && ((1ul << cmd) & VMW_BALLOON_CMD_WITH_TARGET_MASK)) - b->target = local_result; + WRITE_ONCE(b->target, local_result); if (status != VMW_BALLOON_SUCCESS && status != VMW_BALLOON_SUCCESS_WITH_CAPABILITIES) { @@ -417,11 +503,11 @@ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) * reason disabled, do not use 2MB pages, since otherwise the legacy * mechanism is used with 2MB pages, causing a failure. */ + b->max_page_size = VMW_BALLOON_4K_PAGE; if ((b->capabilities & VMW_BALLOON_BATCHED_2M_CMDS) && (b->capabilities & VMW_BALLOON_BATCHED_CMDS)) - b->supported_page_sizes = 2; - else - b->supported_page_sizes = 1; + b->max_page_size = VMW_BALLOON_2M_PAGE; + return success; } @@ -445,12 +531,28 @@ static bool vmballoon_send_guest_id(struct vmballoon *b) return false; } -static u16 vmballoon_page_size(bool is_2m_page) +/** + * vmballoon_page_order() - return the order of the page + * @page_size: the size of the page. + * + * Return: the allocation order. + */ +static inline +unsigned int vmballoon_page_order(enum vmballoon_page_size_type page_size) { - if (is_2m_page) - return 1 << VMW_BALLOON_2M_ORDER; + return page_size == VMW_BALLOON_2M_PAGE ? VMW_BALLOON_2M_ORDER : 0; +} - return 1; +/** + * vmballoon_page_in_frames() - returns the number of frames in a page. + * @page_size: the size of the page. + * + * Return: the number of 4k frames. + */ +static inline unsigned int +vmballoon_page_in_frames(enum vmballoon_page_size_type page_size) +{ + return 1 << vmballoon_page_order(page_size); } /** @@ -478,53 +580,78 @@ static int vmballoon_send_get_target(struct vmballoon *b) return status == VMW_BALLOON_SUCCESS ? 0 : -EIO; } -static struct page *vmballoon_alloc_page(bool is_2m_page) +/** + * vmballoon_alloc_page_list - allocates a list of pages. + * + * @b: pointer to the balloon. + * @ctl: pointer for the %struct vmballoon_ctl, which defines the operation. + * @req_n_pages: the number of requested pages. + * + * Tries to allocate @req_n_pages. Add them to the list of balloon pages in + * @ctl.pages and updates @ctl.n_pages to reflect the number of pages. + * + * Return: zero on success or error code otherwise. + */ +static int vmballoon_alloc_page_list(struct vmballoon *b, + struct vmballoon_ctl *ctl, + unsigned int req_n_pages) { - if (is_2m_page) - return alloc_pages(VMW_HUGE_PAGE_ALLOC_FLAGS, - VMW_BALLOON_2M_ORDER); + struct page *page; + unsigned int i; - return alloc_page(VMW_PAGE_ALLOC_FLAGS); -} + for (i = 0; i < req_n_pages; i++) { + if (ctl->page_size == VMW_BALLOON_2M_PAGE) + page = alloc_pages(VMW_HUGE_PAGE_ALLOC_FLAGS, + VMW_BALLOON_2M_ORDER); + else + page = alloc_page(VMW_PAGE_ALLOC_FLAGS); -static void vmballoon_free_page(struct page *page, bool is_2m_page) -{ - if (is_2m_page) - __free_pages(page, VMW_BALLOON_2M_ORDER); - else - __free_page(page); + /* Update statistics */ + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC, + ctl->page_size); + + if (page) { + /* Success. Add the page to the list and continue. */ + list_add(&page->lru, &ctl->pages); + continue; + } + + /* Allocation failed. Update statistics and stop. */ + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC_FAIL, + ctl->page_size); + break; + } + + ctl->n_pages = i; + + return req_n_pages == ctl->n_pages ? 0 : -ENOMEM; } -/* - * Quickly release all pages allocated for the balloon. This function is - * called when host decides to "reset" balloon for one reason or another. - * Unlike normal "deflate" we do not (shall not) notify host of the pages - * being released. +/** + * vmballoon_handle_one_result - Handle lock/unlock result for a single page. + * + * @b: pointer for %struct vmballoon. + * @page: pointer for the page whose result should be handled. + * @page_size: size of the page. + * @status: status of the operation as provided by the hypervisor. */ -static void vmballoon_pop(struct vmballoon *b) +static int vmballoon_handle_one_result(struct vmballoon *b, struct page *page, + enum vmballoon_page_size_type page_size, + unsigned long status) { - struct page *page, *next; - unsigned is_2m_pages; - - for (is_2m_pages = 0; is_2m_pages < VMW_BALLOON_NUM_PAGE_SIZES; - is_2m_pages++) { - struct vmballoon_page_size *page_size = - &b->page_sizes[is_2m_pages]; - u16 size_per_page = vmballoon_page_size(is_2m_pages); - - list_for_each_entry_safe(page, next, &page_size->pages, lru) { - list_del(&page->lru); - vmballoon_free_page(page, is_2m_pages); - vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_FREE, - is_2m_pages); - b->size -= size_per_page; - cond_resched(); - } - } + /* On success do nothing. The page is already on the balloon list. */ + if (likely(status == VMW_BALLOON_SUCCESS)) + return 0; - /* Clearing the batch_page unconditionally has no adverse effect */ - free_page((unsigned long)b->batch_page); - b->batch_page = NULL; + pr_debug("%s: failed comm pfn %lx status %lu page_size %s\n", __func__, + page_to_pfn(page), status, + vmballoon_page_size_names[page_size]); + + /* Error occurred */ + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC, + page_size); + + return -EIO; } /** @@ -565,8 +692,8 @@ static unsigned long vmballoon_status_page(struct vmballoon *b, int idx, * vmballoon_lock_op - notifies the host about inflated/deflated pages. * @b: pointer to the balloon. * @num_pages: number of inflated/deflated pages. - * @is_2m_pages: whether the page(s) are 2M (or 4k). - * @lock: whether the operation is lock (or unlock). + * @page_size: size of the page. + * @op: the type of operation (lock or unlock). * * Notify the host about page(s) that were ballooned (or removed from the * balloon) so that host can use it without fear that guest will need it (or @@ -578,21 +705,27 @@ static unsigned long vmballoon_status_page(struct vmballoon *b, int idx, */ static unsigned long vmballoon_lock_op(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages, bool lock) + enum vmballoon_page_size_type page_size, + enum vmballoon_op op) { unsigned long cmd, pfn; + lockdep_assert_held(&b->comm_lock); + if (static_branch_likely(&vmw_balloon_batching)) { - if (lock) - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_LOCK : - VMW_BALLOON_CMD_BATCHED_LOCK; + if (op == VMW_BALLOON_INFLATE) + cmd = page_size == VMW_BALLOON_2M_PAGE ? + VMW_BALLOON_CMD_BATCHED_2M_LOCK : + VMW_BALLOON_CMD_BATCHED_LOCK; else - cmd = is_2m_pages ? VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : - VMW_BALLOON_CMD_BATCHED_UNLOCK; + cmd = page_size == VMW_BALLOON_2M_PAGE ? + VMW_BALLOON_CMD_BATCHED_2M_UNLOCK : + VMW_BALLOON_CMD_BATCHED_UNLOCK; pfn = PHYS_PFN(virt_to_phys(b->batch_page)); } else { - cmd = lock ? VMW_BALLOON_CMD_LOCK : VMW_BALLOON_CMD_UNLOCK; + cmd = op == VMW_BALLOON_INFLATE ? VMW_BALLOON_CMD_LOCK : + VMW_BALLOON_CMD_UNLOCK; pfn = page_to_pfn(b->page); /* In non-batching mode, PFNs must fit in 32-bit */ @@ -603,22 +736,75 @@ static unsigned long vmballoon_lock_op(struct vmballoon *b, return vmballoon_cmd(b, cmd, pfn, num_pages); } -static int vmballoon_lock(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages) +/** + * vmballoon_add_page - adds a page towards lock/unlock operation. + * + * @b: pointer to the balloon. + * @idx: index of the page to be ballooned in this batch. + * @p: pointer to the page that is about to be ballooned. + * + * Adds the page to be ballooned. Must be called while holding @comm_lock. + */ +static void vmballoon_add_page(struct vmballoon *b, unsigned int idx, + struct page *p) +{ + lockdep_assert_held(&b->comm_lock); + + if (static_branch_likely(&vmw_balloon_batching)) + b->batch_page[idx] = (struct vmballoon_batch_entry) + { .pfn = page_to_pfn(p) }; + else + b->page = p; +} + +/** + * vmballoon_lock - lock or unlock a batch of pages. + * + * @b: pointer to the balloon. + * @ctl: pointer for the %struct vmballoon_ctl, which defines the operation. + * + * Notifies the host of about ballooned pages (after inflation or deflation, + * according to @ctl). If the host rejects the page put it on the + * @ctl refuse list. These refused page are then released when moving to the + * next size of pages. + * + * Note that we neither free any @page here nor put them back on the ballooned + * pages list. Instead we queue it for later processing. We do that for several + * reasons. First, we do not want to free the page under the lock. Second, it + * allows us to unify the handling of lock and unlock. In the inflate case, the + * caller will check if there are too many refused pages and release them. + * Although it is not identical to the past behavior, it should not affect + * performance. + */ +static int vmballoon_lock(struct vmballoon *b, struct vmballoon_ctl *ctl) { unsigned long batch_status; - int i; - u16 size_per_page = vmballoon_page_size(is_2m_pages); + struct page *page; + unsigned int i, num_pages; + + num_pages = ctl->n_pages; + if (num_pages == 0) + return 0; - batch_status = vmballoon_lock_op(b, num_pages, is_2m_pages, true); + /* communication with the host is done under the communication lock */ + spin_lock(&b->comm_lock); + i = 0; + list_for_each_entry(page, &ctl->pages, lru) + vmballoon_add_page(b, i++, page); + + batch_status = vmballoon_lock_op(b, ctl->n_pages, ctl->page_size, + ctl->op); + + /* + * Iterate over the pages in the provided list. Since we are changing + * @ctl->n_pages we are saving the original value in @num_pages and + * use this value to bound the loop. + */ for (i = 0; i < num_pages; i++) { unsigned long status; - struct page *p; - struct vmballoon_page_size *page_size = - &b->page_sizes[is_2m_pages]; - status = vmballoon_status_page(b, i, &p); + status = vmballoon_status_page(b, i, &page); /* * Failure of the whole batch overrides a single operation @@ -627,109 +813,61 @@ static int vmballoon_lock(struct vmballoon *b, unsigned int num_pages, if (batch_status != VMW_BALLOON_SUCCESS) status = batch_status; - if (status == VMW_BALLOON_SUCCESS) { - /* track allocated page */ - list_add(&p->lru, &page_size->pages); - - /* update balloon size */ - b->size += size_per_page; + /* Continue if no error happened */ + if (!vmballoon_handle_one_result(b, page, ctl->page_size, + status)) continue; - } - - /* Error occurred */ - vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_REFUSED_ALLOC, - is_2m_pages); /* - * Place page on the list of non-balloonable pages - * and retry allocation, unless we already accumulated - * too many of them, in which case take a breather. + * Error happened. Move the pages to the refused list and update + * the pages number. */ - list_add(&p->lru, &page_size->refused_pages); - page_size->n_refused_pages++; + list_move(&page->lru, &ctl->refused_pages); + ctl->n_pages--; + ctl->n_refused_pages++; } + spin_unlock(&b->comm_lock); + return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO; } -/* - * Release the page allocated for the balloon. Note that we first notify - * the host so it can make sure the page will be available for the guest - * to use, if needed. +/** + * vmballoon_release_page_list() - Releases a page list + * + * @page_list: list of pages to release. + * @n_pages: pointer to the number of pages. + * @page_size: whether the pages in the list are 2MB (or else 4KB). + * + * Releases the list of pages and zeros the number of pages. */ -static int vmballoon_unlock(struct vmballoon *b, unsigned int num_pages, - bool is_2m_pages) +static void vmballoon_release_page_list(struct list_head *page_list, + int *n_pages, + enum vmballoon_page_size_type page_size) { - int i; - unsigned long batch_status; - u16 size_per_page = vmballoon_page_size(is_2m_pages); - - batch_status = vmballoon_lock_op(b, num_pages, is_2m_pages, false); - - for (i = 0; i < num_pages; i++) { - struct vmballoon_page_size *page_size; - unsigned long status; - struct page *p; + struct page *page, *tmp; - status = vmballoon_status_page(b, i, &p); - page_size = &b->page_sizes[is_2m_pages]; - - /* - * Failure of the whole batch overrides a single operation - * results. - */ - if (batch_status != VMW_BALLOON_SUCCESS) - status = batch_status; - - if (status != VMW_BALLOON_SUCCESS) { - /* - * That page wasn't successfully unlocked by the - * hypervisor, re-add it to the list of pages owned by - * the balloon driver. - */ - list_add(&p->lru, &page_size->pages); - } else { - /* deallocate page */ - vmballoon_free_page(p, is_2m_pages); - vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_FREE, - is_2m_pages); - - /* update balloon size */ - b->size -= size_per_page; - } + list_for_each_entry_safe(page, tmp, page_list, lru) { + list_del(&page->lru); + __free_pages(page, vmballoon_page_order(page_size)); } - return batch_status == VMW_BALLOON_SUCCESS ? 0 : -EIO; + *n_pages = 0; } + /* * Release pages that were allocated while attempting to inflate the * balloon but were refused by the host for one reason or another. */ static void vmballoon_release_refused_pages(struct vmballoon *b, - bool is_2m_pages) + struct vmballoon_ctl *ctl) { - struct page *page, *next; - struct vmballoon_page_size *page_size = - &b->page_sizes[is_2m_pages]; + vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_REFUSED_FREE, + ctl->page_size); - list_for_each_entry_safe(page, next, &page_size->refused_pages, lru) { - list_del(&page->lru); - vmballoon_free_page(page, is_2m_pages); - vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_REFUSED_FREE, - is_2m_pages); - } - - page_size->n_refused_pages = 0; -} - -static void vmballoon_add_page(struct vmballoon *b, int idx, struct page *p) -{ - if (static_branch_likely(&vmw_balloon_batching)) - b->batch_page[idx] = (struct vmballoon_batch_entry) - { .pfn = page_to_pfn(p) }; - else - b->page = p; + vmballoon_release_page_list(&ctl->refused_pages, &ctl->n_refused_pages, + ctl->page_size); } /** @@ -744,8 +882,8 @@ static int64_t vmballoon_change(struct vmballoon *b) { int64_t size, target; - size = b->size; - target = b->target; + size = atomic64_read(&b->size); + target = READ_ONCE(b->target); /* * We must cast first because of int sizes @@ -756,154 +894,257 @@ static int64_t vmballoon_change(struct vmballoon *b) return 0; /* consider a 2MB slack on deflate, unless the balloon is emptied */ - if (target < size && size - target < vmballoon_page_size(true) && - target != 0) + if (target < size && target != 0 && + size - target < vmballoon_page_in_frames(VMW_BALLOON_2M_PAGE)) return 0; return target - size; } -/* - * Inflate the balloon towards its target size. Note that we try to limit - * the rate of allocation to make sure we are not choking the rest of the - * system. +/** + * vmballoon_enqueue_page_list() - Enqueues list of pages after inflation. + * + * @b: pointer to balloon. + * @pages: list of pages to enqueue. + * @n_pages: pointer to number of pages in list. The value is zeroed. + * @page_size: whether the pages are 2MB or 4KB pages. + * + * Enqueues the provides list of pages in the ballooned page list, clears the + * list and zeroes the number of pages that was provided. + */ +static void vmballoon_enqueue_page_list(struct vmballoon *b, + struct list_head *pages, + unsigned int *n_pages, + enum vmballoon_page_size_type page_size) +{ + struct vmballoon_page_size *page_size_info = &b->page_sizes[page_size]; + + list_splice_init(pages, &page_size_info->pages); + *n_pages = 0; +} + +/** + * vmballoon_dequeue_page_list() - Dequeues page lists for deflation. + * + * @b: pointer to balloon. + * @pages: list of pages to enqueue. + * @n_pages: pointer to number of pages in list. The value is zeroed. + * @page_size: whether the pages are 2MB or 4KB pages. + * @n_req_pages: the number of requested pages. + * + * Dequeues the number of requested pages from the balloon for deflation. The + * number of dequeued pages may be lower, if not enough pages in the requested + * size are available. + */ +static void vmballoon_dequeue_page_list(struct vmballoon *b, + struct list_head *pages, + unsigned int *n_pages, + enum vmballoon_page_size_type page_size, + unsigned int n_req_pages) +{ + struct vmballoon_page_size *page_size_info = &b->page_sizes[page_size]; + struct page *page, *tmp; + unsigned int i = 0; + + list_for_each_entry_safe(page, tmp, &page_size_info->pages, lru) { + list_move(&page->lru, pages); + if (++i == n_req_pages) + break; + } + *n_pages = i; +} + +/** + * vmballoon_inflate() - Inflate the balloon towards its target size. + * + * @b: pointer to the balloon. */ static void vmballoon_inflate(struct vmballoon *b) { - unsigned int num_pages = 0; - int error = 0; - bool is_2m_pages; + int64_t to_inflate_frames; + struct vmballoon_ctl ctl = { + .pages = LIST_HEAD_INIT(ctl.pages), + .refused_pages = LIST_HEAD_INIT(ctl.refused_pages), + .page_size = b->max_page_size, + .op = VMW_BALLOON_INFLATE + }; - /* - * First try NOSLEEP page allocations to inflate balloon. - * - * If we do not throttle nosleep allocations, we can drain all - * free pages in the guest quickly (if the balloon target is high). - * As a side-effect, draining free pages helps to inform (force) - * the guest to start swapping if balloon target is not met yet, - * which is a desired behavior. However, balloon driver can consume - * all available CPU cycles if too many pages are allocated in a - * second. Therefore, we throttle nosleep allocations even when - * the guest is not under memory pressure. OTOH, if we have already - * predicted that the guest is under memory pressure, then we - * slowdown page allocations considerably. - */ + while ((to_inflate_frames = vmballoon_change(b)) > 0) { + unsigned int to_inflate_pages, page_in_frames; + int alloc_error, lock_error = 0; - /* - * Start with no sleep allocation rate which may be higher - * than sleeping allocation rate. - */ - is_2m_pages = b->supported_page_sizes == VMW_BALLOON_NUM_PAGE_SIZES; + VM_BUG_ON(!list_empty(&ctl.pages)); + VM_BUG_ON(ctl.n_pages != 0); - while ((int64_t)(num_pages * vmballoon_page_size(is_2m_pages)) < - vmballoon_change(b)) { - struct page *page; + page_in_frames = vmballoon_page_in_frames(ctl.page_size); - vmballoon_stats_page_inc(b, VMW_BALLOON_PAGE_STAT_ALLOC, - is_2m_pages); - - page = vmballoon_alloc_page(is_2m_pages); - if (!page) { - vmballoon_stats_page_inc(b, - VMW_BALLOON_PAGE_STAT_ALLOC_FAIL, is_2m_pages); - - if (is_2m_pages) { - vmballoon_lock(b, num_pages, true); - - /* - * ignore errors from locking as we now switch - * to 4k pages and we might get different - * errors. - */ - - num_pages = 0; - is_2m_pages = false; - continue; - } + to_inflate_pages = min_t(unsigned long, b->batch_max_pages, + DIV_ROUND_UP_ULL(to_inflate_frames, + page_in_frames)); + + /* Start by allocating */ + alloc_error = vmballoon_alloc_page_list(b, &ctl, + to_inflate_pages); + + /* Actually lock the pages by telling the hypervisor */ + lock_error = vmballoon_lock(b, &ctl); + + /* + * If an error indicates that something serious went wrong, + * stop the inflation. + */ + if (lock_error) break; - } - vmballoon_add_page(b, num_pages++, page); - if (num_pages == b->batch_max_pages) { - struct vmballoon_page_size *page_size = - &b->page_sizes[is_2m_pages]; + /* Update the balloon size */ + atomic64_add(ctl.n_pages * page_in_frames, &b->size); - error = vmballoon_lock(b, num_pages, is_2m_pages); + vmballoon_enqueue_page_list(b, &ctl.pages, &ctl.n_pages, + ctl.page_size); - num_pages = 0; + /* + * If allocation failed or the number of refused pages exceeds + * the maximum allowed, move to the next page size. + */ + if (alloc_error || + ctl.n_refused_pages >= VMW_BALLOON_MAX_REFUSED) { + if (ctl.page_size == VMW_BALLOON_4K_PAGE) + break; /* - * Stop allocating this page size if we already - * accumulated too many pages that the hypervisor - * refused. + * Ignore errors from locking as we now switch to 4k + * pages and we might get different errors. */ - if (page_size->n_refused_pages >= - VMW_BALLOON_MAX_REFUSED) { - if (!is_2m_pages) - break; - - /* - * Release the refused pages as we move to 4k - * pages. - */ - vmballoon_release_refused_pages(b, true); - is_2m_pages = true; - } - - if (error) - break; + vmballoon_release_refused_pages(b, &ctl); + ctl.page_size--; } cond_resched(); } - if (num_pages > 0) - vmballoon_lock(b, num_pages, is_2m_pages); - - vmballoon_release_refused_pages(b, true); - vmballoon_release_refused_pages(b, false); + /* + * Release pages that were allocated while attempting to inflate the + * balloon but were refused by the host for one reason or another, + * and update the statistics. + */ + if (ctl.n_refused_pages != 0) + vmballoon_release_refused_pages(b, &ctl); } -/* +/** + * vmballoon_deflate() - Decrease the size of the balloon. + * + * @b: pointer to the balloon + * @n_frames: the number of frames to deflate. If zero, automatically + * calculated according to the target size. + * @coordinated: whether to coordinate with the host + * * Decrease the size of the balloon allowing guest to use more memory. + * + * Return: The number of deflated frames (i.e., basic page size units) */ -static void vmballoon_deflate(struct vmballoon *b) +static unsigned long vmballoon_deflate(struct vmballoon *b, uint64_t n_frames, + bool coordinated) { - unsigned is_2m_pages; + unsigned long deflated_frames = 0; + unsigned long tried_frames = 0; + struct vmballoon_ctl ctl = { + .pages = LIST_HEAD_INIT(ctl.pages), + .refused_pages = LIST_HEAD_INIT(ctl.refused_pages), + .page_size = VMW_BALLOON_4K_PAGE, + .op = VMW_BALLOON_DEFLATE + }; /* free pages to reach target */ - for (is_2m_pages = 0; is_2m_pages < b->supported_page_sizes; - is_2m_pages++) { - struct page *page, *next; - unsigned int num_pages = 0; - struct vmballoon_page_size *page_size = - &b->page_sizes[is_2m_pages]; - - list_for_each_entry_safe(page, next, &page_size->pages, lru) { - if ((int64_t)(num_pages * - vmballoon_page_size(is_2m_pages)) >= - -vmballoon_change(b)) - break; + while (true) { + unsigned int to_deflate_pages, n_unlocked_frames; + unsigned int page_in_frames; + int64_t to_deflate_frames; + bool deflated_all; + + page_in_frames = vmballoon_page_in_frames(ctl.page_size); + + VM_BUG_ON(!list_empty(&ctl.pages)); + VM_BUG_ON(ctl.n_pages); + VM_BUG_ON(!list_empty(&ctl.refused_pages)); + VM_BUG_ON(ctl.n_refused_pages); + + /* + * If we were requested a specific number of frames, we try to + * deflate this number of frames. Otherwise, deflation is + * performed according to the target and balloon size. + */ + to_deflate_frames = n_frames ? n_frames - tried_frames : + -vmballoon_change(b); + + /* break if no work to do */ + if (to_deflate_frames <= 0) + break; + + /* + * Calculate the number of frames based on current page size, + * but limit the deflated frames to a single chunk + */ + to_deflate_pages = min_t(unsigned long, b->batch_max_pages, + DIV_ROUND_UP_ULL(to_deflate_frames, + page_in_frames)); + + /* First take the pages from the balloon pages. */ + vmballoon_dequeue_page_list(b, &ctl.pages, &ctl.n_pages, + ctl.page_size, to_deflate_pages); - list_del(&page->lru); - vmballoon_add_page(b, num_pages++, page); + /* + * Before pages are moving to the refused list, count their + * frames as frames that we tried to deflate. + */ + tried_frames += ctl.n_pages * page_in_frames; + + /* + * Unlock the pages by communicating with the hypervisor if the + * communication is coordinated (i.e., not pop). We ignore the + * return code. Instead we check if all the pages we manage to + * unlock all the pages. If we failed, we will move to the next + * page size, and would eventually try again later. + */ + if (coordinated) + vmballoon_lock(b, &ctl); + + /* + * Check if we deflated enough. We will move to the next page + * size if we did not manage to do so. This calculation takes + * place now, as once the pages are released, the number of + * pages is zeroed. + */ + deflated_all = (ctl.n_pages == to_deflate_pages); - if (num_pages == b->batch_max_pages) { - int error; + /* Update local and global counters */ + n_unlocked_frames = ctl.n_pages * page_in_frames; + atomic64_sub(n_unlocked_frames, &b->size); + deflated_frames += n_unlocked_frames; - error = vmballoon_unlock(b, num_pages, - is_2m_pages); - num_pages = 0; - if (error) - return; - } + vmballoon_stats_page_add(b, VMW_BALLOON_PAGE_STAT_FREE, + ctl.page_size, ctl.n_pages); - cond_resched(); + /* free the ballooned pages */ + vmballoon_release_page_list(&ctl.pages, &ctl.n_pages, + ctl.page_size); + + /* Return the refused pages to the ballooned list. */ + vmballoon_enqueue_page_list(b, &ctl.refused_pages, + &ctl.n_refused_pages, + ctl.page_size); + + /* If we failed to unlock all the pages, move to next size. */ + if (!deflated_all) { + if (ctl.page_size == b->max_page_size) + break; + ctl.page_size++; } - if (num_pages > 0) - vmballoon_unlock(b, num_pages, is_2m_pages); + cond_resched(); } + + return deflated_frames; } /** @@ -1004,6 +1245,23 @@ fail: return -EIO; } +/** + * vmballoon_pop - Quickly release all pages allocate for the balloon. + * + * @b: pointer to the balloon. + * + * This function is called when host decides to "reset" balloon for one reason + * or another. Unlike normal "deflate" we do not (shall not) notify host of the + * pages being released. + */ +static void vmballoon_pop(struct vmballoon *b) +{ + unsigned long size; + + while ((size = atomic64_read(&b->size))) + vmballoon_deflate(b, size, false); +} + /* * Perform standard reset sequence by popping the balloon (in case it * is not empty) and then restarting protocol. This operation normally @@ -1080,13 +1338,13 @@ static void vmballoon_work(struct work_struct *work) change = vmballoon_change(b); if (change != 0) { - pr_debug("%s - size: %u, target %u", __func__, - b->size, b->target); + pr_debug("%s - size: %llu, target %lu\n", __func__, + atomic64_read(&b->size), READ_ONCE(b->target)); if (change > 0) vmballoon_inflate(b); else /* (change < 0) */ - vmballoon_deflate(b); + vmballoon_deflate(b, 0, true); } up_read(&b->conf_sem); @@ -1118,11 +1376,6 @@ static const char * const vmballoon_stat_names[] = { [VMW_BALLOON_STAT_DOORBELL] = "doorbell" }; -static const char * const vmballoon_page_size_names[] = { - [VMW_BALLOON_4K_PAGE] = "4k", - [VMW_BALLOON_2M_PAGE] = "2M" -}; - static int vmballoon_enable_stats(struct vmballoon *b) { int r = 0; @@ -1171,16 +1424,15 @@ static int vmballoon_debug_show(struct seq_file *f, void *offset) } /* format capabilities info */ - seq_printf(f, "%-22s: %#4x\n", "balloon capabilities", + seq_printf(f, "%-22s: %#16x\n", "balloon capabilities", VMW_BALLOON_CAPABILITIES); - seq_printf(f, "%-22s: %#4lx\n", "used capabilities", - b->capabilities); + seq_printf(f, "%-22s: %#16lx\n", "used capabilities", b->capabilities); seq_printf(f, "%-22s: %16s\n", "is resetting", b->reset_required ? "y" : "n"); /* format size info */ - seq_printf(f, "%-22s: %16u\n", "target", b->target); - seq_printf(f, "%-22s: %16u\n", "current", b->size); + seq_printf(f, "%-22s: %16lu\n", "target", READ_ONCE(b->target)); + seq_printf(f, "%-22s: %16llu\n", "current", atomic64_read(&b->size)); for (i = 0; i < VMW_BALLOON_CMD_NUM; i++) { if (vmballoon_cmd_names[i] == NULL) @@ -1259,8 +1511,9 @@ static inline void vmballoon_debugfs_exit(struct vmballoon *b) static int __init vmballoon_init(void) { + enum vmballoon_page_size_type page_size; int error; - unsigned is_2m_pages; + /* * Check if we are running on VMware's hypervisor and bail out * if we are not. @@ -1268,11 +1521,10 @@ static int __init vmballoon_init(void) if (x86_hyper_type != X86_HYPER_VMWARE) return -ENODEV; - for (is_2m_pages = 0; is_2m_pages < VMW_BALLOON_NUM_PAGE_SIZES; - is_2m_pages++) { - INIT_LIST_HEAD(&balloon.page_sizes[is_2m_pages].pages); - INIT_LIST_HEAD(&balloon.page_sizes[is_2m_pages].refused_pages); - } + for (page_size = VMW_BALLOON_4K_PAGE; + page_size <= VMW_BALLOON_LAST_SIZE; page_size++) + INIT_LIST_HEAD(&balloon.page_sizes[page_size].pages); + INIT_DELAYED_WORK(&balloon.dwork, vmballoon_work); @@ -1280,6 +1532,7 @@ static int __init vmballoon_init(void) if (error) return error; + spin_lock_init(&balloon.comm_lock); init_rwsem(&balloon.conf_sem); balloon.vmci_doorbell = VMCI_INVALID_HANDLE; balloon.batch_page = NULL; -- cgit v1.2.3-58-ga151 From 22d293ee8d82ae2ddbc96b4413d7fbf5c1ddfc17 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:19 -0700 Subject: vmw_balloon: general style cleanup Change all the remaining return values to int to avoid mistakes. Reduce indentation when possible. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index cb363db34b4a..99a831ea82c0 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -477,10 +477,9 @@ vmballoon_cmd(struct vmballoon *b, unsigned long cmd, unsigned long arg1, * Send "start" command to the host, communicating supported version * of the protocol. */ -static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) +static int vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) { unsigned long status, capabilities; - bool success; status = __vmballoon_cmd(b, VMW_BALLOON_CMD_START, req_caps, 0, &capabilities); @@ -488,14 +487,12 @@ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) switch (status) { case VMW_BALLOON_SUCCESS_WITH_CAPABILITIES: b->capabilities = capabilities; - success = true; break; case VMW_BALLOON_SUCCESS: b->capabilities = VMW_BALLOON_BASIC_CMDS; - success = true; break; default: - success = false; + return -EIO; } /* @@ -509,26 +506,29 @@ static bool vmballoon_send_start(struct vmballoon *b, unsigned long req_caps) b->max_page_size = VMW_BALLOON_2M_PAGE; - return success; + return 0; } -/* +/** + * vmballoon_send_guest_id - communicate guest type to the host. + * + * @b: pointer to the balloon. + * * Communicate guest type to the host so that it can adjust ballooning * algorithm to the one most appropriate for the guest. This command * is normally issued after sending "start" command and is part of * standard reset sequence. + * + * Return: zero on success or appropriate error code. */ -static bool vmballoon_send_guest_id(struct vmballoon *b) +static int vmballoon_send_guest_id(struct vmballoon *b) { unsigned long status; status = vmballoon_cmd(b, VMW_BALLOON_CMD_GUEST_ID, VMW_BALLOON_GUEST_ID, 0); - if (status == VMW_BALLOON_SUCCESS) - return true; - - return false; + return status == VMW_BALLOON_SUCCESS ? 0 : -EIO; } /** @@ -1215,8 +1215,15 @@ static void vmballoon_vmci_cleanup(struct vmballoon *b) } } -/* - * Initialize vmci doorbell, to get notified as soon as balloon changes +/** + * vmballoon_vmci_init - Initialize vmci doorbell. + * + * @b: pointer to the balloon. + * + * Return: zero on success or when wakeup command not supported. Error-code + * otherwise. + * + * Initialize vmci doorbell, to get notified as soon as balloon changes. */ static int vmballoon_vmci_init(struct vmballoon *b) { @@ -1278,7 +1285,7 @@ static void vmballoon_reset(struct vmballoon *b) /* free all pages, skipping monitor unlock */ vmballoon_pop(b); - if (!vmballoon_send_start(b, VMW_BALLOON_CAPABILITIES)) + if (vmballoon_send_start(b, VMW_BALLOON_CAPABILITIES)) return; if ((b->capabilities & VMW_BALLOON_BATCHED_CMDS) != 0) { @@ -1302,7 +1309,7 @@ static void vmballoon_reset(struct vmballoon *b) if (error) pr_err("failed to initialize vmci doorbell\n"); - if (!vmballoon_send_guest_id(b)) + if (vmballoon_send_guest_id(b)) pr_err("failed to send guest ID to the host\n"); up_write(&b->conf_sem); -- cgit v1.2.3-58-ga151 From 8840a6f4a7b18cc3da54271b093516afa9eb4362 Mon Sep 17 00:00:00 2001 From: Nadav Amit Date: Thu, 20 Sep 2018 10:30:20 -0700 Subject: vmw_balloon: add reset stat It is useful to expose how many times the balloon resets. If it happens more than very rarely - this is an indication for a problem. Reviewed-by: Xavier Deguillard Signed-off-by: Nadav Amit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_balloon.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 99a831ea82c0..9b0b3fa4f836 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -218,7 +218,8 @@ enum vmballoon_stat_page { enum vmballoon_stat_general { VMW_BALLOON_STAT_TIMER, VMW_BALLOON_STAT_DOORBELL, - VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_DOORBELL + VMW_BALLOON_STAT_RESET, + VMW_BALLOON_STAT_LAST = VMW_BALLOON_STAT_RESET }; #define VMW_BALLOON_STAT_NUM (VMW_BALLOON_STAT_LAST + 1) @@ -1303,6 +1304,7 @@ static void vmballoon_reset(struct vmballoon *b) vmballoon_deinit_batching(b); } + vmballoon_stats_gen_inc(b, VMW_BALLOON_STAT_RESET); b->reset_required = false; error = vmballoon_vmci_init(b); @@ -1380,7 +1382,8 @@ static const char * const vmballoon_stat_page_names[] = { static const char * const vmballoon_stat_names[] = { [VMW_BALLOON_STAT_TIMER] = "timer", - [VMW_BALLOON_STAT_DOORBELL] = "doorbell" + [VMW_BALLOON_STAT_DOORBELL] = "doorbell", + [VMW_BALLOON_STAT_RESET] = "reset", }; static int vmballoon_enable_stats(struct vmballoon *b) -- cgit v1.2.3-58-ga151 From 7153d9afdbd52805f99c28f51e81ee65f5dabbf8 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 11 Sep 2018 17:58:58 +0100 Subject: firmware: vpd: fix spelling mistake "partion" -> "partition" Trivial fix to spelling mistake in comment Signed-off-by: Colin Ian King Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/vpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index 1aa67bb5d8c0..c0c0b4e4e281 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -198,7 +198,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, sec->name = name; - /* We want to export the raw partion with name ${name}_raw */ + /* We want to export the raw partition with name ${name}_raw */ sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name); if (!sec->raw_name) { err = -ENOMEM; -- cgit v1.2.3-58-ga151 From 3104389edc012e2567cabb94af8763121c33e72b Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Sun, 16 Sep 2018 21:08:45 +0800 Subject: misc: sram: remove redundant null pointer check before of_node_put of_node_put has taken the null pinter check into account. So it is safe to remove the duplicated check before of_node_put. Signed-off-by: zhong jiang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 74b183baf044..80d8cbe8c01a 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -323,10 +323,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) cur_start = block->start + block->size; } - err_chunks: - if (child) - of_node_put(child); - +err_chunks: + of_node_put(child); kfree(rblocks); return ret; -- cgit v1.2.3-58-ga151 From 1830dad34c070161fda2ff1db77b39ffa78aa380 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:46 -0700 Subject: slimbus: ngd: register ngd driver only once. Move ngd platform driver out of loop so that it registers only once. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 14a9d18306cb..f872166f8614 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1342,7 +1342,6 @@ static int of_qcom_slim_ngd_register(struct device *parent, ngd->base = ctrl->base + ngd->id * data->offset + (ngd->id - 1) * data->size; ctrl->ngd = ngd; - platform_driver_register(&qcom_slim_ngd_driver); return 0; } @@ -1441,6 +1440,7 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) init_completion(&ctrl->reconf); init_completion(&ctrl->qmi.qmi_comp); + platform_driver_register(&qcom_slim_ngd_driver); return of_qcom_slim_ngd_register(dev, ctrl); } -- cgit v1.2.3-58-ga151 From 9652e6aa62a1836494ebb8dbd402587c083b568c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:45 -0700 Subject: slimbus: ngd: return proper error code instead of zero It looks like there is a typo in probe return. Fix it. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index f872166f8614..ef5eb9e750c2 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1389,7 +1389,7 @@ wq_err: if (ctrl->mwq) destroy_workqueue(ctrl->mwq); - return 0; + return ret; } static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev) -- cgit v1.2.3-58-ga151 From 94fe5f2b45c4108885e4b71f6b181068632ec904 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:44 -0700 Subject: silmbus: ngd: register controller after power up. Register slimbus controller only after finishing powerup sequnce so that we do not endup in situation where core starts sending transactions before the controller is ready. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index ef5eb9e750c2..986e8de4146a 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1234,8 +1234,17 @@ static int qcom_slim_ngd_enable(struct qcom_slim_ngd_ctrl *ctrl, bool enable) pm_runtime_resume(ctrl->dev); pm_runtime_mark_last_busy(ctrl->dev); pm_runtime_put(ctrl->dev); + + ret = slim_register_controller(&ctrl->ctrl); + if (ret) { + dev_err(ctrl->dev, "error adding slim controller\n"); + return ret; + } + + dev_info(ctrl->dev, "SLIM controller Registered\n"); } else { qcom_slim_qmi_exit(ctrl); + slim_unregister_controller(&ctrl->ctrl); } return 0; @@ -1356,11 +1365,6 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev) int ret; ctrl->ctrl.dev = dev; - ret = slim_register_controller(&ctrl->ctrl); - if (ret) { - dev_err(dev, "error adding slim controller\n"); - return ret; - } pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, QCOM_SLIM_NGD_AUTOSUSPEND); @@ -1370,7 +1374,7 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev) ret = qcom_slim_ngd_qmi_svc_event_init(ctrl); if (ret) { dev_err(&pdev->dev, "QMI service registration failed:%d", ret); - goto err; + return ret; } INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker); @@ -1382,8 +1386,6 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev) } return 0; -err: - slim_unregister_controller(&ctrl->ctrl); wq_err: qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi); if (ctrl->mwq) @@ -1456,7 +1458,7 @@ static int qcom_slim_ngd_remove(struct platform_device *pdev) struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - slim_unregister_controller(&ctrl->ctrl); + qcom_slim_ngd_enable(ctrl, false); qcom_slim_ngd_exit_dma(ctrl); qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi); if (ctrl->mwq) -- cgit v1.2.3-58-ga151 From 0e321f19be94448ca0375e2e0cc28a5f3950e9bb Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:43 -0700 Subject: slimbus: ngd: validate logical address assigned by remote Validate logical address assigned by remote, in failure cases this value is all zeors. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/qcom-ngd-ctrl.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/slimbus/qcom-ngd-ctrl.c b/drivers/slimbus/qcom-ngd-ctrl.c index 986e8de4146a..7218fb963d0a 100644 --- a/drivers/slimbus/qcom-ngd-ctrl.c +++ b/drivers/slimbus/qcom-ngd-ctrl.c @@ -1004,6 +1004,7 @@ static int qcom_slim_ngd_get_laddr(struct slim_controller *ctrl, struct slim_eaddr *ea, u8 *laddr) { struct slim_val_inf msg = {0}; + u8 failed_ea[6] = {0, 0, 0, 0, 0, 0}; struct slim_msg_txn txn; u8 wbuf[10] = {0}; u8 rbuf[10] = {0}; @@ -1034,6 +1035,9 @@ static int qcom_slim_ngd_get_laddr(struct slim_controller *ctrl, return ret; } + if (!memcmp(rbuf, failed_ea, 6)) + return -ENXIO; + *laddr = rbuf[6]; return ret; -- cgit v1.2.3-58-ga151 From 14a649d33e8145ec14e0429e56bbb12660baa4df Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:42 -0700 Subject: slimbus: core: match device tree based devices correctly device_id for device tree based devices come from dt compatible string, such drivers need not provide non dt style device id table. Match those device using compatible strings. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 95b00d28ad6e..7cd635eb44ed 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -32,6 +32,10 @@ static int slim_device_match(struct device *dev, struct device_driver *drv) struct slim_device *sbdev = to_slim_device(dev); struct slim_driver *sbdrv = to_slim_driver(drv); + /* Attempt an OF style match first */ + if (of_driver_match_device(dev, drv)) + return 1; + return !!slim_match(sbdrv->id_table, sbdev); } @@ -77,7 +81,7 @@ EXPORT_SYMBOL_GPL(slimbus_bus); int __slim_driver_register(struct slim_driver *drv, struct module *owner) { /* ID table and probe are mandatory */ - if (!drv->id_table || !drv->probe) + if (!(drv->driver.of_match_table || drv->id_table) || !drv->probe) return -EINVAL; drv->driver.bus = &slimbus_bus; -- cgit v1.2.3-58-ga151 From 8f3d5fcde9df6c0b24ec127a18dd1e2e9f54f8e8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:41 -0700 Subject: slimbus: core: update device status in probe device status update can be racy with probe in some cases, so make sure it take lock during the probe. Also after probe the device is expected to be ready for communications, so make sure that a logical address can be assigned to it after probe. If it fails to do so then probe defer such instances. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/core.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 7cd635eb44ed..1888fafc0e73 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -43,8 +43,23 @@ static int slim_device_probe(struct device *dev) { struct slim_device *sbdev = to_slim_device(dev); struct slim_driver *sbdrv = to_slim_driver(dev->driver); + int ret; + + ret = sbdrv->probe(sbdev); + if (ret) + return ret; + + /* try getting the logical address after probe */ + ret = slim_get_logical_addr(sbdev); + if (!ret) { + if (sbdrv->device_status) + sbdrv->device_status(sbdev, sbdev->status); + } else { + dev_err(&sbdev->dev, "Failed to get logical address\n"); + ret = -EPROBE_DEFER; + } - return sbdrv->probe(sbdev); + return ret; } static int slim_device_remove(struct device *dev) -- cgit v1.2.3-58-ga151 From 9e663f4811c6fd58c03e918c3f0a2e08810e38c1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sun, 16 Sep 2018 16:45:40 -0700 Subject: slimbus: core: add support to uevent This patch adds support to uevent to help automatic module loading. Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/slimbus/core.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/slimbus/core.c b/drivers/slimbus/core.c index 1888fafc0e73..55eda5863a6b 100644 --- a/drivers/slimbus/core.c +++ b/drivers/slimbus/core.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include "slimbus.h" @@ -76,11 +77,24 @@ static int slim_device_remove(struct device *dev) return 0; } +static int slim_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct slim_device *sbdev = to_slim_device(dev); + int ret; + + ret = of_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + + return add_uevent_var(env, "MODALIAS=slim:%s", dev_name(&sbdev->dev)); +} + struct bus_type slimbus_bus = { .name = "slimbus", .match = slim_device_match, .probe = slim_device_probe, .remove = slim_device_remove, + .uevent = slim_device_uevent, }; EXPORT_SYMBOL_GPL(slimbus_bus); -- cgit v1.2.3-58-ga151 From 85dc2c65e6c975baaf36ea30f2ccc0a36a8c8add Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 14 Sep 2018 23:43:37 -0700 Subject: misc: echo: Remove unnecessary parentheses and simplify check for zero Clang warns when multiple pairs of parentheses are used for a single conditional statement. drivers/misc/echo/echo.c:384:27: warning: equality comparison with extraneous parentheses [-Wparentheses-equality] if ((ec->nonupdate_dwell == 0)) { ~~~~~~~~~~~~~~~~~~~~^~~~ drivers/misc/echo/echo.c:384:27: note: remove extraneous parentheses around the comparison to silence this warning if ((ec->nonupdate_dwell == 0)) { ~ ^ ~ drivers/misc/echo/echo.c:384:27: note: use '=' to turn this equality comparison into an assignment if ((ec->nonupdate_dwell == 0)) { ^~ = 1 warning generated. Remove them and while we're at it, simplify the zero check as '!var' is used more than 'var == 0'. Reported-by: Nick Desaulniers Signed-off-by: Nathan Chancellor Reviewed-by: Nick Desaulniers Signed-off-by: Greg Kroah-Hartman --- drivers/misc/echo/echo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/echo/echo.c b/drivers/misc/echo/echo.c index 8a5adc0d2e88..3ebe5d75ad6a 100644 --- a/drivers/misc/echo/echo.c +++ b/drivers/misc/echo/echo.c @@ -381,7 +381,7 @@ int16_t oslec_update(struct oslec_state *ec, int16_t tx, int16_t rx) */ ec->factor = 0; ec->shift = 0; - if ((ec->nonupdate_dwell == 0)) { + if (!ec->nonupdate_dwell) { int p, logp, shift; /* Determine: -- cgit v1.2.3-58-ga151 From fa0218ef733e6f247a1a3986e3eb12460064ac77 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Tue, 11 Sep 2018 10:44:03 -0700 Subject: misc: kgdbts: Fix restrict error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kgdbts current fails when compiled with restrict: drivers/misc/kgdbts.c: In function ‘configure_kgdbts’: drivers/misc/kgdbts.c:1070:2: error: ‘strcpy’ source argument is the same as destination [-Werror=restrict] strcpy(config, opt); ^~~~~~~~~~~~~~~~~~~ As the error says, config is being used in both the source and destination. Refactor the code to avoid the extra copy and put the parsing closer to the actual location. Signed-off-by: Laura Abbott Acked-by: Daniel Thompson Signed-off-by: Greg Kroah-Hartman --- drivers/misc/kgdbts.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index 6193270e7b3d..de20bdaa148d 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -985,6 +985,12 @@ static void kgdbts_run_tests(void) int nmi_sleep = 0; int i; + verbose = 0; + if (strstr(config, "V1")) + verbose = 1; + if (strstr(config, "V2")) + verbose = 2; + ptr = strchr(config, 'F'); if (ptr) fork_test = simple_strtol(ptr + 1, NULL, 10); @@ -1068,13 +1074,6 @@ static int kgdbts_option_setup(char *opt) return -ENOSPC; } strcpy(config, opt); - - verbose = 0; - if (strstr(config, "V1")) - verbose = 1; - if (strstr(config, "V2")) - verbose = 2; - return 0; } @@ -1086,9 +1085,6 @@ static int configure_kgdbts(void) if (!strlen(config) || isspace(config[0])) goto noconfig; - err = kgdbts_option_setup(config); - if (err) - goto noconfig; final_ack = 0; run_plant_and_detach_test(1); -- cgit v1.2.3-58-ga151 From 7052c5e12851ed5882ca0d0d201060511e8ffa02 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Tue, 18 Sep 2018 16:59:34 +0800 Subject: misc: remove redundant include moduleparam.h module.h already contains moduleparam.h, so it is safe to remove the redundant include. The issue is detected with the help of Coccinelle. Signed-off-by: zhong jiang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus-fixup.c | 1 - drivers/misc/vmw_vmci/vmci_host.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c index a6f41f96f2a1..80215c312f0e 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index 83e0c95d20a4..be732e5ead75 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -15,7 +15,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3-58-ga151 From 6dbfdc1a4ee0030563af22bb682ff3bc590422f9 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Wed, 19 Sep 2018 12:50:22 -0700 Subject: misc: mic: scif: Remove unused variable Clang warns when a variable is assigned to itself. drivers/misc/mic/scif/scif_dma.c:1577:12: warning: explicitly assigning value of variable of type 'bool' (aka '_Bool') to itself [-Wself-assign] dst_local = dst_local; ~~~~~~~~~ ^ ~~~~~~~~~ 1 warning generated. This is usually done to avoid an unused variable warning, which is the case here. dst_local is used nowhere in this function, which has been the case since the initial code drop in commit 7cc31cd27752 ("misc: mic: SCIF DMA and CPU copy interface") in 2015. Just remove the variable, it can be added back if it was intended to be used. Link: https://github.com/ClangBuiltLinux/linux/issues/107 Signed-off-by: Nathan Chancellor Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 6369aeaa7056..03af8b5b7b0d 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -1553,7 +1553,7 @@ static int scif_rma_list_dma_copy_wrapper(struct scif_endpt *epd, int src_cache_off, dst_cache_off; s64 src_offset = work->src_offset, dst_offset = work->dst_offset; u8 *temp = NULL; - bool src_local = true, dst_local = false; + bool src_local = true; struct scif_dma_comp_cb *comp_cb; dma_addr_t src_dma_addr, dst_dma_addr; int err; @@ -1572,9 +1572,7 @@ static int scif_rma_list_dma_copy_wrapper(struct scif_endpt *epd, src_dma_addr = __scif_off_to_dma_addr(work->src_window, src_offset); dst_dma_addr = __scif_off_to_dma_addr(work->dst_window, dst_offset); src_local = work->src_window->type == SCIF_WINDOW_SELF; - dst_local = work->dst_window->type == SCIF_WINDOW_SELF; - dst_local = dst_local; /* Allocate dma_completion cb */ comp_cb = kzalloc(sizeof(*comp_cb), GFP_KERNEL); if (!comp_cb) -- cgit v1.2.3-58-ga151 From 02241995b004faa7d9ff628e97f24056190853f8 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Thu, 20 Sep 2018 10:29:13 +0800 Subject: misc: genwqe: should return proper error value. The function should return -EFAULT when copy_from_user fails. Even though the caller does not distinguish them. but we should keep backward compatibility. Signed-off-by: zhong jiang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_utils.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 8679e0bd8ec2..f0961ecb10b2 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -298,7 +298,7 @@ static int genwqe_sgl_size(int num_pages) int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, void __user *user_addr, size_t user_size, int write) { - int rc; + int ret = -ENOMEM; struct pci_dev *pci_dev = cd->pci_dev; sgl->fpage_offs = offset_in_page((unsigned long)user_addr); @@ -318,7 +318,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, if (get_order(sgl->sgl_size) > MAX_ORDER) { dev_err(&pci_dev->dev, "[%s] err: too much memory requested!\n", __func__); - return -ENOMEM; + return ret; } sgl->sgl = __genwqe_alloc_consistent(cd, sgl->sgl_size, @@ -326,7 +326,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, if (sgl->sgl == NULL) { dev_err(&pci_dev->dev, "[%s] err: no memory available!\n", __func__); - return -ENOMEM; + return ret; } /* Only use buffering on incomplete pages */ @@ -339,7 +339,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, /* Sync with user memory */ if (copy_from_user(sgl->fpage + sgl->fpage_offs, user_addr, sgl->fpage_size)) { - rc = -EFAULT; + ret = -EFAULT; goto err_out; } } @@ -352,7 +352,7 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, /* Sync with user memory */ if (copy_from_user(sgl->lpage, user_addr + user_size - sgl->lpage_size, sgl->lpage_size)) { - rc = -EFAULT; + ret = -EFAULT; goto err_out2; } } @@ -374,7 +374,8 @@ int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, sgl->sgl = NULL; sgl->sgl_dma_addr = 0; sgl->sgl_size = 0; - return -ENOMEM; + + return ret; } int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, -- cgit v1.2.3-58-ga151 From b85847eeea0d90954216b1d323a5c1c0c2cf07bf Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Thu, 20 Sep 2018 13:07:11 +0800 Subject: misc: genwqe: remove duplicated include file module.h has duplicated include. hence just remove redundant include file. Signed-off-by: zhong jiang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_base.c | 1 - drivers/misc/genwqe/card_ddcb.c | 1 - 2 files changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c index c7cd3675bcd1..d137d0fab9bf 100644 --- a/drivers/misc/genwqe/card_base.c +++ b/drivers/misc/genwqe/card_base.c @@ -24,7 +24,6 @@ * controlled from here. */ -#include #include #include #include diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c index 656449cb4476..9a65bd9d6152 100644 --- a/drivers/misc/genwqe/card_ddcb.c +++ b/drivers/misc/genwqe/card_ddcb.c @@ -27,7 +27,6 @@ */ #include -#include #include #include #include -- cgit v1.2.3-58-ga151 From 4d3ebd3658d8b87d8ead979725305adc6fae3855 Mon Sep 17 00:00:00 2001 From: Suzuki K Poulose Date: Thu, 20 Sep 2018 13:18:16 -0600 Subject: coreisght: tmc: Claim device before use Use CLAIM tags to make sure the device is available for use. Cc: Mathieu Poirier Signed-off-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 22 +++++++++++++++++++--- drivers/hwtracing/coresight/coresight-tmc-etr.c | 4 ++++ 2 files changed, 23 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 36af23d2c0f8..53fc83b72a49 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -36,6 +36,11 @@ static void __tmc_etb_enable_hw(struct tmc_drvdata *drvdata) static int tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { + int rc = coresight_claim_device(drvdata->base); + + if (rc) + return rc; + __tmc_etb_enable_hw(drvdata); return 0; } @@ -63,7 +68,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) return; } -static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) +static void __tmc_etb_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -79,6 +84,12 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } +static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) +{ + coresight_disclaim_device(drvdata); + __tmc_etb_disable_hw(drvdata); +} + static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -97,6 +108,11 @@ static void __tmc_etf_enable_hw(struct tmc_drvdata *drvdata) static int tmc_etf_enable_hw(struct tmc_drvdata *drvdata) { + int rc = coresight_claim_device(drvdata->base); + + if (rc) + return rc; + __tmc_etf_enable_hw(drvdata); return 0; } @@ -107,7 +123,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) tmc_flush_and_stop(drvdata); tmc_disable_hw(drvdata); - + coresight_disclaim_device_unlocked(drvdata->base); CS_LOCK(drvdata->base); } @@ -553,7 +569,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) /* Disable the TMC if need be */ if (drvdata->mode == CS_MODE_SYSFS) - tmc_etb_disable_hw(drvdata); + __tmc_etb_disable_hw(drvdata); drvdata->reading = true; out: diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index daad52146140..f684283890d3 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -991,6 +991,9 @@ static int tmc_etr_enable_hw(struct tmc_drvdata *drvdata, * this on. */ rc = tmc_etr_enable_catu(drvdata, etr_buf); + if (rc) + return rc; + rc = coresight_claim_device(drvdata->base); if (!rc) { drvdata->etr_buf = etr_buf; __tmc_etr_enable_hw(drvdata); @@ -1077,6 +1080,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) __tmc_etr_disable_hw(drvdata); /* Disable CATU device if this ETR is connected to one */ tmc_etr_disable_catu(drvdata); + coresight_disclaim_device(drvdata->base); /* Reset the ETR buf used by hardware */ drvdata->etr_buf = NULL; } -- cgit v1.2.3-58-ga151 From 9b9a4a3f140050797a881dec8f8c5ebb33a16ac0 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Fri, 14 Sep 2018 10:51:02 -0700 Subject: platform: goldfish: pipe: Remove license boilerplate The boilerplate license is not necessary when the SPDX line is present. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe_qemu.h | 14 -------------- 1 file changed, 14 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe_qemu.h b/drivers/platform/goldfish/goldfish_pipe_qemu.h index c272e11c5433..b4d78c108afd 100644 --- a/drivers/platform/goldfish/goldfish_pipe_qemu.h +++ b/drivers/platform/goldfish/goldfish_pipe_qemu.h @@ -1,18 +1,4 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2018 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - /* * IMPORTANT: The following constants must match the ones used and defined in * external/qemu/include/hw/misc/goldfish_pipe.h -- cgit v1.2.3-58-ga151 From 48a2d422b508e707f477f9160c795f70c5c20026 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Fri, 14 Sep 2018 10:51:03 -0700 Subject: platform: goldfish: pipe: Prevent memory corruption from several threads writing to the same variable Move the "pages" buffer into "struct goldfish_pipe". Since we are locking the mutex on the pipe in transfer_max_buffers, other threads willnot be able to write into it, but other pipe instances could be served because they have its own buffer. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index b4a484bbcdaa..6ae2b00f4bff 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -163,6 +163,9 @@ struct goldfish_pipe { /* Pointer to the parent goldfish_pipe_dev instance */ struct goldfish_pipe_dev *dev; + + /* A buffer of pages, too large to fit into a stack frame */ + struct page *pages[MAX_BUFFERS_PER_COMMAND]; }; /* The global driver data. Holds a reference to the i/o page used to @@ -340,21 +343,23 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe, s32 *consumed_size, int *status) { - static struct page *pages[MAX_BUFFERS_PER_COMMAND]; unsigned long first_page = address & PAGE_MASK; unsigned int iter_last_page_size; - int pages_count = pin_user_pages(first_page, last_page, - last_page_size, is_write, - pages, &iter_last_page_size); - - if (pages_count < 0) - return pages_count; + int pages_count; /* Serialize access to the pipe command buffers */ if (mutex_lock_interruptible(&pipe->lock)) return -ERESTARTSYS; - populate_rw_params(pages, pages_count, address, address_end, + pages_count = pin_user_pages(first_page, last_page, + last_page_size, is_write, + pipe->pages, &iter_last_page_size); + if (pages_count < 0) { + mutex_unlock(&pipe->lock); + return pages_count; + } + + populate_rw_params(pipe->pages, pages_count, address, address_end, first_page, last_page, iter_last_page_size, is_write, pipe->command_buffer); @@ -364,10 +369,9 @@ static int transfer_max_buffers(struct goldfish_pipe *pipe, *consumed_size = pipe->command_buffer->rw_params.consumed_size; - release_user_pages(pages, pages_count, is_write, *consumed_size); + release_user_pages(pipe->pages, pages_count, is_write, *consumed_size); mutex_unlock(&pipe->lock); - return 0; } -- cgit v1.2.3-58-ga151 From 77994c69b6523925c9c852b9afd2a2d4d40b8b8c Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Fri, 14 Sep 2018 10:51:04 -0700 Subject: platform: goldfish: pipe: Remove a redundant blank line The blank line is not required there. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 6ae2b00f4bff..8747ec330b7b 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -47,7 +47,6 @@ * exchange is properly mapped during a transfer. */ - #include #include #include -- cgit v1.2.3-58-ga151 From 6b979998a0c602d8de2475359e51d060fe60db37 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Fri, 14 Sep 2018 10:51:05 -0700 Subject: platform: goldfish: pipe: Remove redundant struct declarations goldfish_pipe_command is defines just after declaration and nothing refers to goldfish_pipe before it is defined. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 8747ec330b7b..6b21671c75ee 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -82,8 +82,6 @@ enum { }; struct goldfish_pipe_dev; -struct goldfish_pipe; -struct goldfish_pipe_command; /* A per-pipe command structure, shared with the host */ struct goldfish_pipe_command { -- cgit v1.2.3-58-ga151 From 581ae6b7225aaf469001557900763d14898e3683 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Fri, 14 Sep 2018 10:51:06 -0700 Subject: platform: goldfish: pipe: Remove redundant header include No symbols were used from this header. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 6b21671c75ee..fe01ef88ddfb 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -59,7 +59,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3-58-ga151 From 800b932969c53c4044ff9f9fd1ee793a87fa8ef0 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 14 Sep 2018 09:10:15 -0700 Subject: vmbus: pass channel to hv_process_channel_removal Rather than passing relid and then looking up the channel. Pass the channel directly, since caller already knows it. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 3 +-- drivers/hv/channel_mgmt.c | 17 +++++------------ drivers/hv/vmbus_drv.c | 3 +-- include/linux/hyperv.h | 2 +- 4 files changed, 8 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 741857d80da1..33e6db02dbab 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -690,8 +690,7 @@ void vmbus_close(struct vmbus_channel *channel) wait_for_completion(&cur_channel->rescind_event); mutex_lock(&vmbus_connection.channel_mutex); vmbus_close_internal(cur_channel); - hv_process_channel_removal( - cur_channel->offermsg.child_relid); + hv_process_channel_removal(cur_channel); } else { mutex_lock(&vmbus_connection.channel_mutex); vmbus_close_internal(cur_channel); diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 0f0e091c117c..b7c48ebdf6a1 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -385,21 +385,14 @@ static void vmbus_release_relid(u32 relid) trace_vmbus_release_relid(&msg, ret); } -void hv_process_channel_removal(u32 relid) +void hv_process_channel_removal(struct vmbus_channel *channel) { + struct vmbus_channel *primary_channel; unsigned long flags; - struct vmbus_channel *primary_channel, *channel; BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex)); - - /* - * Make sure channel is valid as we may have raced. - */ - channel = relid2channel(relid); - if (!channel) - return; - BUG_ON(!channel->rescind); + if (channel->target_cpu != get_cpu()) { put_cpu(); smp_call_function_single(channel->target_cpu, @@ -429,7 +422,7 @@ void hv_process_channel_removal(u32 relid) cpumask_clear_cpu(channel->target_cpu, &primary_channel->alloced_cpus_in_node); - vmbus_release_relid(relid); + vmbus_release_relid(channel->offermsg.child_relid); free_channel(channel); } @@ -943,7 +936,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) * The channel is currently not open; * it is safe for us to cleanup the channel. */ - hv_process_channel_removal(rescind->child_relid); + hv_process_channel_removal(channel); } else { complete(&channel->rescind_event); } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 4bbc420d1213..283d184280af 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -864,10 +864,9 @@ static void vmbus_device_release(struct device *device) struct vmbus_channel *channel = hv_dev->channel; mutex_lock(&vmbus_connection.channel_mutex); - hv_process_channel_removal(channel->offermsg.child_relid); + hv_process_channel_removal(channel); mutex_unlock(&vmbus_connection.channel_mutex); kfree(hv_dev); - } /* The one and only one */ diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 2c3798bcb01c..6c4575c7f46b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1443,7 +1443,7 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf, const int *srv_version, int srv_vercnt, int *nego_fw_version, int *nego_srv_version); -void hv_process_channel_removal(u32 relid); +void hv_process_channel_removal(struct vmbus_channel *channel); void vmbus_setevent(struct vmbus_channel *channel); /* -- cgit v1.2.3-58-ga151 From 52a42c2a90226dc61c99bbd0cb096deeb52c334b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 14 Sep 2018 09:10:16 -0700 Subject: vmbus: keep pointer to ring buffer page Avoid going from struct page to virt address (and back) by just keeping pointer to the allocated pages instead of virt address. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 20 +++++++++----------- drivers/uio/uio_hv_generic.c | 5 +++-- include/linux/hyperv.h | 2 +- 3 files changed, 13 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 33e6db02dbab..56ec0d96d876 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -91,11 +91,14 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, unsigned long flags; int ret, err = 0; struct page *page; + unsigned int order; if (send_ringbuffer_size % PAGE_SIZE || recv_ringbuffer_size % PAGE_SIZE) return -EINVAL; + order = get_order(send_ringbuffer_size + recv_ringbuffer_size); + spin_lock_irqsave(&newchannel->lock, flags); if (newchannel->state == CHANNEL_OPEN_STATE) { newchannel->state = CHANNEL_OPENING_STATE; @@ -110,21 +113,17 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, /* Allocate the ring buffer */ page = alloc_pages_node(cpu_to_node(newchannel->target_cpu), - GFP_KERNEL|__GFP_ZERO, - get_order(send_ringbuffer_size + - recv_ringbuffer_size)); + GFP_KERNEL|__GFP_ZERO, order); if (!page) - page = alloc_pages(GFP_KERNEL|__GFP_ZERO, - get_order(send_ringbuffer_size + - recv_ringbuffer_size)); + page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order); if (!page) { err = -ENOMEM; goto error_set_chnstate; } - newchannel->ringbuffer_pages = page_address(page); + newchannel->ringbuffer_page = page; newchannel->ringbuffer_pagecount = (send_ringbuffer_size + recv_ringbuffer_size) >> PAGE_SHIFT; @@ -239,8 +238,7 @@ error_free_gpadl: error_free_pages: hv_ringbuffer_cleanup(&newchannel->outbound); hv_ringbuffer_cleanup(&newchannel->inbound); - __free_pages(page, - get_order(send_ringbuffer_size + recv_ringbuffer_size)); + __free_pages(page, order); error_set_chnstate: newchannel->state = CHANNEL_OPEN_STATE; return err; @@ -658,8 +656,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel) hv_ringbuffer_cleanup(&channel->outbound); hv_ringbuffer_cleanup(&channel->inbound); - free_pages((unsigned long)channel->ringbuffer_pages, - get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); + __free_pages(channel->ringbuffer_page, + get_order(channel->ringbuffer_pagecount << PAGE_SHIFT)); out: return ret; diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index a08860260f55..ba67a5267557 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -130,11 +130,12 @@ static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj, = container_of(kobj, struct vmbus_channel, kobj); struct hv_device *dev = channel->primary_channel->device_obj; u16 q_idx = channel->offermsg.offer.sub_channel_index; + void *ring_buffer = page_address(channel->ringbuffer_page); dev_dbg(&dev->device, "mmap channel %u pages %#lx at %#lx\n", q_idx, vma_pages(vma), vma->vm_pgoff); - return vm_iomap_memory(vma, virt_to_phys(channel->ringbuffer_pages), + return vm_iomap_memory(vma, virt_to_phys(ring_buffer), channel->ringbuffer_pagecount << PAGE_SHIFT); } @@ -223,7 +224,7 @@ hv_uio_probe(struct hv_device *dev, /* mem resources */ pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings"; pdata->info.mem[TXRX_RING_MAP].addr - = (uintptr_t)dev->channel->ringbuffer_pages; + = (uintptr_t)page_address(dev->channel->ringbuffer_page); pdata->info.mem[TXRX_RING_MAP].size = dev->channel->ringbuffer_pagecount << PAGE_SHIFT; pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 6c4575c7f46b..a6c32d2d090b 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -739,7 +739,7 @@ struct vmbus_channel { u32 ringbuffer_gpadlhandle; /* Allocated memory for ring buffer */ - void *ringbuffer_pages; + struct page *ringbuffer_page; u32 ringbuffer_pagecount; struct hv_ring_buffer_info outbound; /* send to parent */ struct hv_ring_buffer_info inbound; /* receive from parent */ -- cgit v1.2.3-58-ga151 From ae6935ed7d424ffa74d634da00767e7b03c98fd3 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 14 Sep 2018 09:10:17 -0700 Subject: vmbus: split ring buffer allocation from open The UIO driver needs the ring buffer to be persistent(reused) across open/close. Split the allocation and setup of ring buffer out of vmbus_open. For normal usage vmbus_open/vmbus_close there are no changes; only impacts uio_hv_generic which needs to keep ring buffer memory and reuse when application restarts. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 267 +++++++++++++++++++++++++++-------------------- drivers/hv/ring_buffer.c | 1 + include/linux/hyperv.h | 9 ++ 3 files changed, 162 insertions(+), 115 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 56ec0d96d876..ddadb7efd1cc 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -79,84 +79,96 @@ void vmbus_setevent(struct vmbus_channel *channel) } EXPORT_SYMBOL_GPL(vmbus_setevent); -/* - * vmbus_open - Open the specified channel. - */ -int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, - u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, - void (*onchannelcallback)(void *context), void *context) +/* vmbus_free_ring - drop mapping of ring buffer */ +void vmbus_free_ring(struct vmbus_channel *channel) { - struct vmbus_channel_open_channel *open_msg; - struct vmbus_channel_msginfo *open_info = NULL; - unsigned long flags; - int ret, err = 0; - struct page *page; - unsigned int order; + hv_ringbuffer_cleanup(&channel->outbound); + hv_ringbuffer_cleanup(&channel->inbound); - if (send_ringbuffer_size % PAGE_SIZE || - recv_ringbuffer_size % PAGE_SIZE) - return -EINVAL; + if (channel->ringbuffer_page) { + __free_pages(channel->ringbuffer_page, + get_order(channel->ringbuffer_pagecount + << PAGE_SHIFT)); + channel->ringbuffer_page = NULL; + } +} +EXPORT_SYMBOL_GPL(vmbus_free_ring); - order = get_order(send_ringbuffer_size + recv_ringbuffer_size); +/* vmbus_alloc_ring - allocate and map pages for ring buffer */ +int vmbus_alloc_ring(struct vmbus_channel *newchannel, + u32 send_size, u32 recv_size) +{ + struct page *page; + int order; - spin_lock_irqsave(&newchannel->lock, flags); - if (newchannel->state == CHANNEL_OPEN_STATE) { - newchannel->state = CHANNEL_OPENING_STATE; - } else { - spin_unlock_irqrestore(&newchannel->lock, flags); + if (send_size % PAGE_SIZE || recv_size % PAGE_SIZE) return -EINVAL; - } - spin_unlock_irqrestore(&newchannel->lock, flags); - - newchannel->onchannel_callback = onchannelcallback; - newchannel->channel_callback_context = context; /* Allocate the ring buffer */ + order = get_order(send_size + recv_size); page = alloc_pages_node(cpu_to_node(newchannel->target_cpu), GFP_KERNEL|__GFP_ZERO, order); if (!page) page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order); - if (!page) { - err = -ENOMEM; - goto error_set_chnstate; - } + if (!page) + return -ENOMEM; newchannel->ringbuffer_page = page; - newchannel->ringbuffer_pagecount = (send_ringbuffer_size + - recv_ringbuffer_size) >> PAGE_SHIFT; + newchannel->ringbuffer_pagecount = (send_size + recv_size) >> PAGE_SHIFT; + newchannel->ringbuffer_send_offset = send_size >> PAGE_SHIFT; - ret = hv_ringbuffer_init(&newchannel->outbound, page, - send_ringbuffer_size >> PAGE_SHIFT); + return 0; +} +EXPORT_SYMBOL_GPL(vmbus_alloc_ring); - if (ret != 0) { - err = ret; - goto error_free_pages; - } +static int __vmbus_open(struct vmbus_channel *newchannel, + void *userdata, u32 userdatalen, + void (*onchannelcallback)(void *context), void *context) +{ + struct vmbus_channel_open_channel *open_msg; + struct vmbus_channel_msginfo *open_info = NULL; + struct page *page = newchannel->ringbuffer_page; + u32 send_pages, recv_pages; + unsigned long flags; + int err; - ret = hv_ringbuffer_init(&newchannel->inbound, - &page[send_ringbuffer_size >> PAGE_SHIFT], - recv_ringbuffer_size >> PAGE_SHIFT); - if (ret != 0) { - err = ret; - goto error_free_pages; + if (userdatalen > MAX_USER_DEFINED_BYTES) + return -EINVAL; + + send_pages = newchannel->ringbuffer_send_offset; + recv_pages = newchannel->ringbuffer_pagecount - send_pages; + + spin_lock_irqsave(&newchannel->lock, flags); + if (newchannel->state != CHANNEL_OPEN_STATE) { + spin_unlock_irqrestore(&newchannel->lock, flags); + return -EINVAL; } + spin_unlock_irqrestore(&newchannel->lock, flags); + newchannel->state = CHANNEL_OPENING_STATE; + newchannel->onchannel_callback = onchannelcallback; + newchannel->channel_callback_context = context; + + err = hv_ringbuffer_init(&newchannel->outbound, page, send_pages); + if (err) + goto error_clean_ring; + + err = hv_ringbuffer_init(&newchannel->inbound, + &page[send_pages], recv_pages); + if (err) + goto error_clean_ring; /* Establish the gpadl for the ring buffer */ newchannel->ringbuffer_gpadlhandle = 0; - ret = vmbus_establish_gpadl(newchannel, - page_address(page), - send_ringbuffer_size + - recv_ringbuffer_size, + err = vmbus_establish_gpadl(newchannel, + page_address(newchannel->ringbuffer_page), + (send_pages + recv_pages) << PAGE_SHIFT, &newchannel->ringbuffer_gpadlhandle); - - if (ret != 0) { - err = ret; - goto error_free_pages; - } + if (err) + goto error_clean_ring; /* Create and init the channel open message */ open_info = kmalloc(sizeof(*open_info) + @@ -175,15 +187,9 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, open_msg->openid = newchannel->offermsg.child_relid; open_msg->child_relid = newchannel->offermsg.child_relid; open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; - open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> - PAGE_SHIFT; + open_msg->downstream_ringbuffer_pageoffset = newchannel->ringbuffer_send_offset; open_msg->target_vp = newchannel->target_vp; - if (userdatalen > MAX_USER_DEFINED_BYTES) { - err = -EINVAL; - goto error_free_gpadl; - } - if (userdatalen) memcpy(open_msg->userdata, userdata, userdatalen); @@ -194,18 +200,16 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (newchannel->rescind) { err = -ENODEV; - goto error_free_gpadl; + goto error_free_info; } - ret = vmbus_post_msg(open_msg, + err = vmbus_post_msg(open_msg, sizeof(struct vmbus_channel_open_channel), true); - trace_vmbus_open(open_msg, ret); + trace_vmbus_open(open_msg, err); - if (ret != 0) { - err = ret; + if (err != 0) goto error_clean_msglist; - } wait_for_completion(&open_info->waitevent); @@ -215,12 +219,12 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (newchannel->rescind) { err = -ENODEV; - goto error_free_gpadl; + goto error_free_info; } if (open_info->response.open_result.status) { err = -EAGAIN; - goto error_free_gpadl; + goto error_free_info; } newchannel->state = CHANNEL_OPENED_STATE; @@ -231,18 +235,50 @@ error_clean_msglist: spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); - +error_free_info: + kfree(open_info); error_free_gpadl: vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle); - kfree(open_info); -error_free_pages: + newchannel->ringbuffer_gpadlhandle = 0; +error_clean_ring: hv_ringbuffer_cleanup(&newchannel->outbound); hv_ringbuffer_cleanup(&newchannel->inbound); - __free_pages(page, order); -error_set_chnstate: newchannel->state = CHANNEL_OPEN_STATE; return err; } + +/* + * vmbus_connect_ring - Open the channel but reuse ring buffer + */ +int vmbus_connect_ring(struct vmbus_channel *newchannel, + void (*onchannelcallback)(void *context), void *context) +{ + return __vmbus_open(newchannel, NULL, 0, onchannelcallback, context); +} +EXPORT_SYMBOL_GPL(vmbus_connect_ring); + +/* + * vmbus_open - Open the specified channel. + */ +int vmbus_open(struct vmbus_channel *newchannel, + u32 send_ringbuffer_size, u32 recv_ringbuffer_size, + void *userdata, u32 userdatalen, + void (*onchannelcallback)(void *context), void *context) +{ + int err; + + err = vmbus_alloc_ring(newchannel, send_ringbuffer_size, + recv_ringbuffer_size); + if (err) + return err; + + err = __vmbus_open(newchannel, userdata, userdatalen, + onchannelcallback, context); + if (err) + vmbus_free_ring(newchannel); + + return err; +} EXPORT_SYMBOL_GPL(vmbus_open); /* Used for Hyper-V Socket: a guest client's connect() to the host */ @@ -610,10 +646,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel) * in Hyper-V Manager), the driver's remove() invokes vmbus_close(): * here we should skip most of the below cleanup work. */ - if (channel->state != CHANNEL_OPENED_STATE) { - ret = -EINVAL; - goto out; - } + if (channel->state != CHANNEL_OPENED_STATE) + return -EINVAL; channel->state = CHANNEL_OPEN_STATE; @@ -635,11 +669,10 @@ static int vmbus_close_internal(struct vmbus_channel *channel) * If we failed to post the close msg, * it is perhaps better to leak memory. */ - goto out; } /* Tear down the gpadl for the channel's ring buffer */ - if (channel->ringbuffer_gpadlhandle) { + else if (channel->ringbuffer_gpadlhandle) { ret = vmbus_teardown_gpadl(channel, channel->ringbuffer_gpadlhandle); if (ret) { @@ -648,59 +681,63 @@ static int vmbus_close_internal(struct vmbus_channel *channel) * If we failed to teardown gpadl, * it is perhaps better to leak memory. */ - goto out; } - } - - /* Cleanup the ring buffers for this channel */ - hv_ringbuffer_cleanup(&channel->outbound); - hv_ringbuffer_cleanup(&channel->inbound); - __free_pages(channel->ringbuffer_page, - get_order(channel->ringbuffer_pagecount << PAGE_SHIFT)); + channel->ringbuffer_gpadlhandle = 0; + } -out: return ret; } -/* - * vmbus_close - Close the specified channel - */ -void vmbus_close(struct vmbus_channel *channel) +/* disconnect ring - close all channels */ +int vmbus_disconnect_ring(struct vmbus_channel *channel) { - struct list_head *cur, *tmp; - struct vmbus_channel *cur_channel; + struct vmbus_channel *cur_channel, *tmp; + unsigned long flags; + LIST_HEAD(list); + int ret; - if (channel->primary_channel != NULL) { - /* - * We will only close sub-channels when - * the primary is closed. - */ - return; - } - /* - * Close all the sub-channels first and then close the - * primary channel. - */ - list_for_each_safe(cur, tmp, &channel->sc_list) { - cur_channel = list_entry(cur, struct vmbus_channel, sc_list); - if (cur_channel->rescind) { + if (channel->primary_channel != NULL) + return -EINVAL; + + /* Snapshot the list of subchannels */ + spin_lock_irqsave(&channel->lock, flags); + list_splice_init(&channel->sc_list, &list); + channel->num_sc = 0; + spin_unlock_irqrestore(&channel->lock, flags); + + list_for_each_entry_safe(cur_channel, tmp, &list, sc_list) { + if (cur_channel->rescind) wait_for_completion(&cur_channel->rescind_event); - mutex_lock(&vmbus_connection.channel_mutex); - vmbus_close_internal(cur_channel); - hv_process_channel_removal(cur_channel); - } else { - mutex_lock(&vmbus_connection.channel_mutex); - vmbus_close_internal(cur_channel); + + mutex_lock(&vmbus_connection.channel_mutex); + if (vmbus_close_internal(cur_channel) == 0) { + vmbus_free_ring(cur_channel); + + if (cur_channel->rescind) + hv_process_channel_removal(cur_channel); } mutex_unlock(&vmbus_connection.channel_mutex); } + /* * Now close the primary. */ mutex_lock(&vmbus_connection.channel_mutex); - vmbus_close_internal(channel); + ret = vmbus_close_internal(channel); mutex_unlock(&vmbus_connection.channel_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(vmbus_disconnect_ring); + +/* + * vmbus_close - Close the specified channel + */ +void vmbus_close(struct vmbus_channel *channel) +{ + if (vmbus_disconnect_ring(channel) == 0) + vmbus_free_ring(channel); } EXPORT_SYMBOL_GPL(vmbus_close); diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 3e90eb91db45..64d0c85d5161 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -241,6 +241,7 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) { vunmap(ring_info->ring_buffer); + ring_info->ring_buffer = NULL; } /* Write to the ring buffer. */ diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index a6c32d2d090b..b3e24368930a 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -741,6 +741,7 @@ struct vmbus_channel { /* Allocated memory for ring buffer */ struct page *ringbuffer_page; u32 ringbuffer_pagecount; + u32 ringbuffer_send_offset; struct hv_ring_buffer_info outbound; /* send to parent */ struct hv_ring_buffer_info inbound; /* receive from parent */ @@ -1021,6 +1022,14 @@ struct vmbus_packet_mpb_array { struct hv_mpb_array range; } __packed; +int vmbus_alloc_ring(struct vmbus_channel *channel, + u32 send_size, u32 recv_size); +void vmbus_free_ring(struct vmbus_channel *channel); + +int vmbus_connect_ring(struct vmbus_channel *channel, + void (*onchannel_callback)(void *context), + void *context); +int vmbus_disconnect_ring(struct vmbus_channel *channel); extern int vmbus_open(struct vmbus_channel *channel, u32 send_ringbuffersize, -- cgit v1.2.3-58-ga151 From bfddabfa230452cea32aae82f9cd85ab22601acf Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 14 Sep 2018 09:10:18 -0700 Subject: uio: introduce UIO_MEM_IOVA Introduce the concept of mapping physical memory locations that are normal memory. The new type UIO_MEM_IOVA are similar to existing UIO_MEM_PHYS but the backing memory is not marked as uncached. Also, indent related switch to the currently used style. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 24 +++++++++++++----------- include/linux/uio_driver.h | 1 + 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 0ffb324aa038..e601bd3fbae1 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -738,7 +738,8 @@ static int uio_mmap_physical(struct vm_area_struct *vma) return -EINVAL; vma->vm_ops = &uio_physical_vm_ops; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + if (idev->info->mem[mi].memtype == UIO_MEM_PHYS) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); /* * We cannot use the vm_iomap_memory() helper here, @@ -795,18 +796,19 @@ static int uio_mmap(struct file *filep, struct vm_area_struct *vma) } switch (idev->info->mem[mi].memtype) { - case UIO_MEM_PHYS: - ret = uio_mmap_physical(vma); - break; - case UIO_MEM_LOGICAL: - case UIO_MEM_VIRTUAL: - ret = uio_mmap_logical(vma); - break; - default: - ret = -EINVAL; + case UIO_MEM_IOVA: + case UIO_MEM_PHYS: + ret = uio_mmap_physical(vma); + break; + case UIO_MEM_LOGICAL: + case UIO_MEM_VIRTUAL: + ret = uio_mmap_logical(vma); + break; + default: + ret = -EINVAL; } -out: + out: mutex_unlock(&idev->info_lock); return ret; } diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h index 6f8b68cd460f..a3cd7cb67a69 100644 --- a/include/linux/uio_driver.h +++ b/include/linux/uio_driver.h @@ -133,6 +133,7 @@ extern void uio_event_notify(struct uio_info *info); #define UIO_MEM_PHYS 1 #define UIO_MEM_LOGICAL 2 #define UIO_MEM_VIRTUAL 3 +#define UIO_MEM_IOVA 4 /* defines for uio_port->porttype */ #define UIO_PORT_NONE 0 -- cgit v1.2.3-58-ga151 From 9da197f1df40c838f0f06abf94cd23b4ed81e522 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 14 Sep 2018 09:10:19 -0700 Subject: hv_uio_generic: map ringbuffer phys addr The ring buffer is contiguous IOVA and is mapped via phys addr for sysfs file. Use same method for the UIO mapping. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_hv_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index ba67a5267557..53f5610c6065 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -224,10 +224,10 @@ hv_uio_probe(struct hv_device *dev, /* mem resources */ pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings"; pdata->info.mem[TXRX_RING_MAP].addr - = (uintptr_t)page_address(dev->channel->ringbuffer_page); + = (uintptr_t)virt_to_phys(page_address(dev->channel->ringbuffer_page)); pdata->info.mem[TXRX_RING_MAP].size = dev->channel->ringbuffer_pagecount << PAGE_SHIFT; - pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL; + pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_IOVA; pdata->info.mem[INT_PAGE_MAP].name = "int_page"; pdata->info.mem[INT_PAGE_MAP].addr -- cgit v1.2.3-58-ga151 From cdfa835c6e5e87d145f9f632b58843de97509f2b Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Fri, 14 Sep 2018 09:10:20 -0700 Subject: uio_hv_generic: defer opening vmbus until first use This fixes two design flaws in hv_uio_generic. Since hv_uio_probe is called from vmbus_probe with lock held it potentially can cause sleep in an atomic section because vmbus_open will wait for response from host. The hv_uio_generic driver could not handle applications exiting and restarting because the vmbus channel was persistent. Change the semantics so that the buffers are allocated on probe, but not attached to host until device is opened. Signed-off-by: Stephen Hemminger Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_hv_generic.c | 104 ++++++++++++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 53f5610c6065..c2493d011225 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -55,6 +55,7 @@ enum hv_uio_map { struct hv_uio_private_data { struct uio_info info; struct hv_device *device; + atomic_t refcnt; void *recv_buf; u32 recv_gpadl; @@ -128,12 +129,10 @@ static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj, { struct vmbus_channel *channel = container_of(kobj, struct vmbus_channel, kobj); - struct hv_device *dev = channel->primary_channel->device_obj; - u16 q_idx = channel->offermsg.offer.sub_channel_index; void *ring_buffer = page_address(channel->ringbuffer_page); - dev_dbg(&dev->device, "mmap channel %u pages %#lx at %#lx\n", - q_idx, vma_pages(vma), vma->vm_pgoff); + if (channel->state != CHANNEL_OPENED_STATE) + return -ENODEV; return vm_iomap_memory(vma, virt_to_phys(ring_buffer), channel->ringbuffer_pagecount << PAGE_SHIFT); @@ -176,57 +175,103 @@ hv_uio_new_channel(struct vmbus_channel *new_sc) } } +/* free the reserved buffers for send and receive */ static void hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata) { - if (pdata->send_gpadl) + if (pdata->send_gpadl) { vmbus_teardown_gpadl(dev->channel, pdata->send_gpadl); - vfree(pdata->send_buf); + pdata->send_gpadl = 0; + vfree(pdata->send_buf); + } - if (pdata->recv_gpadl) + if (pdata->recv_gpadl) { vmbus_teardown_gpadl(dev->channel, pdata->recv_gpadl); - vfree(pdata->recv_buf); + pdata->recv_gpadl = 0; + vfree(pdata->recv_buf); + } +} + +/* VMBus primary channel is opened on first use */ +static int +hv_uio_open(struct uio_info *info, struct inode *inode) +{ + struct hv_uio_private_data *pdata + = container_of(info, struct hv_uio_private_data, info); + struct hv_device *dev = pdata->device; + int ret; + + if (atomic_inc_return(&pdata->refcnt) != 1) + return 0; + + ret = vmbus_connect_ring(dev->channel, + hv_uio_channel_cb, dev->channel); + + if (ret == 0) + dev->channel->inbound.ring_buffer->interrupt_mask = 1; + else + atomic_dec(&pdata->refcnt); + + return ret; +} + +/* VMBus primary channel is closed on last close */ +static int +hv_uio_release(struct uio_info *info, struct inode *inode) +{ + struct hv_uio_private_data *pdata + = container_of(info, struct hv_uio_private_data, info); + struct hv_device *dev = pdata->device; + int ret = 0; + + if (atomic_dec_and_test(&pdata->refcnt)) + ret = vmbus_disconnect_ring(dev->channel); + + return ret; } static int hv_uio_probe(struct hv_device *dev, const struct hv_vmbus_device_id *dev_id) { + struct vmbus_channel *channel = dev->channel; struct hv_uio_private_data *pdata; + void *ring_buffer; int ret; + /* Communicating with host has to be via shared memory not hypercall */ + if (!channel->offermsg.monitor_allocated) { + dev_err(&dev->device, "vmbus channel requires hypercall\n"); + return -ENOTSUPP; + } + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; - ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE, - HV_RING_SIZE * PAGE_SIZE, NULL, 0, - hv_uio_channel_cb, dev->channel); + ret = vmbus_alloc_ring(channel, HV_RING_SIZE * PAGE_SIZE, + HV_RING_SIZE * PAGE_SIZE); if (ret) goto fail; - /* Communicating with host has to be via shared memory not hypercall */ - if (!dev->channel->offermsg.monitor_allocated) { - dev_err(&dev->device, "vmbus channel requires hypercall\n"); - ret = -ENOTSUPP; - goto fail_close; - } - - dev->channel->inbound.ring_buffer->interrupt_mask = 1; - set_channel_read_mode(dev->channel, HV_CALL_ISR); + set_channel_read_mode(channel, HV_CALL_ISR); /* Fill general uio info */ pdata->info.name = "uio_hv_generic"; pdata->info.version = DRIVER_VERSION; pdata->info.irqcontrol = hv_uio_irqcontrol; + pdata->info.open = hv_uio_open; + pdata->info.release = hv_uio_release; pdata->info.irq = UIO_IRQ_CUSTOM; + atomic_set(&pdata->refcnt, 0); /* mem resources */ pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings"; + ring_buffer = page_address(channel->ringbuffer_page); pdata->info.mem[TXRX_RING_MAP].addr - = (uintptr_t)virt_to_phys(page_address(dev->channel->ringbuffer_page)); + = (uintptr_t)virt_to_phys(ring_buffer); pdata->info.mem[TXRX_RING_MAP].size - = dev->channel->ringbuffer_pagecount << PAGE_SHIFT; + = channel->ringbuffer_pagecount << PAGE_SHIFT; pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_IOVA; pdata->info.mem[INT_PAGE_MAP].name = "int_page"; @@ -247,7 +292,7 @@ hv_uio_probe(struct hv_device *dev, goto fail_close; } - ret = vmbus_establish_gpadl(dev->channel, pdata->recv_buf, + ret = vmbus_establish_gpadl(channel, pdata->recv_buf, RECV_BUFFER_SIZE, &pdata->recv_gpadl); if (ret) goto fail_close; @@ -261,14 +306,13 @@ hv_uio_probe(struct hv_device *dev, pdata->info.mem[RECV_BUF_MAP].size = RECV_BUFFER_SIZE; pdata->info.mem[RECV_BUF_MAP].memtype = UIO_MEM_VIRTUAL; - pdata->send_buf = vzalloc(SEND_BUFFER_SIZE); if (pdata->send_buf == NULL) { ret = -ENOMEM; goto fail_close; } - ret = vmbus_establish_gpadl(dev->channel, pdata->send_buf, + ret = vmbus_establish_gpadl(channel, pdata->send_buf, SEND_BUFFER_SIZE, &pdata->send_gpadl); if (ret) goto fail_close; @@ -290,10 +334,10 @@ hv_uio_probe(struct hv_device *dev, goto fail_close; } - vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); - vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel); + vmbus_set_chn_rescind_callback(channel, hv_uio_rescind); + vmbus_set_sc_create_callback(channel, hv_uio_new_channel); - ret = sysfs_create_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr); + ret = sysfs_create_bin_file(&channel->kobj, &ring_buffer_bin_attr); if (ret) dev_notice(&dev->device, "sysfs create ring bin file failed; %d\n", ret); @@ -304,7 +348,6 @@ hv_uio_probe(struct hv_device *dev, fail_close: hv_uio_cleanup(dev, pdata); - vmbus_close(dev->channel); fail: kfree(pdata); @@ -322,7 +365,8 @@ hv_uio_remove(struct hv_device *dev) uio_unregister_device(&pdata->info); hv_uio_cleanup(dev, pdata); hv_set_drvdata(dev, NULL); - vmbus_close(dev->channel); + + vmbus_free_ring(dev->channel); kfree(pdata); return 0; } -- cgit v1.2.3-58-ga151 From 3790e28cdbc8e7c4ffff62a9e4db89fe6e066c0b Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Thu, 20 Sep 2018 06:22:05 +0000 Subject: platform: goldfish: pipe: Make symbol 'goldfish_pipe_dev' static Fixes the following sparse warning: drivers/platform/goldfish/goldfish_pipe.c:214:26: warning: symbol 'goldfish_pipe_dev' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index fe01ef88ddfb..56665e879e5a 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -210,7 +210,7 @@ struct goldfish_pipe_dev { unsigned char __iomem *base; }; -struct goldfish_pipe_dev goldfish_pipe_dev; +static struct goldfish_pipe_dev goldfish_pipe_dev; static int goldfish_pipe_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) -- cgit v1.2.3-58-ga151 From c853d6904f1428c081de5367da2569933556a668 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:01 -0700 Subject: eeprom: eeprom_93xx46: use resource management Use resource managed variants of nvmem_register() and kzalloc(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/eeprom_93xx46.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 38766968bfa2..c6dd9ad9bf7b 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -439,7 +439,7 @@ static int eeprom_93xx46_probe(struct spi_device *spi) return -ENODEV; } - edev = kzalloc(sizeof(*edev), GFP_KERNEL); + edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL); if (!edev) return -ENOMEM; @@ -449,8 +449,7 @@ static int eeprom_93xx46_probe(struct spi_device *spi) edev->addrlen = 6; else { dev_err(&spi->dev, "unspecified address type\n"); - err = -EINVAL; - goto fail; + return -EINVAL; } mutex_init(&edev->lock); @@ -473,11 +472,9 @@ static int eeprom_93xx46_probe(struct spi_device *spi) edev->nvmem_config.word_size = 1; edev->nvmem_config.size = edev->size; - edev->nvmem = nvmem_register(&edev->nvmem_config); - if (IS_ERR(edev->nvmem)) { - err = PTR_ERR(edev->nvmem); - goto fail; - } + edev->nvmem = devm_nvmem_register(&spi->dev, &edev->nvmem_config); + if (IS_ERR(edev->nvmem)) + return PTR_ERR(edev->nvmem); dev_info(&spi->dev, "%d-bit eeprom %s\n", (pd->flags & EE_ADDR8) ? 8 : 16, @@ -490,21 +487,15 @@ static int eeprom_93xx46_probe(struct spi_device *spi) spi_set_drvdata(spi, edev); return 0; -fail: - kfree(edev); - return err; } static int eeprom_93xx46_remove(struct spi_device *spi) { struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi); - nvmem_unregister(edev->nvmem); - if (!(edev->pdata->flags & EE_READONLY)) device_remove_file(&spi->dev, &dev_attr_erase); - kfree(edev); return 0; } -- cgit v1.2.3-58-ga151 From 96d08fb43e30bb55fa1a76953097cf043807837b Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:02 -0700 Subject: eeprom: at25: use devm_nvmem_register() Use the resource managed variant of nvmem_register(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 840afb398f9e..d874df72853f 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -366,7 +366,7 @@ static int at25_probe(struct spi_device *spi) at25->nvmem_config.word_size = 1; at25->nvmem_config.size = chip.byte_len; - at25->nvmem = nvmem_register(&at25->nvmem_config); + at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config); if (IS_ERR(at25->nvmem)) return PTR_ERR(at25->nvmem); @@ -384,7 +384,6 @@ static int at25_remove(struct spi_device *spi) struct at25_data *at25; at25 = spi_get_drvdata(spi); - nvmem_unregister(at25->nvmem); return 0; } -- cgit v1.2.3-58-ga151 From d7b9fd1669d4a4f38281c4e29f1408e1bdce09b3 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:03 -0700 Subject: nvmem: provide nvmem_dev_name() Kernel users don't have any means of checking the names of nvmem devices. Add a routine that returns the name of the nvmem provider. This will be useful for future nvmem notifier subscribers - otherwise they can't check what device is being added/removed. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 13 +++++++++++++ include/linux/nvmem-consumer.h | 8 ++++++++ 2 files changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 6f064002f439..11afa3b6d551 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1343,6 +1343,19 @@ int nvmem_device_write(struct nvmem_device *nvmem, } EXPORT_SYMBOL_GPL(nvmem_device_write); +/** + * nvmem_dev_name() - Get the name of a given nvmem device. + * + * @nvmem: nvmem device. + * + * Return: name of the nvmem device. + */ +const char *nvmem_dev_name(struct nvmem_device *nvmem) +{ + return dev_name(&nvmem->dev); +} +EXPORT_SYMBOL_GPL(nvmem_dev_name); + static int __init nvmem_init(void) { return bus_register(&nvmem_bus_type); diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 4e85447f7860..0389fe00b177 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -55,6 +55,8 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, int nvmem_device_cell_write(struct nvmem_device *nvmem, struct nvmem_cell_info *info, void *buf); +const char *nvmem_dev_name(struct nvmem_device *nvmem); + #else static inline struct nvmem_cell *nvmem_cell_get(struct device *dev, @@ -143,6 +145,12 @@ static inline int nvmem_device_write(struct nvmem_device *nvmem, { return -ENOSYS; } + +static inline const char *nvmem_dev_name(struct nvmem_device *nvmem) +{ + return NULL; +} + #endif /* CONFIG_NVMEM */ #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) -- cgit v1.2.3-58-ga151 From 5db652c9f331ce7ae8662d9380ea4a404ba09344 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:04 -0700 Subject: nvmem: remove the name field from struct nvmem_device This field is never set and is only used in a single error message. Remove the field and use nvmem_dev_name() instead. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 11afa3b6d551..72313a1d215f 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -26,7 +26,6 @@ #include struct nvmem_device { - const char *name; struct module *owner; struct device dev; int stride; @@ -652,7 +651,7 @@ static struct nvmem_device *__nvmem_device_get(struct device_node *np, if (!try_module_get(nvmem->owner)) { dev_err(&nvmem->dev, "could not increase module refcount for cell %s\n", - nvmem->name); + nvmem_dev_name(nvmem)); mutex_lock(&nvmem_mutex); nvmem->users--; -- cgit v1.2.3-58-ga151 From 1852183e142e01cff391b630a7578ff8ddb1a5fa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:05 -0700 Subject: nvmem: use list_for_each_entry_safe in nvmem_device_remove_all_cells() Use the provided helper for iterating over list entries without having to use the list_entry() macro. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 72313a1d215f..0e963d83a099 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -308,14 +308,11 @@ static void nvmem_cell_drop(struct nvmem_cell *cell) static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) { - struct nvmem_cell *cell; - struct list_head *p, *n; + struct nvmem_cell *cell, *p; - list_for_each_safe(p, n, &nvmem_cells) { - cell = list_entry(p, struct nvmem_cell, node); + list_for_each_entry_safe(cell, p, &nvmem_cells, node) if (cell->nvmem == nvmem) nvmem_cell_drop(cell); - } } static void nvmem_cell_add(struct nvmem_cell *cell) -- cgit v1.2.3-58-ga151 From f9fcb7e36cdec27e996606b8b33337056559f8da Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:06 -0700 Subject: nvmem: remove a stray newline There are two empty lines between devm_nvmem_unregister() and __nvmem_device_get(). Remove one. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 0e963d83a099..1ced9caa811b 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -613,7 +613,6 @@ int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) } EXPORT_SYMBOL(devm_nvmem_unregister); - static struct nvmem_device *__nvmem_device_get(struct device_node *np, struct nvmem_cell **cellp, const char *cell_id) -- cgit v1.2.3-58-ga151 From fa72d847d68d7833b77a4bef944cf2c5baf56f49 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:07 -0700 Subject: nvmem: check the return value of nvmem_add_cells() This function can fail so check its return value in nvmem_register() and act accordingly. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 1ced9caa811b..0ef84441ebe5 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -512,11 +512,17 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_device_del; } - if (config->cells) - nvmem_add_cells(nvmem, config->cells, config->ncells); + if (config->cells) { + rval = nvmem_add_cells(nvmem, config->cells, config->ncells); + if (rval) + goto err_teardown_compat; + } return nvmem; +err_teardown_compat: + if (config->compat) + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); err_device_del: device_del(&nvmem->dev); err_put_device: -- cgit v1.2.3-58-ga151 From c1de7f43bd84271e438a4f2dadc4ba9a353baaea Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:08 -0700 Subject: nvmem: use kref Use kref for reference counting. Use an approach similar to the one seen in the common clock subsystem: don't actually destroy the nvmem device until the last user puts it. This way we can get rid of the users check from nvmem_unregister(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 0ef84441ebe5..920b56e500cc 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ struct nvmem_device { int stride; int word_size; int id; - int users; + struct kref refcnt; size_t size; bool read_only; int flags; @@ -463,6 +464,8 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) return ERR_PTR(rval); } + kref_init(&nvmem->refcnt); + nvmem->id = rval; nvmem->owner = config->owner; if (!nvmem->owner && config->dev->driver) @@ -532,6 +535,20 @@ err_put_device: } EXPORT_SYMBOL_GPL(nvmem_register); +static void nvmem_device_release(struct kref *kref) +{ + struct nvmem_device *nvmem; + + nvmem = container_of(kref, struct nvmem_device, refcnt); + + if (nvmem->flags & FLAG_COMPAT) + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + + nvmem_device_remove_all_cells(nvmem); + device_del(&nvmem->dev); + put_device(&nvmem->dev); +} + /** * nvmem_unregister() - Unregister previously registered nvmem device * @@ -541,19 +558,7 @@ EXPORT_SYMBOL_GPL(nvmem_register); */ int nvmem_unregister(struct nvmem_device *nvmem) { - mutex_lock(&nvmem_mutex); - if (nvmem->users) { - mutex_unlock(&nvmem_mutex); - return -EBUSY; - } - mutex_unlock(&nvmem_mutex); - - if (nvmem->flags & FLAG_COMPAT) - device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); - - nvmem_device_remove_all_cells(nvmem); - device_del(&nvmem->dev); - put_device(&nvmem->dev); + kref_put(&nvmem->refcnt, nvmem_device_release); return 0; } @@ -647,7 +652,6 @@ static struct nvmem_device *__nvmem_device_get(struct device_node *np, } } - nvmem->users++; mutex_unlock(&nvmem_mutex); if (!try_module_get(nvmem->owner)) { @@ -655,22 +659,18 @@ static struct nvmem_device *__nvmem_device_get(struct device_node *np, "could not increase module refcount for cell %s\n", nvmem_dev_name(nvmem)); - mutex_lock(&nvmem_mutex); - nvmem->users--; - mutex_unlock(&nvmem_mutex); - return ERR_PTR(-EINVAL); } + kref_get(&nvmem->refcnt); + return nvmem; } static void __nvmem_device_put(struct nvmem_device *nvmem) { module_put(nvmem->owner); - mutex_lock(&nvmem_mutex); - nvmem->users--; - mutex_unlock(&nvmem_mutex); + kref_put(&nvmem->refcnt, nvmem_device_release); } static struct nvmem_device *nvmem_find(const char *name) -- cgit v1.2.3-58-ga151 From 424d7033add17ce897305242901e14df1ff90195 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:09 -0700 Subject: nvmem: sunxi_sid: return -ENOMEM if kzalloc() fails The driver currently returns -EINVAL if kzalloc() fails in probe(). Change it to -ENOMEM as it should be. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sunxi_sid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index d020f89248fd..4d0b7e72aa2e 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -187,7 +187,7 @@ static int sunxi_sid_probe(struct platform_device *pdev) randomness = kzalloc(size, GFP_KERNEL); if (!randomness) { - ret = -EINVAL; + ret = -ENOMEM; goto err_unreg_nvmem; } -- cgit v1.2.3-58-ga151 From 6eed8dd9a513223185b62dd725fd35db60db7217 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:10 -0700 Subject: nvmem: sunxi_sid: use devm_nvmem_register() Use the resource managed variant of nvmem_register(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sunxi_sid.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 4d0b7e72aa2e..1310f8af37f8 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -181,15 +181,13 @@ static int sunxi_sid_probe(struct platform_device *pdev) else econfig.reg_read = sunxi_sid_read; econfig.priv = sid; - nvmem = nvmem_register(&econfig); + nvmem = devm_nvmem_register(dev, &econfig); if (IS_ERR(nvmem)) return PTR_ERR(nvmem); randomness = kzalloc(size, GFP_KERNEL); - if (!randomness) { - ret = -ENOMEM; - goto err_unreg_nvmem; - } + if (!randomness) + return -ENOMEM; for (i = 0; i < size; i++) econfig.reg_read(sid, i, &randomness[i], 1); @@ -200,17 +198,6 @@ static int sunxi_sid_probe(struct platform_device *pdev) platform_set_drvdata(pdev, nvmem); return 0; - -err_unreg_nvmem: - nvmem_unregister(nvmem); - return ret; -} - -static int sunxi_sid_remove(struct platform_device *pdev) -{ - struct nvmem_device *nvmem = platform_get_drvdata(pdev); - - return nvmem_unregister(nvmem); } static const struct sunxi_sid_cfg sun4i_a10_cfg = { @@ -243,7 +230,6 @@ MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); static struct platform_driver sunxi_sid_driver = { .probe = sunxi_sid_probe, - .remove = sunxi_sid_remove, .driver = { .name = "eeprom-sunxi-sid", .of_match_table = sunxi_sid_of_match, -- cgit v1.2.3-58-ga151 From 226014d13fa5aa8d26caeebbd47d12ffd0a3c399 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:11 -0700 Subject: nvmem: lpc18xx_eeprom: use devm_nvmem_register() Use the managed version of nvmem_register(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/lpc18xx_eeprom.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c index a9534a6e8636..b42dbaddb419 100644 --- a/drivers/nvmem/lpc18xx_eeprom.c +++ b/drivers/nvmem/lpc18xx_eeprom.c @@ -236,7 +236,7 @@ static int lpc18xx_eeprom_probe(struct platform_device *pdev) lpc18xx_nvmem_config.dev = dev; lpc18xx_nvmem_config.priv = eeprom; - eeprom->nvmem = nvmem_register(&lpc18xx_nvmem_config); + eeprom->nvmem = devm_nvmem_register(dev, &lpc18xx_nvmem_config); if (IS_ERR(eeprom->nvmem)) { ret = PTR_ERR(eeprom->nvmem); goto err_clk; @@ -257,10 +257,6 @@ static int lpc18xx_eeprom_remove(struct platform_device *pdev) struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev); int ret; - ret = nvmem_unregister(eeprom->nvmem); - if (ret < 0) - return ret; - clk_disable_unprepare(eeprom->clk); return 0; -- cgit v1.2.3-58-ga151 From 7d9f9f24fac774ead58137dff13aa1f74eb5dd10 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:12 -0700 Subject: nvmem: mxs-ocotp: use devm_nvmem_register() Use the resource managed variant of nvmem_register(); Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/mxs-ocotp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/mxs-ocotp.c b/drivers/nvmem/mxs-ocotp.c index 7018e2ef5714..53122f59c4b2 100644 --- a/drivers/nvmem/mxs-ocotp.c +++ b/drivers/nvmem/mxs-ocotp.c @@ -177,7 +177,7 @@ static int mxs_ocotp_probe(struct platform_device *pdev) ocotp_config.size = data->size; ocotp_config.priv = otp; ocotp_config.dev = dev; - otp->nvmem = nvmem_register(&ocotp_config); + otp->nvmem = devm_nvmem_register(dev, &ocotp_config); if (IS_ERR(otp->nvmem)) { ret = PTR_ERR(otp->nvmem); goto err_clk; @@ -199,7 +199,7 @@ static int mxs_ocotp_remove(struct platform_device *pdev) clk_unprepare(otp->clk); - return nvmem_unregister(otp->nvmem); + return 0; } static struct platform_driver mxs_ocotp_driver = { -- cgit v1.2.3-58-ga151 From bf58e8820c48805394ec9e76339f0c4646050432 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:13 -0700 Subject: nvmem: change the signature of nvmem_unregister() We switched the nvmem framework to using kref instead of manually checking the current number of users in nvmem_unregister() so this function can no longer fail. We also converted all remaining users that still checked the return value of nvmem_unregister() to using devm_nvmem_register(). Make the routine return void. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 8 ++------ include/linux/nvmem-provider.h | 9 +++------ 2 files changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 920b56e500cc..bd14d04782cd 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -553,20 +553,16 @@ static void nvmem_device_release(struct kref *kref) * nvmem_unregister() - Unregister previously registered nvmem device * * @nvmem: Pointer to previously registered nvmem device. - * - * Return: Will be an negative on error or a zero on success. */ -int nvmem_unregister(struct nvmem_device *nvmem) +void nvmem_unregister(struct nvmem_device *nvmem) { kref_put(&nvmem->refcnt, nvmem_device_release); - - return 0; } EXPORT_SYMBOL_GPL(nvmem_unregister); static void devm_nvmem_release(struct device *dev, void *res) { - WARN_ON(nvmem_unregister(*(struct nvmem_device **)res)); + nvmem_unregister(*(struct nvmem_device **)res); } /** diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 24def6ad09bb..0f357d0c1e75 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -70,7 +70,7 @@ struct nvmem_config { #if IS_ENABLED(CONFIG_NVMEM) struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); -int nvmem_unregister(struct nvmem_device *nvmem); +void nvmem_unregister(struct nvmem_device *nvmem); struct nvmem_device *devm_nvmem_register(struct device *dev, const struct nvmem_config *cfg); @@ -87,10 +87,7 @@ static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) return ERR_PTR(-ENOSYS); } -static inline int nvmem_unregister(struct nvmem_device *nvmem) -{ - return -ENOSYS; -} +static inline void nvmem_unregister(struct nvmem_device *nvmem) {} static inline struct nvmem_device * devm_nvmem_register(struct device *dev, const struct nvmem_config *c) @@ -101,7 +98,7 @@ devm_nvmem_register(struct device *dev, const struct nvmem_config *c) static inline int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) { - return nvmem_unregister(nvmem); + return -ENOSYS; } -- cgit v1.2.3-58-ga151 From c7235ee3f4b86a01507a12e96a4875ba9b4be842 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:14 -0700 Subject: nvmem: remove the global cell list Nvmem subsystem keeps a global list of cells that, for non-DT systems, can only be referenced by cell name, which makes it impossible to have more than one nvmem device with cells named the same. This patch makes every nvmem device the owner of the list of its cells. This effectively removes the support for non-DT systems, but it will be reintroduced following a different approach in subsequent patches. This isn't a problem as support for board files in nvmem is currently broken anyway: any user that would try to get an nvmem cell from the global cell list would remove the cell after the calling nvmem_cell_put(). This can cause anything from a subsequent user not being able to get the cell to double free errors if more users hold reference to the same cell at the same time. Fortunately there are no such users which allows us to rework this part. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 64 ++++++++++++---------------------------------------- 1 file changed, 15 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index bd14d04782cd..ee794613024c 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -38,6 +38,7 @@ struct nvmem_device { int flags; struct bin_attribute eeprom; struct device *base_dev; + struct list_head cells; nvmem_reg_read_t reg_read; nvmem_reg_write_t reg_write; void *priv; @@ -58,9 +59,6 @@ struct nvmem_cell { static DEFINE_MUTEX(nvmem_mutex); static DEFINE_IDA(nvmem_ida); -static LIST_HEAD(nvmem_cells); -static DEFINE_MUTEX(nvmem_cells_mutex); - #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif @@ -282,28 +280,11 @@ static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np) return to_nvmem_device(d); } -static struct nvmem_cell *nvmem_find_cell(const char *cell_id) -{ - struct nvmem_cell *p; - - mutex_lock(&nvmem_cells_mutex); - - list_for_each_entry(p, &nvmem_cells, node) - if (!strcmp(p->name, cell_id)) { - mutex_unlock(&nvmem_cells_mutex); - return p; - } - - mutex_unlock(&nvmem_cells_mutex); - - return NULL; -} - static void nvmem_cell_drop(struct nvmem_cell *cell) { - mutex_lock(&nvmem_cells_mutex); + mutex_lock(&nvmem_mutex); list_del(&cell->node); - mutex_unlock(&nvmem_cells_mutex); + mutex_unlock(&nvmem_mutex); kfree(cell); } @@ -311,16 +292,15 @@ static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) { struct nvmem_cell *cell, *p; - list_for_each_entry_safe(cell, p, &nvmem_cells, node) - if (cell->nvmem == nvmem) - nvmem_cell_drop(cell); + list_for_each_entry_safe(cell, p, &nvmem->cells, node) + nvmem_cell_drop(cell); } static void nvmem_cell_add(struct nvmem_cell *cell) { - mutex_lock(&nvmem_cells_mutex); - list_add_tail(&cell->node, &nvmem_cells); - mutex_unlock(&nvmem_cells_mutex); + mutex_lock(&nvmem_mutex); + list_add_tail(&cell->node, &cell->nvmem->cells); + mutex_unlock(&nvmem_mutex); } static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, @@ -465,6 +445,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) } kref_init(&nvmem->refcnt); + INIT_LIST_HEAD(&nvmem->cells); nvmem->id = rval; nvmem->owner = config->owner; @@ -626,29 +607,14 @@ static struct nvmem_device *__nvmem_device_get(struct device_node *np, { struct nvmem_device *nvmem = NULL; - mutex_lock(&nvmem_mutex); - - if (np) { - nvmem = of_nvmem_find(np); - if (!nvmem) { - mutex_unlock(&nvmem_mutex); - return ERR_PTR(-EPROBE_DEFER); - } - } else { - struct nvmem_cell *cell = nvmem_find_cell(cell_id); - - if (cell) { - nvmem = cell->nvmem; - *cellp = cell; - } - - if (!nvmem) { - mutex_unlock(&nvmem_mutex); - return ERR_PTR(-ENOENT); - } - } + if (!np) + return ERR_PTR(-ENOENT); + mutex_lock(&nvmem_mutex); + nvmem = of_nvmem_find(np); mutex_unlock(&nvmem_mutex); + if (!nvmem) + return ERR_PTR(-EPROBE_DEFER); if (!try_module_get(nvmem->owner)) { dev_err(&nvmem->dev, -- cgit v1.2.3-58-ga151 From b985f4cba6dbb3b60ad119c6f7e5cbad6f0d7e45 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:15 -0700 Subject: nvmem: add support for cell info Add new structs and routines allowing users to define nvmem cells from machine code. This global list of entries is parsed when a provider is registered and cells are associated with the relevant nvmem_device struct. A possible improvement for the future is to allow users to register cell tables after the nvmem provider has been registered by updating the cell list at each call to nvmem_(add|del)_cell_table(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 72 ++++++++++++++++++++++++++++++++++++++++++ include/linux/nvmem-provider.h | 33 +++++++++++++------ 2 files changed, 96 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index ee794613024c..8e0108806e65 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -59,6 +59,9 @@ struct nvmem_cell { static DEFINE_MUTEX(nvmem_mutex); static DEFINE_IDA(nvmem_ida); +static DEFINE_MUTEX(nvmem_cell_mutex); +static LIST_HEAD(nvmem_cell_tables); + #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif @@ -416,6 +419,43 @@ static int nvmem_setup_compat(struct nvmem_device *nvmem, return 0; } +static int nvmem_add_cells_from_table(struct nvmem_device *nvmem) +{ + const struct nvmem_cell_info *info; + struct nvmem_cell_table *table; + struct nvmem_cell *cell; + int rval = 0, i; + + mutex_lock(&nvmem_cell_mutex); + list_for_each_entry(table, &nvmem_cell_tables, node) { + if (strcmp(nvmem_dev_name(nvmem), table->nvmem_name) == 0) { + for (i = 0; i < table->ncells; i++) { + info = &table->cells[i]; + + cell = kzalloc(sizeof(*cell), GFP_KERNEL); + if (!cell) { + rval = -ENOMEM; + goto out; + } + + rval = nvmem_cell_info_to_nvmem_cell(nvmem, + info, + cell); + if (rval) { + kfree(cell); + goto out; + } + + nvmem_cell_add(cell); + } + } + } + +out: + mutex_unlock(&nvmem_cell_mutex); + return rval; +} + /** * nvmem_register() - Register a nvmem device for given nvmem_config. * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem @@ -502,8 +542,14 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) goto err_teardown_compat; } + rval = nvmem_add_cells_from_table(nvmem); + if (rval) + goto err_remove_cells; + return nvmem; +err_remove_cells: + nvmem_device_remove_all_cells(nvmem); err_teardown_compat: if (config->compat) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); @@ -1306,6 +1352,32 @@ int nvmem_device_write(struct nvmem_device *nvmem, } EXPORT_SYMBOL_GPL(nvmem_device_write); +/** + * nvmem_add_cell_table() - register a table of cell info entries + * + * @table: table of cell info entries + */ +void nvmem_add_cell_table(struct nvmem_cell_table *table) +{ + mutex_lock(&nvmem_cell_mutex); + list_add_tail(&table->node, &nvmem_cell_tables); + mutex_unlock(&nvmem_cell_mutex); +} +EXPORT_SYMBOL_GPL(nvmem_add_cell_table); + +/** + * nvmem_del_cell_table() - remove a previously registered cell info table + * + * @table: table of cell info entries + */ +void nvmem_del_cell_table(struct nvmem_cell_table *table) +{ + mutex_lock(&nvmem_cell_mutex); + list_del(&table->node); + mutex_unlock(&nvmem_cell_mutex); +} +EXPORT_SYMBOL_GPL(nvmem_del_cell_table); + /** * nvmem_dev_name() - Get the name of a given nvmem device. * diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 0f357d0c1e75..5c9f205cac8f 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -67,6 +67,25 @@ struct nvmem_config { struct device *base_dev; }; +/** + * struct nvmem_cell_table - NVMEM cell definitions for given provider + * + * @nvmem_name: Provider name. + * @cells: Array of cell definitions. + * @ncells: Number of cell definitions in the array. + * @node: List node. + * + * This structure together with related helper functions is provided for users + * that don't can't access the nvmem provided structure but wish to register + * cell definitions for it e.g. board files registering an EEPROM device. + */ +struct nvmem_cell_table { + const char *nvmem_name; + const struct nvmem_cell_info *cells; + size_t ncells; + struct list_head node; +}; + #if IS_ENABLED(CONFIG_NVMEM) struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); @@ -77,9 +96,9 @@ struct nvmem_device *devm_nvmem_register(struct device *dev, int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem); -int nvmem_add_cells(struct nvmem_device *nvmem, - const struct nvmem_cell_info *info, - int ncells); +void nvmem_add_cell_table(struct nvmem_cell_table *table); +void nvmem_del_cell_table(struct nvmem_cell_table *table); + #else static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) @@ -102,12 +121,8 @@ devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) } -static inline int nvmem_add_cells(struct nvmem_device *nvmem, - const struct nvmem_cell_info *info, - int ncells) -{ - return -ENOSYS; -} +static inline void nvmem_add_cell_table(struct nvmem_cell_table *table) {} +static inline void nvmem_del_cell_table(struct nvmem_cell_table *table) {} #endif /* CONFIG_NVMEM */ #endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ -- cgit v1.2.3-58-ga151 From e888d445ac33a5b0288d670ecd970908b13f07cd Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:16 -0700 Subject: nvmem: resolve cells from DT at registration time Currently we're creating a new cell structure everytime a DT user calls nvmem_cell_get(). Change this behavior by resolving the cells during nvmem provider registration and adding all cells to the provider's list. Make of_nvmem_cell_get() just parse the phandle and look the cell up in the relevant provider's list. Don't drop the cell in nvmem_cell_put(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 123 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 75 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 8e0108806e65..74b6b97680d5 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -456,6 +456,73 @@ out: return rval; } +static struct nvmem_cell * +nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) +{ + struct nvmem_cell *cell = NULL; + int i = 0; + + mutex_lock(&nvmem_mutex); + list_for_each_entry(cell, &nvmem->cells, node) { + if (index == i++) + break; + } + mutex_unlock(&nvmem_mutex); + + return cell; +} + +static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) +{ + struct device_node *parent, *child; + struct device *dev = &nvmem->dev; + struct nvmem_cell *cell; + const __be32 *addr; + int len; + + parent = dev->of_node; + + for_each_child_of_node(parent, child) { + addr = of_get_property(child, "reg", &len); + if (!addr || (len < 2 * sizeof(u32))) { + dev_err(dev, "nvmem: invalid reg on %pOF\n", child); + return -EINVAL; + } + + cell = kzalloc(sizeof(*cell), GFP_KERNEL); + if (!cell) + return -ENOMEM; + + cell->nvmem = nvmem; + cell->offset = be32_to_cpup(addr++); + cell->bytes = be32_to_cpup(addr); + cell->name = child->name; + + addr = of_get_property(child, "bits", &len); + if (addr && len == (2 * sizeof(u32))) { + cell->bit_offset = be32_to_cpup(addr++); + cell->nbits = be32_to_cpup(addr); + } + + if (cell->nbits) + cell->bytes = DIV_ROUND_UP( + cell->nbits + cell->bit_offset, + BITS_PER_BYTE); + + if (!IS_ALIGNED(cell->offset, nvmem->stride)) { + dev_err(dev, "cell %s unaligned to nvmem stride %d\n", + cell->name, nvmem->stride); + /* Cells already added will be freed later. */ + kfree(cell); + return -EINVAL; + } + + nvmem_cell_add(cell); + } + + return 0; +} + /** * nvmem_register() - Register a nvmem device for given nvmem_config. * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem @@ -546,6 +613,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_cells; + rval = nvmem_add_cells_from_of(nvmem); + if (rval) + goto err_remove_cells; + return nvmem; err_remove_cells: @@ -848,10 +919,8 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *name) { struct device_node *cell_np, *nvmem_np; - struct nvmem_cell *cell; struct nvmem_device *nvmem; - const __be32 *addr; - int rval, len; + struct nvmem_cell *cell; int index = 0; /* if cell name exists, find index to the name */ @@ -871,54 +940,13 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, if (IS_ERR(nvmem)) return ERR_CAST(nvmem); - addr = of_get_property(cell_np, "reg", &len); - if (!addr || (len < 2 * sizeof(u32))) { - dev_err(&nvmem->dev, "nvmem: invalid reg on %pOF\n", - cell_np); - rval = -EINVAL; - goto err_mem; - } - - cell = kzalloc(sizeof(*cell), GFP_KERNEL); + cell = nvmem_find_cell_by_index(nvmem, index); if (!cell) { - rval = -ENOMEM; - goto err_mem; - } - - cell->nvmem = nvmem; - cell->offset = be32_to_cpup(addr++); - cell->bytes = be32_to_cpup(addr); - cell->name = cell_np->name; - - addr = of_get_property(cell_np, "bits", &len); - if (addr && len == (2 * sizeof(u32))) { - cell->bit_offset = be32_to_cpup(addr++); - cell->nbits = be32_to_cpup(addr); - } - - if (cell->nbits) - cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, - BITS_PER_BYTE); - - if (!IS_ALIGNED(cell->offset, nvmem->stride)) { - dev_err(&nvmem->dev, - "cell %s unaligned to nvmem stride %d\n", - cell->name, nvmem->stride); - rval = -EINVAL; - goto err_sanity; + __nvmem_device_put(nvmem); + return ERR_PTR(-ENOENT); } - nvmem_cell_add(cell); - return cell; - -err_sanity: - kfree(cell); - -err_mem: - __nvmem_device_put(nvmem); - - return ERR_PTR(rval); } EXPORT_SYMBOL_GPL(of_nvmem_cell_get); #endif @@ -1024,7 +1052,6 @@ void nvmem_cell_put(struct nvmem_cell *cell) struct nvmem_device *nvmem = cell->nvmem; __nvmem_device_put(nvmem); - nvmem_cell_drop(cell); } EXPORT_SYMBOL_GPL(nvmem_cell_put); -- cgit v1.2.3-58-ga151 From 506157be06ba28137b18b7419a4dccfa244f4983 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:17 -0700 Subject: nvmem: add support for cell lookups from machine code Add a way for machine code users to associate devices with nvmem cells. This restores the support for non-DT systems but following a different approach. Cells must now be associated with devices using provided routines and data structures before they can be retrieved using nvmem_cell_get(). It's still possible to define cells statically in nvmem_config but cells created this way still need to be associated with consumers using lookup entries. Note that nvmem_find() must be moved higher in the source file as we want to call it from __nvmem_device_get() for devices that don't have a device node. The signature of __nvmem_device_get() is also changed as it's no longer used to retrieve cells. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 129 ++++++++++++++++++++++++++++++++--------- include/linux/nvmem-consumer.h | 28 +++++++++ 2 files changed, 131 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 74b6b97680d5..9cc86d131e1e 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -62,6 +62,9 @@ static DEFINE_IDA(nvmem_ida); static DEFINE_MUTEX(nvmem_cell_mutex); static LIST_HEAD(nvmem_cell_tables); +static DEFINE_MUTEX(nvmem_lookup_mutex); +static LIST_HEAD(nvmem_lookup_list); + #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif @@ -283,6 +286,18 @@ static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np) return to_nvmem_device(d); } +static struct nvmem_device *nvmem_find(const char *name) +{ + struct device *d; + + d = bus_find_device_by_name(&nvmem_bus_type, NULL, name); + + if (!d) + return NULL; + + return to_nvmem_device(d); +} + static void nvmem_cell_drop(struct nvmem_cell *cell) { mutex_lock(&nvmem_mutex); @@ -472,6 +487,21 @@ nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) return cell; } +static struct nvmem_cell * +nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id) +{ + struct nvmem_cell *cell = NULL; + + mutex_lock(&nvmem_mutex); + list_for_each_entry(cell, &nvmem->cells, node) { + if (strcmp(cell_id, cell->name) == 0) + break; + } + mutex_unlock(&nvmem_mutex); + + return cell; +} + static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) { struct device_node *parent, *child; @@ -719,16 +749,12 @@ int devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem) EXPORT_SYMBOL(devm_nvmem_unregister); static struct nvmem_device *__nvmem_device_get(struct device_node *np, - struct nvmem_cell **cellp, - const char *cell_id) + const char *nvmem_name) { struct nvmem_device *nvmem = NULL; - if (!np) - return ERR_PTR(-ENOENT); - mutex_lock(&nvmem_mutex); - nvmem = of_nvmem_find(np); + nvmem = np ? of_nvmem_find(np) : nvmem_find(nvmem_name); mutex_unlock(&nvmem_mutex); if (!nvmem) return ERR_PTR(-EPROBE_DEFER); @@ -752,18 +778,6 @@ static void __nvmem_device_put(struct nvmem_device *nvmem) kref_put(&nvmem->refcnt, nvmem_device_release); } -static struct nvmem_device *nvmem_find(const char *name) -{ - struct device *d; - - d = bus_find_device_by_name(&nvmem_bus_type, NULL, name); - - if (!d) - return ERR_PTR(-ENOENT); - - return to_nvmem_device(d); -} - #if IS_ENABLED(CONFIG_OF) /** * of_nvmem_device_get() - Get nvmem device from a given id @@ -786,7 +800,7 @@ struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) if (!nvmem_np) return ERR_PTR(-EINVAL); - return __nvmem_device_get(nvmem_np, NULL, NULL); + return __nvmem_device_get(nvmem_np, NULL); } EXPORT_SYMBOL_GPL(of_nvmem_device_get); #endif @@ -890,15 +904,43 @@ struct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(devm_nvmem_device_get); -static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) +static struct nvmem_cell * +nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) { - struct nvmem_cell *cell = NULL; + struct nvmem_cell *cell = ERR_PTR(-ENOENT); + struct nvmem_cell_lookup *lookup; struct nvmem_device *nvmem; + const char *dev_id; - nvmem = __nvmem_device_get(NULL, &cell, cell_id); - if (IS_ERR(nvmem)) - return ERR_CAST(nvmem); + if (!dev) + return ERR_PTR(-EINVAL); + + dev_id = dev_name(dev); + + mutex_lock(&nvmem_lookup_mutex); + + list_for_each_entry(lookup, &nvmem_lookup_list, node) { + if ((strcmp(lookup->dev_id, dev_id) == 0) && + (strcmp(lookup->con_id, con_id) == 0)) { + /* This is the right entry. */ + nvmem = __nvmem_device_get(NULL, lookup->nvmem_name); + if (!nvmem) { + /* Provider may not be registered yet. */ + cell = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + cell = nvmem_find_cell_by_name(nvmem, + lookup->cell_name); + if (!cell) { + __nvmem_device_put(nvmem); + goto out; + } + } + } +out: + mutex_unlock(&nvmem_lookup_mutex); return cell; } @@ -935,7 +977,7 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, if (!nvmem_np) return ERR_PTR(-EINVAL); - nvmem = __nvmem_device_get(nvmem_np, NULL, NULL); + nvmem = __nvmem_device_get(nvmem_np, NULL); of_node_put(nvmem_np); if (IS_ERR(nvmem)) return ERR_CAST(nvmem); @@ -975,7 +1017,7 @@ struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *cell_id) if (!cell_id) return ERR_PTR(-EINVAL); - return nvmem_cell_get_from_list(cell_id); + return nvmem_cell_get_from_lookup(dev, cell_id); } EXPORT_SYMBOL_GPL(nvmem_cell_get); @@ -1405,6 +1447,41 @@ void nvmem_del_cell_table(struct nvmem_cell_table *table) } EXPORT_SYMBOL_GPL(nvmem_del_cell_table); +/** + * nvmem_add_cell_lookups() - register a list of cell lookup entries + * + * @entries: array of cell lookup entries + * @nentries: number of cell lookup entries in the array + */ +void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) +{ + int i; + + mutex_lock(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) + list_add_tail(&entries[i].node, &nvmem_lookup_list); + mutex_unlock(&nvmem_lookup_mutex); +} +EXPORT_SYMBOL_GPL(nvmem_add_cell_lookups); + +/** + * nvmem_del_cell_lookups() - remove a list of previously added cell lookup + * entries + * + * @entries: array of cell lookup entries + * @nentries: number of cell lookup entries in the array + */ +void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) +{ + int i; + + mutex_lock(&nvmem_lookup_mutex); + for (i = 0; i < nentries; i++) + list_del(&entries[i].node); + mutex_unlock(&nvmem_lookup_mutex); +} +EXPORT_SYMBOL_GPL(nvmem_del_cell_lookups); + /** * nvmem_dev_name() - Get the name of a given nvmem device. * diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 0389fe00b177..27eee3945405 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -29,6 +29,24 @@ struct nvmem_cell_info { unsigned int nbits; }; +/** + * struct nvmem_cell_lookup - cell lookup entry + * + * @nvmem_name: Name of the provider. + * @cell_name: Name of the nvmem cell as defined in the name field of + * struct nvmem_cell_info. + * @dev_id: Name of the consumer device that will be associated with + * this cell. + * @con_id: Connector id for this cell lookup. + */ +struct nvmem_cell_lookup { + const char *nvmem_name; + const char *cell_name; + const char *dev_id; + const char *con_id; + struct list_head node; +}; + #if IS_ENABLED(CONFIG_NVMEM) /* Cell based interface */ @@ -57,6 +75,11 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem, const char *nvmem_dev_name(struct nvmem_device *nvmem); +void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, + size_t nentries); +void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, + size_t nentries); + #else static inline struct nvmem_cell *nvmem_cell_get(struct device *dev, @@ -151,6 +174,11 @@ static inline const char *nvmem_dev_name(struct nvmem_device *nvmem) return NULL; } +static inline void +nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {} +static inline void +nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {} + #endif /* CONFIG_NVMEM */ #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) -- cgit v1.2.3-58-ga151 From bee1138bea15a640aaa9e9bb909af5b2762520e0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:19 -0700 Subject: nvmem: add a notifier chain Add a blocking notifier chain with four events (add and remove for both devices and cells) so that users can get notified about the addition of nvmem resources they're waiting for. We'll use this instead of the at24 setup callback in the mityomapl138 board file. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/nvmem-consumer.h | 21 +++++++++++++++++++++ 2 files changed, 57 insertions(+) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 9cc86d131e1e..da441019b609 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -65,6 +65,8 @@ static LIST_HEAD(nvmem_cell_tables); static DEFINE_MUTEX(nvmem_lookup_mutex); static LIST_HEAD(nvmem_lookup_list); +static BLOCKING_NOTIFIER_HEAD(nvmem_notifier); + #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif @@ -300,6 +302,7 @@ static struct nvmem_device *nvmem_find(const char *name) static void nvmem_cell_drop(struct nvmem_cell *cell) { + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell); mutex_lock(&nvmem_mutex); list_del(&cell->node); mutex_unlock(&nvmem_mutex); @@ -319,6 +322,7 @@ static void nvmem_cell_add(struct nvmem_cell *cell) mutex_lock(&nvmem_mutex); list_add_tail(&cell->node, &cell->nvmem->cells); mutex_unlock(&nvmem_mutex); + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell); } static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, @@ -434,6 +438,32 @@ static int nvmem_setup_compat(struct nvmem_device *nvmem, return 0; } +/** + * nvmem_register_notifier() - Register a notifier block for nvmem events. + * + * @nb: notifier block to be called on nvmem events. + * + * Return: 0 on success, negative error number on failure. + */ +int nvmem_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&nvmem_notifier, nb); +} +EXPORT_SYMBOL_GPL(nvmem_register_notifier); + +/** + * nvmem_unregister_notifier() - Unregister a notifier block for nvmem events. + * + * @nb: notifier block to be unregistered. + * + * Return: 0 on success, negative error number on failure. + */ +int nvmem_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&nvmem_notifier, nb); +} +EXPORT_SYMBOL_GPL(nvmem_unregister_notifier); + static int nvmem_add_cells_from_table(struct nvmem_device *nvmem) { const struct nvmem_cell_info *info; @@ -647,6 +677,10 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) if (rval) goto err_remove_cells; + rval = blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem); + if (rval) + goto err_remove_cells; + return nvmem; err_remove_cells: @@ -669,6 +703,8 @@ static void nvmem_device_release(struct kref *kref) nvmem = container_of(kref, struct nvmem_device, refcnt); + blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem); + if (nvmem->flags & FLAG_COMPAT) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 27eee3945405..0326b52e906b 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -14,6 +14,7 @@ #include #include +#include struct device; struct device_node; @@ -47,6 +48,13 @@ struct nvmem_cell_lookup { struct list_head node; }; +enum { + NVMEM_ADD = 1, + NVMEM_REMOVE, + NVMEM_CELL_ADD, + NVMEM_CELL_REMOVE, +}; + #if IS_ENABLED(CONFIG_NVMEM) /* Cell based interface */ @@ -80,6 +88,9 @@ void nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries); +int nvmem_register_notifier(struct notifier_block *nb); +int nvmem_unregister_notifier(struct notifier_block *nb); + #else static inline struct nvmem_cell *nvmem_cell_get(struct device *dev, @@ -179,6 +190,16 @@ nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {} static inline void nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries) {} +static inline int nvmem_register_notifier(struct notifier_block *nb) +{ + return -ENOSYS; +} + +static inline int nvmem_unregister_notifier(struct notifier_block *nb) +{ + return -ENOSYS; +} + #endif /* CONFIG_NVMEM */ #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) -- cgit v1.2.3-58-ga151 From b1c1db9883c276fe41a04e9ce8f89576a64b1ac0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:20 -0700 Subject: nvmem: use SPDX license identifiers Use SPDX license identiefiers to core nvmem files and remove GPL 2.0 license boilerplate. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 10 +--------- include/linux/nvmem-consumer.h | 5 +---- include/linux/nvmem-provider.h | 5 +---- 3 files changed, 3 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index da441019b609..ea05219e60b4 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * nvmem framework core. * * Copyright (C) 2015 Srinivas Kandagatla * Copyright (C) 2013 Maxime Ripard - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index 0326b52e906b..d18caae2f7ac 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * nvmem framework consumer. * * Copyright (C) 2015 Srinivas Kandagatla * Copyright (C) 2013 Maxime Ripard - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef _LINUX_NVMEM_CONSUMER_H diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 5c9f205cac8f..8ae012f6545a 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * nvmem framework provider. * * Copyright (C) 2015 Srinivas Kandagatla * Copyright (C) 2013 Maxime Ripard - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef _LINUX_NVMEM_PROVIDER_H -- cgit v1.2.3-58-ga151 From 165589f0cb52b34db12e15676a034b8f83dfa756 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:21 -0700 Subject: nvmem: make the naming of arguments in nvmem_cell_get() consistent The argument representing the cell name in the nvmem_cell_get() family of functions is not consistend between function prototypes and definitions. Name it 'id' in all those routines. This is in line with other frameworks and can represent both the DT cell name from the nvmem-cell-names property as well as the con_id field from cell lookup entries. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 27 ++++++++++++++------------- include/linux/nvmem-consumer.h | 12 ++++++------ 2 files changed, 20 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index ea05219e60b4..ff21402fbd7d 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -977,16 +977,15 @@ out: * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * * @np: Device tree node that uses the nvmem cell. - * @name: nvmem cell name from nvmem-cell-names property, or NULL - * for the cell at index 0 (the lone cell with no accompanying - * nvmem-cell-names property). + * @id: nvmem cell name from nvmem-cell-names property, or NULL + * for the cell at index 0 (the lone cell with no accompanying + * nvmem-cell-names property). * * Return: Will be an ERR_PTR() on error or a valid pointer * to a struct nvmem_cell. The nvmem_cell will be freed by the * nvmem_cell_put(). */ -struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, - const char *name) +struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id) { struct device_node *cell_np, *nvmem_np; struct nvmem_device *nvmem; @@ -994,8 +993,8 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, int index = 0; /* if cell name exists, find index to the name */ - if (name) - index = of_property_match_string(np, "nvmem-cell-names", name); + if (id) + index = of_property_match_string(np, "nvmem-cell-names", id); cell_np = of_parse_phandle(np, "nvmem-cells", index); if (!cell_np) @@ -1025,27 +1024,29 @@ EXPORT_SYMBOL_GPL(of_nvmem_cell_get); * nvmem_cell_get() - Get nvmem cell of device form a given cell name * * @dev: Device that requests the nvmem cell. - * @cell_id: nvmem cell name to get. + * @id: nvmem cell name to get (this corresponds with the name from the + * nvmem-cell-names property for DT systems and with the con_id from + * the lookup entry for non-DT systems). * * Return: Will be an ERR_PTR() on error or a valid pointer * to a struct nvmem_cell. The nvmem_cell will be freed by the * nvmem_cell_put(). */ -struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *cell_id) +struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id) { struct nvmem_cell *cell; if (dev->of_node) { /* try dt first */ - cell = of_nvmem_cell_get(dev->of_node, cell_id); + cell = of_nvmem_cell_get(dev->of_node, id); if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER) return cell; } - /* NULL cell_id only allowed for device tree; invalid otherwise */ - if (!cell_id) + /* NULL cell id only allowed for device tree; invalid otherwise */ + if (!id) return ERR_PTR(-EINVAL); - return nvmem_cell_get_from_lookup(dev, cell_id); + return nvmem_cell_get_from_lookup(dev, id); } EXPORT_SYMBOL_GPL(nvmem_cell_get); diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h index d18caae2f7ac..e17617fa034f 100644 --- a/include/linux/nvmem-consumer.h +++ b/include/linux/nvmem-consumer.h @@ -55,8 +55,8 @@ enum { #if IS_ENABLED(CONFIG_NVMEM) /* Cell based interface */ -struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name); -struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name); +struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id); +struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id); void nvmem_cell_put(struct nvmem_cell *cell); void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); @@ -91,13 +91,13 @@ int nvmem_unregister_notifier(struct notifier_block *nb); #else static inline struct nvmem_cell *nvmem_cell_get(struct device *dev, - const char *name) + const char *id) { return ERR_PTR(-ENOSYS); } static inline struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, - const char *name) + const char *id) { return ERR_PTR(-ENOSYS); } @@ -201,12 +201,12 @@ static inline int nvmem_unregister_notifier(struct notifier_block *nb) #if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, - const char *name); + const char *id); struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *name); #else static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, - const char *name) + const char *id) { return ERR_PTR(-ENOSYS); } -- cgit v1.2.3-58-ga151 From 48f63a2c108a7d88562229f14124e464c6a40cb4 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:23 -0700 Subject: nvmem: fix commenting style Remove a redundant '*/' as pointed out by checkpatch. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index ff21402fbd7d..e44a1860a2b3 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -1374,7 +1374,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_cell_read); * @buf: buffer to be written to cell. * * Return: length of bytes written or negative error code on failure. - * */ + */ int nvmem_device_cell_write(struct nvmem_device *nvmem, struct nvmem_cell_info *info, void *buf) { @@ -1430,7 +1430,7 @@ EXPORT_SYMBOL_GPL(nvmem_device_read); * @buf: buffer to be written. * * Return: length of bytes written or negative error code on failure. - * */ + */ int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, size_t bytes, void *buf) -- cgit v1.2.3-58-ga151 From e7e07f4f7b452c8c2a113db28902e41c9daca855 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 21 Sep 2018 06:40:24 -0700 Subject: nvmem: use octal permissions instead of constants Checkpatch emits a warning when using symbolic permissions. Use octal permissions instead. Signed-off-by: Bartosz Golaszewski Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index e44a1860a2b3..ad1227df1984 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -154,7 +154,7 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, static struct bin_attribute bin_attr_rw_nvmem = { .attr = { .name = "nvmem", - .mode = S_IWUSR | S_IRUGO, + .mode = 0644, }, .read = bin_attr_nvmem_read, .write = bin_attr_nvmem_write, @@ -178,7 +178,7 @@ static const struct attribute_group *nvmem_rw_dev_groups[] = { static struct bin_attribute bin_attr_ro_nvmem = { .attr = { .name = "nvmem", - .mode = S_IRUGO, + .mode = 0444, }, .read = bin_attr_nvmem_read, }; @@ -201,7 +201,7 @@ static const struct attribute_group *nvmem_ro_dev_groups[] = { static struct bin_attribute bin_attr_rw_root_nvmem = { .attr = { .name = "nvmem", - .mode = S_IWUSR | S_IRUSR, + .mode = 0600, }, .read = bin_attr_nvmem_read, .write = bin_attr_nvmem_write, @@ -225,7 +225,7 @@ static const struct attribute_group *nvmem_rw_root_dev_groups[] = { static struct bin_attribute bin_attr_ro_root_nvmem = { .attr = { .name = "nvmem", - .mode = S_IRUSR, + .mode = 0400, }, .read = bin_attr_nvmem_read, }; -- cgit v1.2.3-58-ga151 From 4876bfe6cdecc5037fed6959425fe0cb2af19d98 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 21 Sep 2018 06:40:25 -0700 Subject: nvmem: sunxi_sid: remove unused variable in probe This patch fixes below warning: sunxi_sid.c:157:6: warning: unused variable 'ret' [-Wunused-variable] Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/sunxi_sid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c index 1310f8af37f8..570a2e354f30 100644 --- a/drivers/nvmem/sunxi_sid.c +++ b/drivers/nvmem/sunxi_sid.c @@ -154,7 +154,7 @@ static int sunxi_sid_probe(struct platform_device *pdev) struct resource *res; struct nvmem_device *nvmem; struct sunxi_sid *sid; - int ret, i, size; + int i, size; char *randomness; const struct sunxi_sid_cfg *cfg; -- cgit v1.2.3-58-ga151 From ef92ab3001c4656d3b9a7c4a8044b5bc08f6f6ef Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 21 Sep 2018 06:40:26 -0700 Subject: nvmem: make nvmem_add_cells() static Now we have new api nvmem_add/del_cell_table() we do not want users to use nvmem_add_cells() anymore. So mark it accordingly. I guess it was missed in original cleanup patch. This also fixes below warning: core.c:355:5: warning: no previous prototype for 'nvmem_add_cells' [-Wmissing-prototypes] Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index ad1227df1984..cc815bb2eebd 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -352,7 +352,7 @@ static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, * * Return: 0 or negative error code on failure. */ -int nvmem_add_cells(struct nvmem_device *nvmem, +static int nvmem_add_cells(struct nvmem_device *nvmem, const struct nvmem_cell_info *info, int ncells) { @@ -391,7 +391,6 @@ err: return rval; } -EXPORT_SYMBOL_GPL(nvmem_add_cells); /* * nvmem_setup_compat() - Create an additional binary entry in -- cgit v1.2.3-58-ga151 From 11924ba5e671d6caef1516923e2bd8c72929a3fe Mon Sep 17 00:00:00 2001 From: Jorgen Hansen Date: Fri, 21 Sep 2018 00:31:05 -0700 Subject: VMCI: Resource wildcard match fixed When adding a VMCI resource, the check for an existing entry would ignore that the new entry could be a wildcard. This could result in multiple resource entries that would match a given handle. One disastrous outcome of this is that the refcounting used to ensure that delayed callbacks for VMCI datagrams have run before the datagram is destroyed can be wrong, since the refcount could be increased on the duplicate entry. This in turn leads to a use after free bug. This issue was discovered by Hangbin Liu using KASAN and syzkaller. Fixes: bc63dedb7d46 ("VMCI: resource object implementation") Reported-by: Hangbin Liu Reviewed-by: Adit Ranadive Reviewed-by: Vishnu Dasa Signed-off-by: Jorgen Hansen Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_driver.c | 2 +- drivers/misc/vmw_vmci/vmci_resource.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_vmci/vmci_driver.c b/drivers/misc/vmw_vmci/vmci_driver.c index d7eaf1eb11e7..003bfba40758 100644 --- a/drivers/misc/vmw_vmci/vmci_driver.c +++ b/drivers/misc/vmw_vmci/vmci_driver.c @@ -113,5 +113,5 @@ module_exit(vmci_drv_exit); MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Virtual Machine Communication Interface."); -MODULE_VERSION("1.1.5.0-k"); +MODULE_VERSION("1.1.6.0-k"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/vmw_vmci/vmci_resource.c b/drivers/misc/vmw_vmci/vmci_resource.c index 1ab6e8737a5f..da1ee2e1ba99 100644 --- a/drivers/misc/vmw_vmci/vmci_resource.c +++ b/drivers/misc/vmw_vmci/vmci_resource.c @@ -57,7 +57,8 @@ static struct vmci_resource *vmci_resource_lookup(struct vmci_handle handle, if (r->type == type && rid == handle.resource && - (cid == handle.context || cid == VMCI_INVALID_ID)) { + (cid == handle.context || cid == VMCI_INVALID_ID || + handle.context == VMCI_INVALID_ID)) { resource = r; break; } -- cgit v1.2.3-58-ga151 From a007734618fee1bf35556c04fa498d41d42c7301 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sat, 22 Sep 2018 21:20:54 +0200 Subject: w1: omap-hdq: fix missing bus unregister at removal The bus master was not removed after unloading the module or unbinding the driver. That lead to oopses like this [ 127.842987] Unable to handle kernel paging request at virtual address bf01d04c [ 127.850646] pgd = 70e3cd9a [ 127.853698] [bf01d04c] *pgd=8f908811, *pte=00000000, *ppte=00000000 [ 127.860412] Internal error: Oops: 80000007 [#1] PREEMPT SMP ARM [ 127.866668] Modules linked in: bq27xxx_battery overlay [last unloaded: omap_hdq] [ 127.874542] CPU: 0 PID: 1022 Comm: w1_bus_master1 Not tainted 4.19.0-rc4-00001-g2d51da718324 #12 [ 127.883819] Hardware name: Generic OMAP36xx (Flattened Device Tree) [ 127.890441] PC is at 0xbf01d04c [ 127.893798] LR is at w1_search_process_cb+0x4c/0xfc [ 127.898956] pc : [] lr : [] psr: a0070013 [ 127.905609] sp : cf885f48 ip : bf01d04c fp : ddf1e11c [ 127.911132] r10: cf8fe040 r9 : c05f8d00 r8 : cf8fe040 [ 127.916656] r7 : 000000f0 r6 : cf8fe02c r5 : cf8fe000 r4 : cf8fe01c [ 127.923553] r3 : c05f8d00 r2 : 000000f0 r1 : cf8fe000 r0 : dde1ef10 [ 127.930450] Flags: NzCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment none [ 127.938018] Control: 10c5387d Table: 8f8f0019 DAC: 00000051 [ 127.944091] Process w1_bus_master1 (pid: 1022, stack limit = 0x9135699f) [ 127.951171] Stack: (0xcf885f48 to 0xcf886000) [ 127.955810] 5f40: cf8fe000 00000000 cf884000 cf8fe090 000003e8 c05f8d00 [ 127.964477] 5f60: dde5fc34 c05f9700 ddf1e100 ddf1e540 cf884000 cf8fe000 c05f9694 00000000 [ 127.973114] 5f80: dde5fc34 c01499a4 00000000 ddf1e540 c0149874 00000000 00000000 00000000 [ 127.981781] 5fa0: 00000000 00000000 00000000 c01010e8 00000000 00000000 00000000 00000000 [ 127.990447] 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 127.999114] 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000 [ 128.007781] [] (w1_search_process_cb) from [] (w1_process+0x6c/0x118) [ 128.016479] [] (w1_process) from [] (kthread+0x130/0x148) [ 128.024047] [] (kthread) from [] (ret_from_fork+0x14/0x2c) [ 128.031677] Exception stack(0xcf885fb0 to 0xcf885ff8) [ 128.037017] 5fa0: 00000000 00000000 00000000 00000000 [ 128.045684] 5fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 [ 128.054351] 5fe0: 00000000 00000000 00000000 00000000 00000013 00000000 [ 128.061340] Code: bad PC value [ 128.064697] ---[ end trace af066e33c0e14119 ]--- Cc: Signed-off-by: Andreas Kemnade Signed-off-by: Greg Kroah-Hartman --- drivers/w1/masters/omap_hdq.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 83fc9aab34e8..3099052e1243 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -763,6 +763,8 @@ static int omap_hdq_remove(struct platform_device *pdev) /* remove module dependency */ pm_runtime_disable(&pdev->dev); + w1_remove_master_device(&omap_w1_master); + return 0; } -- cgit v1.2.3-58-ga151 From fe857bb40fb6788afe6b12b1df50b94e2d363c89 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Sun, 23 Sep 2018 21:10:41 +0000 Subject: Drivers: hv: vmbus: Fix the descriptions of some function parameters No functional change. Added descriptions for some parameters. Fixed some typos. Removed some out-of-date comments. Signed-off-by: Dexuan Cui Cc: Jonathan Corbet Cc: linux-doc@vger.kernel.org Cc: K. Y. Srinivasan Cc: Haiyang Zhang Cc: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel.c | 24 +++++++++++++----------- drivers/hv/channel_mgmt.c | 23 +++++++++-------------- 2 files changed, 22 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index ddadb7efd1cc..de8193f3b838 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -743,15 +743,16 @@ EXPORT_SYMBOL_GPL(vmbus_close); /** * vmbus_sendpacket() - Send the specified buffer on the given channel - * @channel: Pointer to vmbus_channel structure. - * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold + * @channel: Pointer to vmbus_channel structure + * @buffer: Pointer to the buffer you want to send the data from. + * @bufferlen: Maximum size of what the buffer holds. * @requestid: Identifier of the request - * @type: Type of packet that is being send e.g. negotiate, time - * packet etc. + * @type: Type of packet that is being sent e.g. negotiate, time + * packet etc. + * @flags: 0 or VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED * - * Sends data in @buffer directly to hyper-v via the vmbus - * This will send the data unparsed to hyper-v. + * Sends data in @buffer directly to Hyper-V via the vmbus. + * This will send the data unparsed to Hyper-V. * * Mainly used by Hyper-V drivers. */ @@ -884,12 +885,13 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel, EXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc); /** - * vmbus_recvpacket() - Retrieve the user packet on the specified channel - * @channel: Pointer to vmbus_channel structure. + * __vmbus_recvpacket() - Retrieve the user packet on the specified channel + * @channel: Pointer to vmbus_channel structure * @buffer: Pointer to the buffer you want to receive the data into. - * @bufferlen: Maximum size of what the the buffer will hold - * @buffer_actual_len: The actual size of the data after it was received + * @bufferlen: Maximum size of what the buffer can hold. + * @buffer_actual_len: The actual size of the data after it was received. * @requestid: Identifier of the request + * @raw: true means keep the vmpacket_descriptor header in the received data. * * Receives directly from the hyper-v vmbus and puts the data it received * into Buffer. This will receive the data unparsed from hyper-v. diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b7c48ebdf6a1..6f3e6af5e891 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -198,24 +198,19 @@ static u16 hv_get_dev_type(const struct vmbus_channel *channel) } /** - * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message + * vmbus_prep_negotiate_resp() - Create default response for Negotiate message * @icmsghdrp: Pointer to msg header structure - * @icmsg_negotiate: Pointer to negotiate message structure * @buf: Raw buffer channel data + * @fw_version: The framework versions we can support. + * @fw_vercnt: The size of @fw_version. + * @srv_version: The service versions we can support. + * @srv_vercnt: The size of @srv_version. + * @nego_fw_version: The selected framework version. + * @nego_srv_version: The selected service version. * - * @icmsghdrp is of type &struct icmsg_hdr. - * Set up and fill in default negotiate response message. - * - * The fw_version and fw_vercnt specifies the framework version that - * we can support. - * - * The srv_version and srv_vercnt specifies the service - * versions we can support. - * - * Versions are given in decreasing order. - * - * nego_fw_version and nego_srv_version store the selected protocol versions. + * Note: Versions are given in decreasing order. * + * Set up and fill in default negotiate response message. * Mainly used by Hyper-V drivers. */ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, -- cgit v1.2.3-58-ga151 From d544c22d6951be3386ac59bb9a99c9bc566b3f09 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Sun, 23 Sep 2018 21:10:42 +0000 Subject: Drivers: hv: kvp: Fix the indentation of some "break" statements No functional change. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Cc: Haiyang Zhang Cc: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 5eed1e7da15c..023bd185d21a 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -421,7 +421,7 @@ kvp_send_key(struct work_struct *dummy) UTF16_LITTLE_ENDIAN, message->body.kvp_set.data.value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE - 1) + 1; - break; + break; case REG_U32: /* @@ -454,7 +454,7 @@ kvp_send_key(struct work_struct *dummy) UTF16_LITTLE_ENDIAN, message->body.kvp_set.data.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; - break; + break; case KVP_OP_DELETE: message->body.kvp_delete.key_size = @@ -464,12 +464,12 @@ kvp_send_key(struct work_struct *dummy) UTF16_LITTLE_ENDIAN, message->body.kvp_delete.key, HV_KVP_EXCHANGE_MAX_KEY_SIZE - 1) + 1; - break; + break; case KVP_OP_ENUMERATE: message->body.kvp_enum_data.index = in_msg->body.kvp_enum_data.index; - break; + break; } kvp_transaction.state = HVUTIL_USERSPACE_REQ; -- cgit v1.2.3-58-ga151 From fc62c3b1977d62e6374fd6e28d371bb42dfa5c9d Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Sun, 23 Sep 2018 21:10:43 +0000 Subject: Drivers: hv: kvp: Fix two "this statement may fall through" warnings We don't need to call process_ib_ipinfo() if message->kvp_hdr.operation is KVP_OP_GET_IP_INFO in kvp_send_key(), because here we just need to pass on the op code from the host to the userspace; when the userspace returns the info requested by the host, we pass the info on to the host in kvp_respond_to_host() -> process_ob_ipinfo(). BTW, the current buggy code actually doesn't cause any harm, because only message->kvp_hdr.operation is used by the userspace, in the case of KVP_OP_GET_IP_INFO. The patch also adds a missing "break;" in kvp_send_key(). BTW, the current buggy code actually doesn't cause any harm, because in the case of KVP_OP_SET, the unexpected fall-through corrupts message->body.kvp_set.data.key_size, but that is not really used: see the definition of struct hv_kvp_exchg_msg_value. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Cc: Haiyang Zhang Cc: Stephen Hemminger Cc: Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_kvp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 023bd185d21a..a7513a8a8e37 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -353,7 +353,6 @@ static void process_ib_ipinfo(void *in_msg, void *out_msg, int op) out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled; - default: utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id, MAX_ADAPTER_ID_SIZE, UTF16_LITTLE_ENDIAN, @@ -406,7 +405,7 @@ kvp_send_key(struct work_struct *dummy) process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO); break; case KVP_OP_GET_IP_INFO: - process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO); + /* We only need to pass on message->kvp_hdr.operation. */ break; case KVP_OP_SET: switch (in_msg->body.kvp_set.data.value_type) { @@ -446,6 +445,9 @@ kvp_send_key(struct work_struct *dummy) break; } + + break; + case KVP_OP_GET: message->body.kvp_set.data.key_size = utf16s_to_utf8s( -- cgit v1.2.3-58-ga151 From 25355252607ca288f329ee033f387764883393f6 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Sun, 23 Sep 2018 21:10:44 +0000 Subject: Drivers: hv: vmbus: Use cpumask_var_t for on-stack cpu mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A cpumask structure on the stack can cause a warning with CONFIG_NR_CPUS=8192 (e.g. Ubuntu 16.04 and 18.04 use this): drivers/hv//channel_mgmt.c: In function ‘init_vp_index’: drivers/hv//channel_mgmt.c:702:1: warning: the frame size of 1032 bytes is larger than 1024 bytes [-Wframe-larger-than=] Nowadays it looks most distros enable CONFIG_CPUMASK_OFFSTACK=y, and hence we can work around the warning by using cpumask_var_t. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Cc: Haiyang Zhang Cc: Stephen Hemminger Cc: Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- drivers/hv/channel_mgmt.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 6f3e6af5e891..6277597d3d58 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -594,16 +594,18 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) bool perf_chn = vmbus_devs[dev_type].perf_device; struct vmbus_channel *primary = channel->primary_channel; int next_node; - struct cpumask available_mask; + cpumask_var_t available_mask; struct cpumask *alloced_mask; if ((vmbus_proto_version == VERSION_WS2008) || - (vmbus_proto_version == VERSION_WIN7) || (!perf_chn)) { + (vmbus_proto_version == VERSION_WIN7) || (!perf_chn) || + !alloc_cpumask_var(&available_mask, GFP_KERNEL)) { /* * Prior to win8, all channel interrupts are * delivered on cpu 0. * Also if the channel is not a performance critical * channel, bind it to cpu 0. + * In case alloc_cpumask_var() fails, bind it to cpu 0. */ channel->numa_node = 0; channel->target_cpu = 0; @@ -641,7 +643,7 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) cpumask_clear(alloced_mask); } - cpumask_xor(&available_mask, alloced_mask, + cpumask_xor(available_mask, alloced_mask, cpumask_of_node(primary->numa_node)); cur_cpu = -1; @@ -659,10 +661,10 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) } while (true) { - cur_cpu = cpumask_next(cur_cpu, &available_mask); + cur_cpu = cpumask_next(cur_cpu, available_mask); if (cur_cpu >= nr_cpu_ids) { cur_cpu = -1; - cpumask_copy(&available_mask, + cpumask_copy(available_mask, cpumask_of_node(primary->numa_node)); continue; } @@ -692,6 +694,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) channel->target_cpu = cur_cpu; channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu); + + free_cpumask_var(available_mask); } static void vmbus_wait_for_unload(void) -- cgit v1.2.3-58-ga151 From 6011002c1584d29c317e0895b9667d57f254537a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Sep 2018 15:05:20 +0000 Subject: uio: make symbol 'uio_class_registered' static Fixes the following sparse warning: drivers/uio/uio.c:277:6: warning: symbol 'uio_class_registered' was not declared. Should it be static? Fixes: ae61cf5b9913 ("uio: ensure class is registered before devices") Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index e601bd3fbae1..85644669fbe7 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -274,7 +274,7 @@ static struct class uio_class = { .dev_groups = uio_groups, }; -bool uio_class_registered; +static bool uio_class_registered; /* * device functions -- cgit v1.2.3-58-ga151 From 03b2cbb6ea3c73e08fcf72d9ef8e286c4dcbd1fe Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Fri, 28 Sep 2018 23:27:48 +0300 Subject: mei: replace POLL* with EPOLL* for write queues. Looks like during merging the bulk POLL* -> EPOLL* replacement missed the patch 'commit af336cabe083 ("mei: limit the number of queued writes")' Fix sparse warning: drivers/misc/mei/main.c:602:13: warning: restricted __poll_t degrades to integer drivers/misc/mei/main.c:605:30: warning: invalid assignment: |= drivers/misc/mei/main.c:605:30: left side has type restricted __poll_t drivers/misc/mei/main.c:605:30: right side has type int Fixes: af336cabe083 ("mei: limit the number of queued writes") Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 4d77a6ae183a..87281b3695e6 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -599,10 +599,10 @@ static __poll_t mei_poll(struct file *file, poll_table *wait) mei_cl_read_start(cl, mei_cl_mtu(cl), file); } - if (req_events & (POLLOUT | POLLWRNORM)) { + if (req_events & (EPOLLOUT | EPOLLWRNORM)) { poll_wait(file, &cl->tx_wait, wait); if (cl->tx_cb_queued < dev->tx_queue_limit) - mask |= POLLOUT | POLLWRNORM; + mask |= EPOLLOUT | EPOLLWRNORM; } out: -- cgit v1.2.3-58-ga151 From 0bb5a1a28ee644798dc3ca167cf9e5ac2058863e Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Mon, 1 Oct 2018 12:31:18 +0300 Subject: thunderbolt: Remove a meaningless NULL pointer check before dma_pool_destroy dma_pool_destroy() already takes NULL pointer into account so there is no need to check that again in tb_ctl_free(). Signed-off-by: zhong jiang [mw: reword commit log a bit] Signed-off-by: Mika Westerberg Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/ctl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 37a7f4c735d0..e54e84e43ede 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -662,8 +662,7 @@ void tb_ctl_free(struct tb_ctl *ctl) tb_ctl_pkg_free(ctl->rx_packets[i]); - if (ctl->frame_pool) - dma_pool_destroy(ctl->frame_pool); + dma_pool_destroy(ctl->frame_pool); kfree(ctl); } -- cgit v1.2.3-58-ga151 From daa5140f7e71f513606c2e4f394b9e8b8d679661 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 1 Oct 2018 12:31:19 +0300 Subject: thunderbolt: Make the driver less verbose Currently the driver logs quite a lot to the system message buffer even when doing normal operations. This information is not useful for ordinary users and might even annoy some. For this reason convert most of the logs at info level to happen at debug level instead. The nice output formatting is untouched. Logging can be easily re-enabled by passing "thunderbolt.dyndbg" in the kernel command line (or using the corresponding control file runtime). Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/ctl.c | 6 ++--- drivers/thunderbolt/eeprom.c | 2 +- drivers/thunderbolt/nhi.c | 30 +++++++++++------------ drivers/thunderbolt/path.c | 26 ++++++++++---------- drivers/thunderbolt/switch.c | 57 +++++++++++++++++++++----------------------- drivers/thunderbolt/tb.c | 10 ++++---- drivers/thunderbolt/tb.h | 6 +++-- 7 files changed, 68 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index e54e84e43ede..7456c094e232 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -631,7 +631,7 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) ctl->rx_packets[i]->frame.callback = tb_ctl_rx_callback; } - tb_ctl_info(ctl, "control channel created\n"); + tb_ctl_dbg(ctl, "control channel created\n"); return ctl; err: tb_ctl_free(ctl); @@ -672,7 +672,7 @@ void tb_ctl_free(struct tb_ctl *ctl) void tb_ctl_start(struct tb_ctl *ctl) { int i; - tb_ctl_info(ctl, "control channel starting...\n"); + tb_ctl_dbg(ctl, "control channel starting...\n"); tb_ring_start(ctl->tx); /* is used to ack hotplug packets, start first */ tb_ring_start(ctl->rx); for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) @@ -701,7 +701,7 @@ void tb_ctl_stop(struct tb_ctl *ctl) if (!list_empty(&ctl->request_queue)) tb_ctl_WARN(ctl, "dangling request in request_queue\n"); INIT_LIST_HEAD(&ctl->request_queue); - tb_ctl_info(ctl, "control channel stopped\n"); + tb_ctl_dbg(ctl, "control channel stopped\n"); } /* public interface, commands */ diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 3e8caf22c294..d2dd40390783 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -540,7 +540,7 @@ int tb_drom_read(struct tb_switch *sw) return res; size &= 0x3ff; size += TB_DROM_DATA_START; - tb_sw_info(sw, "reading drom (length: %#x)\n", size); + tb_sw_dbg(sw, "reading drom (length: %#x)\n", size); if (size < sizeof(*header)) { tb_sw_warn(sw, "drom too small, aborting\n"); return -EIO; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 88cff05a1808..02379aa27120 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -95,9 +95,9 @@ static void ring_interrupt_active(struct tb_ring *ring, bool active) else new = old & ~mask; - dev_info(&ring->nhi->pdev->dev, - "%s interrupt at register %#x bit %d (%#x -> %#x)\n", - active ? "enabling" : "disabling", reg, bit, old, new); + dev_dbg(&ring->nhi->pdev->dev, + "%s interrupt at register %#x bit %d (%#x -> %#x)\n", + active ? "enabling" : "disabling", reg, bit, old, new); if (new == old) dev_WARN(&ring->nhi->pdev->dev, @@ -476,8 +476,9 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, void *poll_data) { struct tb_ring *ring = NULL; - dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", - transmit ? "TX" : "RX", hop, size); + + dev_dbg(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", + transmit ? "TX" : "RX", hop, size); /* Tx Ring 2 is reserved for E2E workaround */ if (transmit && hop == RING_E2E_UNUSED_HOPID) @@ -585,8 +586,8 @@ void tb_ring_start(struct tb_ring *ring) dev_WARN(&ring->nhi->pdev->dev, "ring already started\n"); goto err; } - dev_info(&ring->nhi->pdev->dev, "starting %s %d\n", - RING_TYPE(ring), ring->hop); + dev_dbg(&ring->nhi->pdev->dev, "starting %s %d\n", + RING_TYPE(ring), ring->hop); if (ring->flags & RING_FLAG_FRAME) { /* Means 4096 */ @@ -647,8 +648,8 @@ void tb_ring_stop(struct tb_ring *ring) { spin_lock_irq(&ring->nhi->lock); spin_lock(&ring->lock); - dev_info(&ring->nhi->pdev->dev, "stopping %s %d\n", - RING_TYPE(ring), ring->hop); + dev_dbg(&ring->nhi->pdev->dev, "stopping %s %d\n", + RING_TYPE(ring), ring->hop); if (ring->nhi->going_away) goto err; if (!ring->running) { @@ -716,10 +717,8 @@ void tb_ring_free(struct tb_ring *ring) ring->descriptors_dma = 0; - dev_info(&ring->nhi->pdev->dev, - "freeing %s %d\n", - RING_TYPE(ring), - ring->hop); + dev_dbg(&ring->nhi->pdev->dev, "freeing %s %d\n", RING_TYPE(ring), + ring->hop); /** * ring->work can no longer be scheduled (it is scheduled only @@ -931,7 +930,8 @@ static int nhi_runtime_resume(struct device *dev) static void nhi_shutdown(struct tb_nhi *nhi) { int i; - dev_info(&nhi->pdev->dev, "shutdown\n"); + + dev_dbg(&nhi->pdev->dev, "shutdown\n"); for (i = 0; i < nhi->hop_count; i++) { if (nhi->tx_rings[i]) @@ -1059,7 +1059,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENODEV; } - dev_info(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n"); + dev_dbg(&nhi->pdev->dev, "NHI initialized, starting thunderbolt\n"); res = tb_domain_add(tb); if (res) { diff --git a/drivers/thunderbolt/path.c b/drivers/thunderbolt/path.c index ff49ad880bfd..a11956522bac 100644 --- a/drivers/thunderbolt/path.c +++ b/drivers/thunderbolt/path.c @@ -13,19 +13,19 @@ static void tb_dump_hop(struct tb_port *port, struct tb_regs_hop *hop) { - tb_port_info(port, " Hop through port %d to hop %d (%s)\n", - hop->out_port, hop->next_hop, - hop->enable ? "enabled" : "disabled"); - tb_port_info(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n", - hop->weight, hop->priority, - hop->initial_credits, hop->drop_packages); - tb_port_info(port, " Counter enabled: %d Counter index: %d\n", - hop->counter_enable, hop->counter); - tb_port_info(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n", - hop->ingress_fc, hop->egress_fc, - hop->ingress_shared_buffer, hop->egress_shared_buffer); - tb_port_info(port, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n", - hop->unknown1, hop->unknown2, hop->unknown3); + tb_port_dbg(port, " Hop through port %d to hop %d (%s)\n", + hop->out_port, hop->next_hop, + hop->enable ? "enabled" : "disabled"); + tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n", + hop->weight, hop->priority, + hop->initial_credits, hop->drop_packages); + tb_port_dbg(port, " Counter enabled: %d Counter index: %d\n", + hop->counter_enable, hop->counter); + tb_port_dbg(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n", + hop->ingress_fc, hop->egress_fc, + hop->ingress_shared_buffer, hop->egress_shared_buffer); + tb_port_dbg(port, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n", + hop->unknown1, hop->unknown2, hop->unknown3); } /** diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 7442bc4c6433..81134ee29578 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -436,15 +436,15 @@ static const char *tb_port_type(struct tb_regs_port_header *port) static void tb_dump_port(struct tb *tb, struct tb_regs_port_header *port) { - tb_info(tb, - " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", - port->port_number, port->vendor_id, port->device_id, - port->revision, port->thunderbolt_version, tb_port_type(port), - port->type); - tb_info(tb, " Max hop id (in/out): %d/%d\n", - port->max_in_hop_id, port->max_out_hop_id); - tb_info(tb, " Max counters: %d\n", port->max_counters); - tb_info(tb, " NFC Credits: %#x\n", port->nfc_credits); + tb_dbg(tb, + " Port %d: %x:%x (Revision: %d, TB Version: %d, Type: %s (%#x))\n", + port->port_number, port->vendor_id, port->device_id, + port->revision, port->thunderbolt_version, tb_port_type(port), + port->type); + tb_dbg(tb, " Max hop id (in/out): %d/%d\n", + port->max_in_hop_id, port->max_out_hop_id); + tb_dbg(tb, " Max counters: %d\n", port->max_counters); + tb_dbg(tb, " NFC Credits: %#x\n", port->nfc_credits); } /** @@ -605,20 +605,18 @@ static int tb_init_port(struct tb_port *port) static void tb_dump_switch(struct tb *tb, struct tb_regs_switch_header *sw) { - tb_info(tb, - " Switch: %x:%x (Revision: %d, TB Version: %d)\n", - sw->vendor_id, sw->device_id, sw->revision, - sw->thunderbolt_version); - tb_info(tb, " Max Port Number: %d\n", sw->max_port_number); - tb_info(tb, " Config:\n"); - tb_info(tb, + tb_dbg(tb, " Switch: %x:%x (Revision: %d, TB Version: %d)\n", + sw->vendor_id, sw->device_id, sw->revision, + sw->thunderbolt_version); + tb_dbg(tb, " Max Port Number: %d\n", sw->max_port_number); + tb_dbg(tb, " Config:\n"); + tb_dbg(tb, " Upstream Port Number: %d Depth: %d Route String: %#llx Enabled: %d, PlugEventsDelay: %dms\n", - sw->upstream_port_number, sw->depth, - (((u64) sw->route_hi) << 32) | sw->route_lo, - sw->enabled, sw->plug_events_delay); - tb_info(tb, - " unknown1: %#x unknown4: %#x\n", - sw->__unknown1, sw->__unknown4); + sw->upstream_port_number, sw->depth, + (((u64) sw->route_hi) << 32) | sw->route_lo, + sw->enabled, sw->plug_events_delay); + tb_dbg(tb, " unknown1: %#x unknown4: %#x\n", + sw->__unknown1, sw->__unknown4); } /** @@ -634,7 +632,7 @@ int tb_switch_reset(struct tb *tb, u64 route) header.route_lo = route, header.enabled = true, }; - tb_info(tb, "resetting switch at %llx\n", route); + tb_dbg(tb, "resetting switch at %llx\n", route); res.err = tb_cfg_write(tb->ctl, ((u32 *) &header) + 2, route, 0, 2, 2, 2); if (res.err) @@ -1139,7 +1137,7 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, if (tb_cfg_read(tb->ctl, &sw->config, route, 0, TB_CFG_SWITCH, 0, 5)) goto err_free_sw_ports; - tb_info(tb, "current switch config:\n"); + tb_dbg(tb, "current switch config:\n"); tb_dump_switch(tb, &sw->config); /* configure switch */ @@ -1246,9 +1244,8 @@ int tb_switch_configure(struct tb_switch *sw) int ret; route = tb_route(sw); - tb_info(tb, - "initializing Switch at %#llx (depth: %d, up port: %d)\n", - route, tb_route_length(route), sw->config.upstream_port_number); + tb_dbg(tb, "initializing Switch at %#llx (depth: %d, up port: %d)\n", + route, tb_route_length(route), sw->config.upstream_port_number); if (sw->config.vendor_id != PCI_VENDOR_ID_INTEL) tb_sw_warn(sw, "unknown switch vendor id %#x\n", @@ -1386,13 +1383,13 @@ int tb_switch_add(struct tb_switch *sw) tb_sw_warn(sw, "tb_eeprom_read_rom failed\n"); return ret; } - tb_sw_info(sw, "uid: %#llx\n", sw->uid); + tb_sw_dbg(sw, "uid: %#llx\n", sw->uid); tb_switch_set_uuid(sw); for (i = 0; i <= sw->config.max_port_number; i++) { if (sw->ports[i].disabled) { - tb_port_info(&sw->ports[i], "disabled by eeprom\n"); + tb_port_dbg(&sw->ports[i], "disabled by eeprom\n"); continue; } ret = tb_init_port(&sw->ports[i]); @@ -1483,7 +1480,7 @@ void tb_sw_set_unplugged(struct tb_switch *sw) int tb_switch_resume(struct tb_switch *sw) { int i, err; - tb_sw_info(sw, "resuming switch\n"); + tb_sw_dbg(sw, "resuming switch\n"); /* * Check for UID of the connected switches except for root diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 1424581fd9af..30e02c716f6c 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -404,10 +404,10 @@ static int tb_suspend_noirq(struct tb *tb) { struct tb_cm *tcm = tb_priv(tb); - tb_info(tb, "suspending...\n"); + tb_dbg(tb, "suspending...\n"); tb_switch_suspend(tb->root_switch); tcm->hotplug_active = false; /* signal tb_handle_hotplug to quit */ - tb_info(tb, "suspend finished\n"); + tb_dbg(tb, "suspend finished\n"); return 0; } @@ -417,7 +417,7 @@ static int tb_resume_noirq(struct tb *tb) struct tb_cm *tcm = tb_priv(tb); struct tb_pci_tunnel *tunnel, *n; - tb_info(tb, "resuming...\n"); + tb_dbg(tb, "resuming...\n"); /* remove any pci devices the firmware might have setup */ tb_switch_reset(tb, 0); @@ -432,12 +432,12 @@ static int tb_resume_noirq(struct tb *tb) * the pcie links need some time to get going. * 100ms works for me... */ - tb_info(tb, "tunnels restarted, sleeping for 100ms\n"); + tb_dbg(tb, "tunnels restarted, sleeping for 100ms\n"); msleep(100); } /* Allow tb_handle_hotplug to progress events */ tcm->hotplug_active = true; - tb_info(tb, "resume finished\n"); + tb_dbg(tb, "resume finished\n"); return 0; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 5067d69d0501..5a99245573c0 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -327,7 +327,7 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, #define tb_WARN(tb, fmt, arg...) dev_WARN(&(tb)->nhi->pdev->dev, fmt, ## arg) #define tb_warn(tb, fmt, arg...) dev_warn(&(tb)->nhi->pdev->dev, fmt, ## arg) #define tb_info(tb, fmt, arg...) dev_info(&(tb)->nhi->pdev->dev, fmt, ## arg) - +#define tb_dbg(tb, fmt, arg...) dev_dbg(&(tb)->nhi->pdev->dev, fmt, ## arg) #define __TB_SW_PRINT(level, sw, fmt, arg...) \ do { \ @@ -338,7 +338,7 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, #define tb_sw_WARN(sw, fmt, arg...) __TB_SW_PRINT(tb_WARN, sw, fmt, ##arg) #define tb_sw_warn(sw, fmt, arg...) __TB_SW_PRINT(tb_warn, sw, fmt, ##arg) #define tb_sw_info(sw, fmt, arg...) __TB_SW_PRINT(tb_info, sw, fmt, ##arg) - +#define tb_sw_dbg(sw, fmt, arg...) __TB_SW_PRINT(tb_dbg, sw, fmt, ##arg) #define __TB_PORT_PRINT(level, _port, fmt, arg...) \ do { \ @@ -352,6 +352,8 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, __TB_PORT_PRINT(tb_warn, port, fmt, ##arg) #define tb_port_info(port, fmt, arg...) \ __TB_PORT_PRINT(tb_info, port, fmt, ##arg) +#define tb_port_dbg(port, fmt, arg...) \ + __TB_PORT_PRINT(tb_dbg, port, fmt, ##arg) struct tb *icm_probe(struct tb_nhi *nhi); struct tb *tb_probe(struct tb_nhi *nhi); -- cgit v1.2.3-58-ga151 From a83bc4a5e8e08c15464f0b0b79934b834911cf9c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 1 Oct 2018 12:31:20 +0300 Subject: thunderbolt: Print connected devices The previous patch made the driver less verbose meanining that all the switch structures and ports are now logged as debug level. However, we have been missing similar output that USB for intance prints when a new USB device is connected and disconnected. This information is useful for end users as well as developers because it immediately shows the actual device that was connected. This patch adds printing of the actual connected devices to the driver. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/switch.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 81134ee29578..9f44a2924dac 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1402,6 +1402,14 @@ int tb_switch_add(struct tb_switch *sw) if (ret) return ret; + if (tb_route(sw)) { + dev_info(&sw->dev, "new device found, vendor=%#x device=%#x\n", + sw->vendor, sw->device); + if (sw->vendor_name && sw->device_name) + dev_info(&sw->dev, "%s %s\n", sw->vendor_name, + sw->device_name); + } + ret = tb_switch_nvm_add(sw); if (ret) { device_del(&sw->dev); @@ -1453,6 +1461,9 @@ void tb_switch_remove(struct tb_switch *sw) tb_plug_events_active(sw, false); tb_switch_nvm_remove(sw); + + if (tb_route(sw)) + dev_info(&sw->dev, "device disconnected\n"); device_unregister(&sw->dev); } -- cgit v1.2.3-58-ga151 From fd3b339cbb6047dacfa7ccc81c846efcb61032a9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 1 Oct 2018 12:31:21 +0300 Subject: thunderbolt: Convert rest of the driver files to use SPDX identifier This gets rid of the licence boilerplate duplicated in each file. While there fix doubled space in domain.c author line. No functional changes intended. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/dma_port.c | 5 +---- drivers/thunderbolt/dma_port.h | 5 +---- drivers/thunderbolt/domain.c | 7 ++----- drivers/thunderbolt/icm.c | 5 +---- drivers/thunderbolt/property.c | 5 +---- drivers/thunderbolt/tb_msgs.h | 5 +---- drivers/thunderbolt/xdomain.c | 5 +---- include/linux/thunderbolt.h | 5 +---- 8 files changed, 9 insertions(+), 33 deletions(-) (limited to 'drivers') diff --git a/drivers/thunderbolt/dma_port.c b/drivers/thunderbolt/dma_port.c index f2701194f810..847dd07a7b17 100644 --- a/drivers/thunderbolt/dma_port.c +++ b/drivers/thunderbolt/dma_port.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Thunderbolt DMA configuration based mailbox support * * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/thunderbolt/dma_port.h b/drivers/thunderbolt/dma_port.h index c4a69e0fbff7..7deadd97ce31 100644 --- a/drivers/thunderbolt/dma_port.h +++ b/drivers/thunderbolt/dma_port.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Thunderbolt DMA configuration based mailbox support * * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef DMA_PORT_H_ diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 092381e2accf..93e562f18d40 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Thunderbolt bus support * * Copyright (C) 2017, Intel Corporation - * Author: Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * Author: Mika Westerberg */ #include diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index e1e264a9a4c7..533068651f90 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Internal Thunderbolt Connection Manager. This is a firmware running on * the Thunderbolt host controller performing most of the low-level @@ -6,10 +7,6 @@ * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 8fe913a95b4a..b2f0d6386cee 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Thunderbolt XDomain property support * * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 2487e162c885..02c84aa3d018 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Thunderbolt control channel messages * * Copyright (C) 2014 Andreas Noever * Copyright (C) 2017, Intel Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _TB_MSGS diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index db8bece63327..e27dd8beb94b 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Thunderbolt XDomain discovery protocol support * * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index a3ed26082bc1..bf6ec83e60ee 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Thunderbolt service API * @@ -5,10 +6,6 @@ * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef THUNDERBOLT_H_ -- cgit v1.2.3-58-ga151 From 15c6784c7cee3b653f127b41340210284dea66f6 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 1 Oct 2018 12:31:22 +0300 Subject: thunderbolt: Add Intel as copyright holder Intel has done pretty major changes to the driver and we continue to do so in the future as well. Add Intel as copyright holder of the files we have done changes. While there drop "Cactus Ridge" from the headers because this driver works also with other Thunderbolt controllers. No functional changes intended. Signed-off-by: Mika Westerberg Acked-by: Yehezkel Bernat Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/cap.c | 3 ++- drivers/thunderbolt/ctl.c | 3 ++- drivers/thunderbolt/ctl.h | 3 ++- drivers/thunderbolt/eeprom.c | 3 ++- drivers/thunderbolt/nhi.c | 3 ++- drivers/thunderbolt/nhi.h | 3 ++- drivers/thunderbolt/nhi_regs.h | 1 + drivers/thunderbolt/switch.c | 3 ++- drivers/thunderbolt/tb.h | 3 ++- drivers/thunderbolt/tb_regs.h | 3 ++- 10 files changed, 19 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/thunderbolt/cap.c b/drivers/thunderbolt/cap.c index c2277b8ee88d..9553305c63ea 100644 --- a/drivers/thunderbolt/cap.c +++ b/drivers/thunderbolt/cap.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Thunderbolt Cactus Ridge driver - capabilities lookup + * Thunderbolt driver - capabilities lookup * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #include diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 7456c094e232..73b386de4d15 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Thunderbolt Cactus Ridge driver - control channel and configuration commands + * Thunderbolt driver - control channel and configuration commands * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #include diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index 3062e0b5f71e..2f1a1e111110 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Thunderbolt Cactus Ridge driver - control channel and configuration commands + * Thunderbolt driver - control channel and configuration commands * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #ifndef _TB_CFG diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index d2dd40390783..81e8ac4c5805 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Thunderbolt Cactus Ridge driver - eeprom access + * Thunderbolt driver - eeprom access * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #include diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 02379aa27120..2874f9400123 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1,10 +1,11 @@ /* - * Thunderbolt Cactus Ridge driver - NHI driver + * Thunderbolt driver - NHI driver * * The NHI (native host interface) is the pci device that allows us to send and * receive frames from the thunderbolt bus. * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #include diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 1696a4560948..1b5d47ecd3ed 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Thunderbolt Cactus Ridge driver - NHI driver + * Thunderbolt driver - NHI driver * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #ifndef DSL3510_H_ diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index b3e49d19c01e..a60bd98c1d04 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -3,6 +3,7 @@ * Thunderbolt driver - NHI registers * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #ifndef NHI_REGS_H_ diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 9f44a2924dac..52ff854f0d6c 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Thunderbolt Cactus Ridge driver - switch/port utility functions + * Thunderbolt driver - switch/port utility functions * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #include diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 5a99245573c0..52584c4003e3 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Thunderbolt Cactus Ridge driver - bus logic (NHI independent) + * Thunderbolt driver - bus logic (NHI independent) * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #ifndef TB_H_ diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 693b0353c3fe..6f1ff04ee195 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Thunderbolt Cactus Ridge driver - Port/Switch config area registers + * Thunderbolt driver - Port/Switch config area registers * * Every thunderbolt device consists (logically) of a switch with multiple * ports. Every port contains up to four config regions (HOPS, PORT, SWITCH, * COUNTERS) which are used to configure the device. * * Copyright (c) 2014 Andreas Noever + * Copyright (C) 2018, Intel Corporation */ #ifndef _TB_REGS -- cgit v1.2.3-58-ga151 From f4608ce917d6f50824f4c609d52a3c8900c59164 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 25 Sep 2018 14:30:36 +0000 Subject: binder: make symbol 'binder_free_buf' static Fixes the following sparse warning: drivers/android/binder.c:3312:1: warning: symbol 'binder_free_buf' was not declared. Should it be static? Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 1b54bb57a9fb..cb30a524d16d 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3308,7 +3308,7 @@ err_invalid_target_handle: * * Cleanup buffer and free it. */ -void +static void binder_free_buf(struct binder_proc *proc, struct binder_buffer *buffer) { if (buffer->transaction) { -- cgit v1.2.3-58-ga151 From ef8ec6e1f93df868e441101beb62733f09167764 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Thu, 27 Sep 2018 17:51:48 +0800 Subject: misc: card_utils: remove duplicated include file delay.h and dma-mapping.h have duplicated include. hence just remove redundant file. Signed-off-by: zhong jiang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/genwqe/card_utils.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index f0961ecb10b2..3fcb9a2fe1c9 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c @@ -23,14 +23,12 @@ */ #include -#include #include #include #include #include #include #include -#include #include #include #include -- cgit v1.2.3-58-ga151 From 52e2dc2ce2d88a1c588cca4fb8ce0c1608c8a7f9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 1 Oct 2018 16:43:51 -0500 Subject: uio: Convert a few more users to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Greg Kroah-Hartman Signed-off-by: Rob Herring Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dmem_genirq.c | 3 ++- drivers/uio/uio_pdrv_genirq.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index e1134a4d97f3..003badaef5f3 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -163,7 +163,8 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to kmalloc\n"); goto bad2; } - uioinfo->name = pdev->dev.of_node->name; + uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", + pdev->dev.of_node); uioinfo->version = "devicetree"; /* Multiple IRQs are not supported */ diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index f598ecddc8a7..6c759934bff3 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -118,7 +118,8 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to kmalloc\n"); return -ENOMEM; } - uioinfo->name = pdev->dev.of_node->name; + uioinfo->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%pOFn", + pdev->dev.of_node); uioinfo->version = "devicetree"; /* Multiple IRQs are not supported */ } -- cgit v1.2.3-58-ga151 From 8f523d6db7ed69f69720267af170c0719023f373 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 27 Sep 2018 11:01:19 +0000 Subject: VMCI: remove set but not used variable 'cid' Fixes gcc '-Wunused-but-set-variable' warning: drivers/misc/vmw_vmci/vmci_host.c: In function 'vmci_host_do_alloc_queuepair': drivers/misc/vmw_vmci/vmci_host.c:450:6: warning: variable 'cid' set but not used [-Wunused-but-set-variable] u32 cid; ^ Signed-off-by: YueHaibing Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_host.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index be732e5ead75..edfffc9699ba 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -447,15 +447,12 @@ static int vmci_host_do_alloc_queuepair(struct vmci_host_dev *vmci_host_dev, struct vmci_handle handle; int vmci_status; int __user *retptr; - u32 cid; if (vmci_host_dev->ct_type != VMCIOBJ_CONTEXT) { vmci_ioctl_err("only valid for contexts\n"); return -EINVAL; } - cid = vmci_ctx_get_id(vmci_host_dev->context); - if (vmci_host_dev->user_version < VMCI_VERSION_NOVMVM) { struct vmci_qp_alloc_info_vmvm alloc_info; struct vmci_qp_alloc_info_vmvm __user *info = uptr; -- cgit v1.2.3-58-ga151 From cb6102bd99efe35f016dc6d7282e681e6dbde154 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:51 +0300 Subject: stm class: Rework policy node fallback Currently, if no matching policy node can be found for a trace source, we'll try to use "default" policy node, then, if that doesn't exist, we'll pick the first node, in order of creation. If that also fails, we'll allocate M/C range from the beginning of the device's M/C range. This makes it difficult to know which node (if any) was used in any particular case. In order to make things more deterministic, the new order is as follows: * if they supply ID string, use that and nothing else, * if they are a task, use their task name (comm), * use "default", if it exists, * return failure, to let them know there is no suitable rule. This should provide enough convenience with the "default" catch-all node, while not leaving *everything* to chance. As a side effect, this relaxes the requirement of using ioctl() for identification with the possibility of using task names as policy nodes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 85 ++++++++++++++++++++++++------------------ drivers/hwtracing/stm/policy.c | 12 +++--- drivers/hwtracing/stm/stm.h | 2 - 3 files changed, 55 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 10bcb5d73f90..17198e79df3e 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -293,15 +293,15 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, if (width > stm->data->sw_nchannels) return -EINVAL; - if (policy_node) { - stp_policy_node_get_ranges(policy_node, - &midx, &mend, &cidx, &cend); - } else { - midx = stm->data->sw_start; - cidx = 0; - mend = stm->data->sw_end; - cend = stm->data->sw_nchannels - 1; - } + /* We no longer accept policy_node==NULL here */ + if (WARN_ON_ONCE(!policy_node)) + return -EINVAL; + + /* + * Also, the caller holds reference to policy_node, so it won't + * disappear on us. + */ + stp_policy_node_get_ranges(policy_node, &midx, &mend, &cidx, &cend); spin_lock(&stm->mc_lock); spin_lock(&output->lock); @@ -405,19 +405,30 @@ static int stm_char_release(struct inode *inode, struct file *file) return 0; } -static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) +static int +stm_assign_first_policy(struct stm_device *stm, struct stm_output *output, + char **ids, unsigned int width) { - struct stm_device *stm = stmf->stm; - int ret; + struct stp_policy_node *pn; + int err, n; - stmf->policy_node = stp_policy_node_lookup(stm, id); + /* + * On success, stp_policy_node_lookup() will return holding the + * configfs subsystem mutex, which is then released in + * stp_policy_node_put(). This allows the pdrv->output_open() in + * stm_output_assign() to serialize against the attribute accessors. + */ + for (n = 0, pn = NULL; ids[n] && !pn; n++) + pn = stp_policy_node_lookup(stm, ids[n]); - ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output); + if (!pn) + return -EINVAL; - if (stmf->policy_node) - stp_policy_node_put(stmf->policy_node); + err = stm_output_assign(stm, width, pn, output); - return ret; + stp_policy_node_put(pn); + + return err; } static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, @@ -455,16 +466,21 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, count = PAGE_SIZE - 1; /* - * if no m/c have been assigned to this writer up to this - * point, use "default" policy entry + * If no m/c have been assigned to this writer up to this + * point, try to use the task name and "default" policy entries. */ if (!stmf->output.nr_chans) { - err = stm_file_assign(stmf, "default", 1); + char comm[sizeof(current->comm)]; + char *ids[] = { comm, "default", NULL }; + + get_task_comm(comm, current); + + err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1); /* * EBUSY means that somebody else just assigned this * output, which is just fine for write() */ - if (err && err != -EBUSY) + if (err) return err; } @@ -550,6 +566,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) { struct stm_device *stm = stmf->stm; struct stp_policy_id *id; + char *ids[] = { NULL, NULL }; int ret = -EINVAL; u32 size; @@ -582,7 +599,9 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) id->width > PAGE_SIZE / stm->data->sw_mmiosz) goto err_free; - ret = stm_file_assign(stmf, id->id, id->width); + ids[0] = id->id; + ret = stm_assign_first_policy(stmf->stm, &stmf->output, ids, + id->width); if (ret) goto err_free; @@ -818,8 +837,8 @@ EXPORT_SYMBOL_GPL(stm_unregister_device); static int stm_source_link_add(struct stm_source_device *src, struct stm_device *stm) { - char *id; - int err; + char *ids[] = { NULL, "default", NULL }; + int err = -ENOMEM; mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); @@ -833,19 +852,13 @@ static int stm_source_link_add(struct stm_source_device *src, spin_unlock(&stm->link_lock); mutex_unlock(&stm->link_mutex); - id = kstrdup(src->data->name, GFP_KERNEL); - if (id) { - src->policy_node = - stp_policy_node_lookup(stm, id); - - kfree(id); - } - - err = stm_output_assign(stm, src->data->nr_chans, - src->policy_node, &src->output); + ids[0] = kstrdup(src->data->name, GFP_KERNEL); + if (!ids[0]) + goto fail_detach; - if (src->policy_node) - stp_policy_node_put(src->policy_node); + err = stm_assign_first_policy(stm, &src->output, ids, + src->data->nr_chans); + kfree(ids[0]); if (err) goto fail_detach; diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 3fd07e275b34..15d35d891643 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -392,7 +392,7 @@ static struct configfs_subsystem stp_policy_subsys = { static struct stp_policy_node * __stp_policy_node_lookup(struct stp_policy *policy, char *s) { - struct stp_policy_node *policy_node, *ret; + struct stp_policy_node *policy_node, *ret = NULL; struct list_head *head = &policy->group.cg_children; struct config_item *item; char *start, *end = s; @@ -400,10 +400,6 @@ __stp_policy_node_lookup(struct stp_policy *policy, char *s) if (list_empty(head)) return NULL; - /* return the first entry if everything else fails */ - item = list_entry(head->next, struct config_item, ci_entry); - ret = to_stp_policy_node(item); - next: for (;;) { start = strsep(&end, "/"); @@ -449,13 +445,17 @@ stp_policy_node_lookup(struct stm_device *stm, char *s) if (policy_node) config_item_get(&policy_node->group.cg_item); - mutex_unlock(&stp_policy_subsys.su_mutex); + else + mutex_unlock(&stp_policy_subsys.su_mutex); return policy_node; } void stp_policy_node_put(struct stp_policy_node *policy_node) { + lockdep_assert_held(&stp_policy_subsys.su_mutex); + + mutex_unlock(&stp_policy_subsys.su_mutex); config_item_put(&policy_node->group.cg_item); } diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 923571adc6f4..e5df08ae59cf 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -57,7 +57,6 @@ struct stm_output { struct stm_file { struct stm_device *stm; - struct stp_policy_node *policy_node; struct stm_output output; }; @@ -71,7 +70,6 @@ struct stm_source_device { struct stm_device __rcu *link; struct list_head link_entry; /* one output per stm_source device */ - struct stp_policy_node *policy_node; struct stm_output output; }; -- cgit v1.2.3-58-ga151 From 25e3c0062a089544a861f72287a1fa3ce34b00b7 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:52 +0300 Subject: stm class: Clarify configfs root type/operations names The current naming of stp-policy root type and group ops is confusing, rename them for better readability. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 15d35d891643..530448bd5a34 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -311,7 +311,7 @@ static const struct config_item_type stp_policy_type = { }; static struct config_group * -stp_policies_make(struct config_group *group, const char *name) +stp_policy_make(struct config_group *group, const char *name) { struct config_group *ret; struct stm_device *stm; @@ -368,12 +368,12 @@ unlock_policy: return ret; } -static struct configfs_group_operations stp_policies_group_ops = { - .make_group = stp_policies_make, +static struct configfs_group_operations stp_policy_root_group_ops = { + .make_group = stp_policy_make, }; -static const struct config_item_type stp_policies_type = { - .ct_group_ops = &stp_policies_group_ops, +static const struct config_item_type stp_policy_root_type = { + .ct_group_ops = &stp_policy_root_group_ops, .ct_owner = THIS_MODULE, }; @@ -381,7 +381,7 @@ static struct configfs_subsystem stp_policy_subsys = { .su_group = { .cg_item = { .ci_namebuf = "stp-policy", - .ci_type = &stp_policies_type, + .ci_type = &stp_policy_root_type, }, }, }; -- cgit v1.2.3-58-ga151 From e967b8bdd462def829b1722ea2f0babdc9806bc2 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:53 +0300 Subject: stm class: Clean up stp_configfs_init Minor code shortening, no functional changes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 530448bd5a34..a505f055f464 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -461,13 +461,9 @@ void stp_policy_node_put(struct stp_policy_node *policy_node) int __init stp_configfs_init(void) { - int err; - config_group_init(&stp_policy_subsys.su_group); mutex_init(&stp_policy_subsys.su_mutex); - err = configfs_register_subsystem(&stp_policy_subsys); - - return err; + return configfs_register_subsystem(&stp_policy_subsys); } void __exit stp_configfs_exit(void) -- cgit v1.2.3-58-ga151 From c7fd62bc69d0224877a49383e606f0fe52cba741 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:54 +0300 Subject: stm class: Introduce framing protocol drivers At the moment, the stm class applies a certain STP framing pattern to the data as it is written to the underlying STM device. In order to allow different framing patterns (aka protocols), this patch introduces the concept of STP protocol drivers, defines data structures and APIs for the protocol drivers to use. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 140 +++++++++++++++++++++++++++++++++++++++ drivers/hwtracing/stm/policy.c | 145 ++++++++++++++++++++++++++++++++++++++--- drivers/hwtracing/stm/stm.h | 51 ++++++++++++--- 3 files changed, 318 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 17198e79df3e..915af2541dcd 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -316,11 +316,26 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, output->master = midx; output->channel = cidx; output->nr_chans = width; + if (stm->pdrv->output_open) { + void *priv = stp_policy_node_priv(policy_node); + + if (WARN_ON_ONCE(!priv)) + goto unlock; + + /* configfs subsys mutex is held by the caller */ + ret = stm->pdrv->output_open(priv, output); + if (ret) + goto unlock; + } + stm_output_claim(stm, output); dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width); ret = 0; unlock: + if (ret) + output->nr_chans = 0; + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); @@ -333,6 +348,8 @@ static void stm_output_free(struct stm_device *stm, struct stm_output *output) spin_lock(&output->lock); if (output->nr_chans) stm_output_disclaim(stm, output); + if (stm->pdrv && stm->pdrv->output_close) + stm->pdrv->output_close(output); spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); } @@ -349,6 +366,127 @@ static int major_match(struct device *dev, const void *data) return MAJOR(dev->devt) == major; } +/* + * Framing protocol management + * Modules can implement STM protocol drivers and (un-)register them + * with the STM class framework. + */ +static struct list_head stm_pdrv_head; +static struct mutex stm_pdrv_mutex; + +struct stm_pdrv_entry { + struct list_head entry; + const struct stm_protocol_driver *pdrv; + const struct config_item_type *node_type; +}; + +static const struct stm_pdrv_entry * +__stm_lookup_protocol(const char *name) +{ + struct stm_pdrv_entry *pe; + + /* + * If no name is given (NULL or ""), fall back to "p_basic". + */ + if (!name || !*name) + name = "p_basic"; + + list_for_each_entry(pe, &stm_pdrv_head, entry) { + if (!strcmp(name, pe->pdrv->name)) + return pe; + } + + return NULL; +} + +int stm_register_protocol(const struct stm_protocol_driver *pdrv) +{ + struct stm_pdrv_entry *pe = NULL; + int ret = -ENOMEM; + + mutex_lock(&stm_pdrv_mutex); + + if (__stm_lookup_protocol(pdrv->name)) { + ret = -EEXIST; + goto unlock; + } + + pe = kzalloc(sizeof(*pe), GFP_KERNEL); + if (!pe) + goto unlock; + + if (pdrv->policy_attr) { + pe->node_type = get_policy_node_type(pdrv->policy_attr); + if (!pe->node_type) + goto unlock; + } + + list_add_tail(&pe->entry, &stm_pdrv_head); + pe->pdrv = pdrv; + + ret = 0; +unlock: + mutex_unlock(&stm_pdrv_mutex); + + if (ret) + kfree(pe); + + return ret; +} +EXPORT_SYMBOL_GPL(stm_register_protocol); + +void stm_unregister_protocol(const struct stm_protocol_driver *pdrv) +{ + struct stm_pdrv_entry *pe, *iter; + + mutex_lock(&stm_pdrv_mutex); + + list_for_each_entry_safe(pe, iter, &stm_pdrv_head, entry) { + if (pe->pdrv == pdrv) { + list_del(&pe->entry); + + if (pe->node_type) { + kfree(pe->node_type->ct_attrs); + kfree(pe->node_type); + } + kfree(pe); + break; + } + } + + mutex_unlock(&stm_pdrv_mutex); +} +EXPORT_SYMBOL_GPL(stm_unregister_protocol); + +static bool stm_get_protocol(const struct stm_protocol_driver *pdrv) +{ + return try_module_get(pdrv->owner); +} + +void stm_put_protocol(const struct stm_protocol_driver *pdrv) +{ + module_put(pdrv->owner); +} + +int stm_lookup_protocol(const char *name, + const struct stm_protocol_driver **pdrv, + const struct config_item_type **node_type) +{ + const struct stm_pdrv_entry *pe; + + mutex_lock(&stm_pdrv_mutex); + + pe = __stm_lookup_protocol(name); + if (pe && pe->pdrv && stm_get_protocol(pe->pdrv)) { + *pdrv = pe->pdrv; + *node_type = pe->node_type; + } + + mutex_unlock(&stm_pdrv_mutex); + + return pe ? 0 : -ENOENT; +} + static int stm_char_open(struct inode *inode, struct file *file) { struct stm_file *stmf; @@ -1176,6 +1314,8 @@ static int __init stm_core_init(void) goto err_src; init_srcu_struct(&stm_source_srcu); + INIT_LIST_HEAD(&stm_pdrv_head); + mutex_init(&stm_pdrv_mutex); stm_core_up++; diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index a505f055f464..8bc90432ae69 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -33,8 +33,18 @@ struct stp_policy_node { unsigned int last_master; unsigned int first_channel; unsigned int last_channel; + /* this is the one that's exposed to the attributes */ + unsigned char priv[0]; }; +void *stp_policy_node_priv(struct stp_policy_node *pn) +{ + if (!pn) + return NULL; + + return pn->priv; +} + static struct configfs_subsystem stp_policy_subsys; void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, @@ -68,6 +78,14 @@ to_stp_policy_node(struct config_item *item) NULL; } +void *to_pdrv_policy_node(struct config_item *item) +{ + struct stp_policy_node *node = to_stp_policy_node(item); + + return stp_policy_node_priv(node); +} +EXPORT_SYMBOL_GPL(to_pdrv_policy_node); + static ssize_t stp_policy_node_masters_show(struct config_item *item, char *page) { @@ -163,7 +181,9 @@ unlock: static void stp_policy_node_release(struct config_item *item) { - kfree(to_stp_policy_node(item)); + struct stp_policy_node *node = to_stp_policy_node(item); + + kfree(node); } static struct configfs_item_operations stp_policy_node_item_ops = { @@ -182,10 +202,61 @@ static struct configfs_attribute *stp_policy_node_attrs[] = { static const struct config_item_type stp_policy_type; static const struct config_item_type stp_policy_node_type; +/* lifted from arch/x86/events/core.c */ +static struct configfs_attribute **merge_attr(struct configfs_attribute **a, struct configfs_attribute **b) +{ + struct configfs_attribute **new; + int j, i; + + for (j = 0; a[j]; j++) + ; + for (i = 0; b[i]; i++) + j++; + j++; + + new = kmalloc_array(j, sizeof(struct configfs_attribute *), + GFP_KERNEL); + if (!new) + return NULL; + + j = 0; + for (i = 0; a[i]; i++) + new[j++] = a[i]; + for (i = 0; b[i]; i++) + new[j++] = b[i]; + new[j] = NULL; + + return new; +} + +const struct config_item_type * +get_policy_node_type(struct configfs_attribute **attrs) +{ + struct config_item_type *type; + struct configfs_attribute **merged; + + type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type), + GFP_KERNEL); + if (!type) + return NULL; + + merged = merge_attr(stp_policy_node_attrs, attrs); + if (!merged) { + kfree(type); + return NULL; + } + + type->ct_attrs = merged; + + return type; +} + static struct config_group * stp_policy_node_make(struct config_group *group, const char *name) { + const struct config_item_type *type = &stp_policy_node_type; struct stp_policy_node *policy_node, *parent_node; + const struct stm_protocol_driver *pdrv; struct stp_policy *policy; if (group->cg_item.ci_type == &stp_policy_type) { @@ -199,12 +270,20 @@ stp_policy_node_make(struct config_group *group, const char *name) if (!policy->stm) return ERR_PTR(-ENODEV); - policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL); + pdrv = policy->stm->pdrv; + policy_node = + kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), + GFP_KERNEL); if (!policy_node) return ERR_PTR(-ENOMEM); - config_group_init_type_name(&policy_node->group, name, - &stp_policy_node_type); + if (pdrv->policy_node_init) + pdrv->policy_node_init((void *)policy_node->priv); + + if (policy->stm->pdrv_node_type) + type = policy->stm->pdrv_node_type; + + config_group_init_type_name(&policy_node->group, name, type); policy_node->policy = policy; @@ -254,8 +333,25 @@ static ssize_t stp_policy_device_show(struct config_item *item, CONFIGFS_ATTR_RO(stp_policy_, device); +static ssize_t stp_policy_protocol_show(struct config_item *item, + char *page) +{ + struct stp_policy *policy = to_stp_policy(item); + ssize_t count; + + count = sprintf(page, "%s\n", + (policy && policy->stm) ? + policy->stm->pdrv->name : + ""); + + return count; +} + +CONFIGFS_ATTR_RO(stp_policy_, protocol); + static struct configfs_attribute *stp_policy_attrs[] = { &stp_policy_attr_device, + &stp_policy_attr_protocol, NULL, }; @@ -276,6 +372,7 @@ void stp_policy_unbind(struct stp_policy *policy) stm->policy = NULL; policy->stm = NULL; + stm_put_protocol(stm->pdrv); stm_put_device(stm); } @@ -313,9 +410,12 @@ static const struct config_item_type stp_policy_type = { static struct config_group * stp_policy_make(struct config_group *group, const char *name) { + const struct config_item_type *pdrv_node_type; + const struct stm_protocol_driver *pdrv; + char *devname, *proto, *p; struct config_group *ret; struct stm_device *stm; - char *devname, *p; + int err; devname = kasprintf(GFP_KERNEL, "%s", name); if (!devname) @@ -326,6 +426,7 @@ stp_policy_make(struct config_group *group, const char *name) * is the name of an existing stm device; may * contain dots; * is an arbitrary string; may not contain dots + * :. */ p = strrchr(devname, '.'); if (!p) { @@ -335,11 +436,29 @@ stp_policy_make(struct config_group *group, const char *name) *p = '\0'; + /* + * look for ":": + * + no protocol suffix: fall back to whatever is available; + * + unknown protocol: fail the whole thing + */ + proto = strrchr(devname, ':'); + if (proto) + *proto++ = '\0'; + stm = stm_find_device(devname); + if (!stm) { + kfree(devname); + return ERR_PTR(-ENODEV); + } + + err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); kfree(devname); - if (!stm) + /* We don't have any protocol drivers yet */ + if (err != -ENOENT) { + stm_put_device(stm); return ERR_PTR(-ENODEV); + } mutex_lock(&stm->policy_mutex); if (stm->policy) { @@ -349,21 +468,27 @@ stp_policy_make(struct config_group *group, const char *name) stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); if (!stm->policy) { - ret = ERR_PTR(-ENOMEM); - goto unlock_policy; + mutex_unlock(&stm->policy_mutex); + stm_put_protocol(pdrv); + stm_put_device(stm); + return ERR_PTR(-ENOMEM); } config_group_init_type_name(&stm->policy->group, name, &stp_policy_type); - stm->policy->stm = stm; + stm->pdrv = pdrv; + stm->pdrv_node_type = pdrv_node_type; + stm->policy->stm = stm; ret = &stm->policy->group; unlock_policy: mutex_unlock(&stm->policy_mutex); - if (IS_ERR(ret)) + if (IS_ERR(ret)) { + stm_put_protocol(stm->pdrv); stm_put_device(stm); + } return ret; } diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index e5df08ae59cf..ed7f3d07fa47 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -10,20 +10,17 @@ #ifndef _STM_STM_H_ #define _STM_STM_H_ +#include + struct stp_policy; struct stp_policy_node; +struct stm_protocol_driver; -struct stp_policy_node * -stp_policy_node_lookup(struct stm_device *stm, char *s); -void stp_policy_node_put(struct stp_policy_node *policy_node); -void stp_policy_unbind(struct stp_policy *policy); - -void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, - unsigned int *mstart, unsigned int *mend, - unsigned int *cstart, unsigned int *cend); int stp_configfs_init(void); void stp_configfs_exit(void); +void *stp_policy_node_priv(struct stp_policy_node *pn); + struct stp_master { unsigned int nr_free; unsigned long chan_map[0]; @@ -40,6 +37,9 @@ struct stm_device { struct mutex link_mutex; spinlock_t link_lock; struct list_head link_list; + /* framing protocol in use */ + const struct stm_protocol_driver *pdrv; + const struct config_item_type *pdrv_node_type; /* master allocation */ spinlock_t mc_lock; struct stp_master *masters[0]; @@ -48,11 +48,24 @@ struct stm_device { #define to_stm_device(_d) \ container_of((_d), struct stm_device, dev) +struct stp_policy_node * +stp_policy_node_lookup(struct stm_device *stm, char *s); +void stp_policy_node_put(struct stp_policy_node *policy_node); +void stp_policy_unbind(struct stp_policy *policy); + +void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, + unsigned int *mstart, unsigned int *mend, + unsigned int *cstart, unsigned int *cend); + +const struct config_item_type * +get_policy_node_type(struct configfs_attribute **attrs); + struct stm_output { spinlock_t lock; unsigned int master; unsigned int channel; unsigned int nr_chans; + void *pdrv_private; }; struct stm_file { @@ -76,4 +89,26 @@ struct stm_source_device { #define to_stm_source_device(_d) \ container_of((_d), struct stm_source_device, dev) +void *to_pdrv_policy_node(struct config_item *item); + +struct stm_protocol_driver { + struct module *owner; + const char *name; + ssize_t (*write)(struct stm_data *data, + struct stm_output *output, unsigned int chan, + const char *buf, size_t count); + void (*policy_node_init)(void *arg); + int (*output_open)(void *priv, struct stm_output *output); + void (*output_close)(struct stm_output *output); + ssize_t priv_sz; + struct configfs_attribute **policy_attr; +}; + +int stm_register_protocol(const struct stm_protocol_driver *pdrv); +void stm_unregister_protocol(const struct stm_protocol_driver *pdrv); +int stm_lookup_protocol(const char *name, + const struct stm_protocol_driver **pdrv, + const struct config_item_type **type); +void stm_put_protocol(const struct stm_protocol_driver *pdrv); + #endif /* _STM_STM_H_ */ -- cgit v1.2.3-58-ga151 From d279a38020d2483cb75f5f82f5e4ab5f73bc94f2 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:55 +0300 Subject: stm class: Add a helper for writing data packets Add a helper to write a sequence of bytes as STP data packets. This is used by protocol drivers to output their metadata, as well as the actual data payload. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 51 +++++++++++++++++++++++++++++++++----------- drivers/hwtracing/stm/stm.h | 3 +++ 2 files changed, 41 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 915af2541dcd..b789a5f0688e 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -569,27 +569,52 @@ stm_assign_first_policy(struct stm_device *stm, struct stm_output *output, return err; } -static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +/** + * stm_data_write() - send the given payload as data packets + * @data: stm driver's data + * @m: STP master + * @c: STP channel + * @ts_first: timestamp the first packet + * @buf: data payload buffer + * @count: data payload size + */ +ssize_t notrace stm_data_write(struct stm_data *data, unsigned int m, + unsigned int c, bool ts_first, const void *buf, + size_t count) { - unsigned int flags = STP_PACKET_TIMESTAMPED; - const unsigned char *p = buf, nil = 0; - size_t pos; + unsigned int flags = ts_first ? STP_PACKET_TIMESTAMPED : 0; ssize_t sz; + size_t pos; - for (pos = 0, p = buf; count > pos; pos += sz, p += sz) { + for (pos = 0, sz = 0; pos < count; pos += sz) { sz = min_t(unsigned int, count - pos, 8); - sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, - sz, p); - flags = 0; - - if (sz < 0) + sz = data->packet(data, m, c, STP_PACKET_DATA, flags, sz, + &((u8 *)buf)[pos]); + if (sz <= 0) break; + + if (ts_first) { + flags = 0; + ts_first = false; + } } - data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); + return sz < 0 ? sz : pos; +} +EXPORT_SYMBOL_GPL(stm_data_write); + +static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, + unsigned int channel, const char *buf, size_t count) +{ + const unsigned char nil = 0; + ssize_t sz; + + sz = stm_data_write(data, master, channel, true, buf, count); + if (sz > 0) + data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, + &nil); - return pos; + return sz; } static ssize_t stm_char_write(struct file *file, const char __user *buf, diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index ed7f3d07fa47..3569439d53bb 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -110,5 +110,8 @@ int stm_lookup_protocol(const char *name, const struct stm_protocol_driver **pdrv, const struct config_item_type **type); void stm_put_protocol(const struct stm_protocol_driver *pdrv); +ssize_t stm_data_write(struct stm_data *data, unsigned int m, + unsigned int c, bool ts_first, const void *buf, + size_t count); #endif /* _STM_STM_H_ */ -- cgit v1.2.3-58-ga151 From a02509f301c68c9a7f01d75d7d326f7a9b81a630 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:56 +0300 Subject: stm class: Factor out default framing protocol The STP framing pattern that the stm class implicitly applies to the data payload is, in fact, a protocol. This patch moves the relevant code out of the stm core into its own driver module. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 15 +++++++++++++ drivers/hwtracing/stm/Makefile | 4 ++++ drivers/hwtracing/stm/p_basic.c | 48 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 drivers/hwtracing/stm/p_basic.c (limited to 'drivers') diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 723e2d90083d..262e7891fb97 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -11,6 +11,21 @@ config STM if STM +config STM_PROTO_BASIC + tristate "Basic STM framing protocol driver" + default CONFIG_STM + help + This is a simple framing protocol for sending data over STM + devices. This was the protocol that the STM framework used + exclusively until the MIPI SyS-T support was added. Use this + driver for compatibility with your existing STM setup. + + The receiving side only needs to be able to decode the MIPI + STP protocol in order to extract the data. + + If you want to be able to use the basic protocol or want the + backwards compatibility for your existing setup, say Y. + config STM_DUMMY tristate "Dummy STM driver" help diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index effc19e5190f..1571de66eca4 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -3,6 +3,10 @@ obj-$(CONFIG_STM) += stm_core.o stm_core-y := core.o policy.o +obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o + +stm_p_basic-y := p_basic.o + obj-$(CONFIG_STM_DUMMY) += dummy_stm.o obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o diff --git a/drivers/hwtracing/stm/p_basic.c b/drivers/hwtracing/stm/p_basic.c new file mode 100644 index 000000000000..8980a6a5fd6c --- /dev/null +++ b/drivers/hwtracing/stm/p_basic.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Basic framing protocol for STM devices. + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include "stm.h" + +static ssize_t basic_write(struct stm_data *data, struct stm_output *output, + unsigned int chan, const char *buf, size_t count) +{ + unsigned int c = output->channel + chan; + unsigned int m = output->master; + const unsigned char nil = 0; + ssize_t sz; + + sz = stm_data_write(data, m, c, true, buf, count); + if (sz > 0) + data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); + + return sz; +} + +static const struct stm_protocol_driver basic_pdrv = { + .owner = THIS_MODULE, + .name = "p_basic", + .write = basic_write, +}; + +static int basic_stm_init(void) +{ + return stm_register_protocol(&basic_pdrv); +} + +static void basic_stm_exit(void) +{ + stm_unregister_protocol(&basic_pdrv); +} + +module_init(basic_stm_init); +module_exit(basic_stm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Basic STM framing protocol driver"); +MODULE_AUTHOR("Alexander Shishkin "); -- cgit v1.2.3-58-ga151 From 24c7bcb6a74914b529859a07fde27a1a55e13ade Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:57 +0300 Subject: stm class: Switch over to the protocol driver Now that the default framing protocol is factored out into its own driver, switch over to using the driver for writing data. To that end, make the policy code require a valid protocol name (or absence thereof, which is equivalent to "p_basic"). Also, to make transition easier, make stm class request "p_basic" module at initialization time. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/core.c | 34 ++++++++++++++++++++-------------- drivers/hwtracing/stm/policy.c | 3 +-- 2 files changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index b789a5f0688e..93ce3aa740a9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -603,18 +603,21 @@ ssize_t notrace stm_data_write(struct stm_data *data, unsigned int m, } EXPORT_SYMBOL_GPL(stm_data_write); -static ssize_t notrace stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +static ssize_t notrace +stm_write(struct stm_device *stm, struct stm_output *output, + unsigned int chan, const char *buf, size_t count) { - const unsigned char nil = 0; - ssize_t sz; + int err; + + /* stm->pdrv is serialized against policy_mutex */ + if (!stm->pdrv) + return -ENODEV; - sz = stm_data_write(data, master, channel, true, buf, count); - if (sz > 0) - data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, - &nil); + err = stm->pdrv->write(stm->data, output, chan, buf, count); + if (err < 0) + return err; - return sz; + return err; } static ssize_t stm_char_write(struct file *file, const char __user *buf, @@ -659,8 +662,7 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, pm_runtime_get_sync(&stm->dev); - count = stm_write(stm->data, stmf->output.master, stmf->output.channel, - kbuf, count); + count = stm_write(stm, &stmf->output, 0, kbuf, count); pm_runtime_mark_last_busy(&stm->dev); pm_runtime_put_autosuspend(&stm->dev); @@ -1310,9 +1312,7 @@ int notrace stm_source_write(struct stm_source_data *data, stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) - count = stm_write(stm->data, src->output.master, - src->output.channel + chan, - buf, count); + count = stm_write(stm, &src->output, chan, buf, count); else count = -ENODEV; @@ -1342,6 +1342,12 @@ static int __init stm_core_init(void) INIT_LIST_HEAD(&stm_pdrv_head); mutex_init(&stm_pdrv_mutex); + /* + * So as to not confuse existing users with a requirement + * to load yet another module, do it here. + */ + if (IS_ENABLED(CONFIG_STM_PROTO_BASIC)) + (void)request_module_nowait("stm_p_basic"); stm_core_up++; return 0; diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 8bc90432ae69..5e51bed0787b 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -454,8 +454,7 @@ stp_policy_make(struct config_group *group, const char *name) err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); kfree(devname); - /* We don't have any protocol drivers yet */ - if (err != -ENOENT) { + if (err) { stm_put_device(stm); return ERR_PTR(-ENODEV); } -- cgit v1.2.3-58-ga151 From d69d5e83110fedd15ff463ed2d5320ab3dec75f1 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:58 +0300 Subject: stm class: Add MIPI SyS-T protocol support This adds support for MIPI SyS-T protocol as specified in an open standard [1]. In addition to marking message boundaries, it also supports tagging messages with the source UUID, to provide better distinction between trace sources, including payload length and timestamp in the message's metadata. This driver adds attributes to STP policy nodes to control/configure these metadata features. [1] https://www.mipi.org/specifications/sys-t Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/Kconfig | 14 ++ drivers/hwtracing/stm/Makefile | 2 + drivers/hwtracing/stm/p_sys-t.c | 302 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 318 insertions(+) create mode 100644 drivers/hwtracing/stm/p_sys-t.c (limited to 'drivers') diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 262e7891fb97..752dd66742bf 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -26,6 +26,20 @@ config STM_PROTO_BASIC If you want to be able to use the basic protocol or want the backwards compatibility for your existing setup, say Y. +config STM_PROTO_SYS_T + tristate "MIPI SyS-T STM framing protocol driver" + default CONFIG_STM + help + This is an implementation of MIPI SyS-T protocol to be used + over the STP transport. In addition to the data payload, it + also carries additional metadata for time correlation, better + means of trace source identification, etc. + + The receiving side must be able to decode this protocol in + addition to the MIPI STP, in order to extract the data. + + If you don't know what this is, say N. + config STM_DUMMY tristate "Dummy STM driver" help diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index 1571de66eca4..1692fcd29277 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -4,8 +4,10 @@ obj-$(CONFIG_STM) += stm_core.o stm_core-y := core.o policy.o obj-$(CONFIG_STM_PROTO_BASIC) += stm_p_basic.o +obj-$(CONFIG_STM_PROTO_SYS_T) += stm_p_sys-t.o stm_p_basic-y := p_basic.o +stm_p_sys-t-y := p_sys-t.o obj-$(CONFIG_STM_DUMMY) += dummy_stm.o diff --git a/drivers/hwtracing/stm/p_sys-t.c b/drivers/hwtracing/stm/p_sys-t.c new file mode 100644 index 000000000000..ffeb057b777c --- /dev/null +++ b/drivers/hwtracing/stm/p_sys-t.c @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MIPI SyS-T framing protocol for STM devices. + * Copyright (c) 2018, Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include "stm.h" + +enum sys_t_message_type { + MIPI_SYST_TYPE_BUILD = 0, + MIPI_SYST_TYPE_SHORT32, + MIPI_SYST_TYPE_STRING, + MIPI_SYST_TYPE_CATALOG, + MIPI_SYST_TYPE_RAW = 6, + MIPI_SYST_TYPE_SHORT64, + MIPI_SYST_TYPE_CLOCK, +}; + +enum sys_t_message_severity { + MIPI_SYST_SEVERITY_MAX = 0, + MIPI_SYST_SEVERITY_FATAL, + MIPI_SYST_SEVERITY_ERROR, + MIPI_SYST_SEVERITY_WARNING, + MIPI_SYST_SEVERITY_INFO, + MIPI_SYST_SEVERITY_USER1, + MIPI_SYST_SEVERITY_USER2, + MIPI_SYST_SEVERITY_DEBUG, +}; + +enum sys_t_message_build_subtype { + MIPI_SYST_BUILD_ID_COMPACT32 = 0, + MIPI_SYST_BUILD_ID_COMPACT64, + MIPI_SYST_BUILD_ID_LONG, +}; + +enum sys_t_message_clock_subtype { + MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1, +}; + +enum sys_t_message_string_subtype { + MIPI_SYST_STRING_GENERIC = 1, + MIPI_SYST_STRING_FUNCTIONENTER, + MIPI_SYST_STRING_FUNCTIONEXIT, + MIPI_SYST_STRING_INVALIDPARAM = 5, + MIPI_SYST_STRING_ASSERT = 7, + MIPI_SYST_STRING_PRINTF_32 = 11, + MIPI_SYST_STRING_PRINTF_64 = 12, +}; + +#define MIPI_SYST_TYPE(t) ((u32)(MIPI_SYST_TYPE_ ## t)) +#define MIPI_SYST_SEVERITY(s) ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4) +#define MIPI_SYST_OPT_LOC BIT(8) +#define MIPI_SYST_OPT_LEN BIT(9) +#define MIPI_SYST_OPT_CHK BIT(10) +#define MIPI_SYST_OPT_TS BIT(11) +#define MIPI_SYST_UNIT(u) ((u32)(u) << 12) +#define MIPI_SYST_ORIGIN(o) ((u32)(o) << 16) +#define MIPI_SYST_OPT_GUID BIT(23) +#define MIPI_SYST_SUBTYPE(s) ((u32)(MIPI_SYST_ ## s) << 24) +#define MIPI_SYST_UNITLARGE(u) (MIPI_SYST_UNIT(u & 0xf) | \ + MIPI_SYST_ORIGIN(u >> 4)) +#define MIPI_SYST_TYPES(t, s) (MIPI_SYST_TYPE(t) | \ + MIPI_SYST_SUBTYPE(t ## _ ## s)) + +#define DATA_HEADER (MIPI_SYST_TYPES(STRING, GENERIC) | \ + MIPI_SYST_SEVERITY(INFO) | \ + MIPI_SYST_OPT_GUID) + +struct sys_t_policy_node { + uuid_t uuid; + bool do_len; + unsigned long ts_interval; +}; + +struct sys_t_output { + struct sys_t_policy_node node; + unsigned long ts_jiffies; +}; + +static void sys_t_policy_node_init(void *priv) +{ + struct sys_t_policy_node *pn = priv; + + generate_random_uuid(pn->uuid.b); +} + +static int sys_t_output_open(void *priv, struct stm_output *output) +{ + struct sys_t_policy_node *pn = priv; + struct sys_t_output *opriv; + + opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC); + if (!opriv) + return -ENOMEM; + + memcpy(&opriv->node, pn, sizeof(opriv->node)); + output->pdrv_private = opriv; + + return 0; +} + +static void sys_t_output_close(struct stm_output *output) +{ + kfree(output->pdrv_private); +} + +static ssize_t sys_t_policy_uuid_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%pU\n", &pn->uuid); +} + +static ssize_t +sys_t_policy_uuid_store(struct config_item *item, const char *page, + size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + int ret; + + mutex_lock(mutexp); + ret = uuid_parse(page, &pn->uuid); + mutex_unlock(mutexp); + + return ret < 0 ? ret : count; +} + +CONFIGFS_ATTR(sys_t_policy_, uuid); + +static ssize_t sys_t_policy_do_len_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%d\n", pn->do_len); +} + +static ssize_t +sys_t_policy_do_len_store(struct config_item *item, const char *page, + size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + int ret; + + mutex_lock(mutexp); + ret = kstrtobool(page, &pn->do_len); + mutex_unlock(mutexp); + + return ret ? ret : count; +} + +CONFIGFS_ATTR(sys_t_policy_, do_len); + +static ssize_t sys_t_policy_ts_interval_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval)); +} + +static ssize_t +sys_t_policy_ts_interval_store(struct config_item *item, const char *page, + size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + unsigned int ms; + int ret; + + mutex_lock(mutexp); + ret = kstrtouint(page, 10, &ms); + mutex_unlock(mutexp); + + if (!ret) { + pn->ts_interval = msecs_to_jiffies(ms); + return count; + } + + return ret; +} + +CONFIGFS_ATTR(sys_t_policy_, ts_interval); + +static struct configfs_attribute *sys_t_policy_attrs[] = { + &sys_t_policy_attr_uuid, + &sys_t_policy_attr_do_len, + &sys_t_policy_attr_ts_interval, + NULL, +}; + +static inline bool sys_t_need_ts(struct sys_t_output *op) +{ + if (op->node.ts_interval && + time_after(op->ts_jiffies + op->node.ts_interval, jiffies)) { + op->ts_jiffies = jiffies; + + return true; + } + + return false; +} + +static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, + unsigned int chan, const char *buf, size_t count) +{ + struct sys_t_output *op = output->pdrv_private; + unsigned int c = output->channel + chan; + unsigned int m = output->master; + const unsigned char nil = 0; + u32 header = DATA_HEADER; + ssize_t sz; + + /* We require an existing policy node to proceed */ + if (!op) + return -EINVAL; + + if (op->node.do_len) + header |= MIPI_SYST_OPT_LEN; + if (sys_t_need_ts(op)) + header |= MIPI_SYST_OPT_TS; + + /* + * STP framing rules for SyS-T frames: + * * the first packet of the SyS-T frame is timestamped; + * * the last packet is a FLAG. + */ + /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */ + /* HEADER */ + sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, + 4, (u8 *)&header); + if (sz <= 0) + return sz; + + /* GUID */ + sz = stm_data_write(data, m, c, false, op->node.uuid.b, UUID_SIZE); + if (sz <= 0) + return sz; + + /* [LENGTH] */ + if (op->node.do_len) { + u16 length = count; + + sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2, + (u8 *)&length); + if (sz <= 0) + return sz; + } + + /* [TIMESTAMP] */ + if (header & MIPI_SYST_OPT_TS) { + u64 ts = ktime_get_real_ns(); + + sz = stm_data_write(data, m, c, false, &ts, sizeof(ts)); + if (sz <= 0) + return sz; + } + + /* DATA */ + sz = stm_data_write(data, m, c, false, buf, count); + if (sz > 0) + data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); + + return sz; +} + +static const struct stm_protocol_driver sys_t_pdrv = { + .owner = THIS_MODULE, + .name = "p_sys-t", + .priv_sz = sizeof(struct sys_t_policy_node), + .write = sys_t_write, + .policy_attr = sys_t_policy_attrs, + .policy_node_init = sys_t_policy_node_init, + .output_open = sys_t_output_open, + .output_close = sys_t_output_close, +}; + +static int sys_t_stm_init(void) +{ + return stm_register_protocol(&sys_t_pdrv); +} + +static void sys_t_stm_exit(void) +{ + stm_unregister_protocol(&sys_t_pdrv); +} + +module_init(sys_t_stm_init); +module_exit(sys_t_stm_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver"); +MODULE_AUTHOR("Alexander Shishkin "); -- cgit v1.2.3-58-ga151 From 39f10239df751e4ec5190ef851a3e71c052c2539 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:42:59 +0300 Subject: stm class: p_sys-t: Add support for CLOCKSYNC packets This adds support for CLOCKSYNC SyS-T packets, that establish correlation between the transport clock (STP timestamps) and SyS-T timestamps. These packets are sent periodically to allow the decoder to keep both time sources in sync. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/p_sys-t.c | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/p_sys-t.c b/drivers/hwtracing/stm/p_sys-t.c index ffeb057b777c..b178a5495b67 100644 --- a/drivers/hwtracing/stm/p_sys-t.c +++ b/drivers/hwtracing/stm/p_sys-t.c @@ -72,15 +72,20 @@ enum sys_t_message_string_subtype { MIPI_SYST_SEVERITY(INFO) | \ MIPI_SYST_OPT_GUID) +#define CLOCK_SYNC_HEADER (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \ + MIPI_SYST_SEVERITY(MAX)) + struct sys_t_policy_node { uuid_t uuid; bool do_len; unsigned long ts_interval; + unsigned long clocksync_interval; }; struct sys_t_output { struct sys_t_policy_node node; unsigned long ts_jiffies; + unsigned long clocksync_jiffies; }; static void sys_t_policy_node_init(void *priv) @@ -191,10 +196,42 @@ sys_t_policy_ts_interval_store(struct config_item *item, const char *page, CONFIGFS_ATTR(sys_t_policy_, ts_interval); +static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item, + char *page) +{ + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + + return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval)); +} + +static ssize_t +sys_t_policy_clocksync_interval_store(struct config_item *item, + const char *page, size_t count) +{ + struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex; + struct sys_t_policy_node *pn = to_pdrv_policy_node(item); + unsigned int ms; + int ret; + + mutex_lock(mutexp); + ret = kstrtouint(page, 10, &ms); + mutex_unlock(mutexp); + + if (!ret) { + pn->clocksync_interval = msecs_to_jiffies(ms); + return count; + } + + return ret; +} + +CONFIGFS_ATTR(sys_t_policy_, clocksync_interval); + static struct configfs_attribute *sys_t_policy_attrs[] = { &sys_t_policy_attr_uuid, &sys_t_policy_attr_do_len, &sys_t_policy_attr_ts_interval, + &sys_t_policy_attr_clocksync_interval, NULL, }; @@ -210,6 +247,43 @@ static inline bool sys_t_need_ts(struct sys_t_output *op) return false; } +static bool sys_t_need_clock_sync(struct sys_t_output *op) +{ + if (op->node.clocksync_interval && + time_after(op->clocksync_jiffies + op->node.clocksync_interval, + jiffies)) { + op->clocksync_jiffies = jiffies; + + return true; + } + + return false; +} + +static ssize_t +sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c) +{ + u32 header = CLOCK_SYNC_HEADER; + const unsigned char nil = 0; + u64 payload[2]; /* Clock value and frequency */ + ssize_t sz; + + sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED, + 4, (u8 *)&header); + if (sz <= 0) + return sz; + + payload[0] = ktime_get_real_ns(); + payload[1] = NSEC_PER_SEC; + sz = stm_data_write(data, m, c, false, &payload, sizeof(payload)); + if (sz <= 0) + return sz; + + data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil); + + return sizeof(header) + sizeof(payload); +} + static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, unsigned int chan, const char *buf, size_t count) { @@ -224,6 +298,12 @@ static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output, if (!op) return -EINVAL; + if (sys_t_need_clock_sync(op)) { + sz = sys_t_clock_sync(data, m, c); + if (sz <= 0) + return sz; + } + if (op->node.do_len) header |= MIPI_SYST_OPT_LEN; if (sys_t_need_ts(op)) -- cgit v1.2.3-58-ga151 From 6c7e4b6882ad080dad623ba2f4c1a4db578313cb Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:04 +0300 Subject: stm class: heartbeat: Fix whitespace Fix whitespace in the code for better readability, no functional changes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/heartbeat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c index 7db42395e131..3e7df1c0477f 100644 --- a/drivers/hwtracing/stm/heartbeat.c +++ b/drivers/hwtracing/stm/heartbeat.c @@ -76,7 +76,7 @@ static int stm_heartbeat_init(void) goto fail_unregister; stm_heartbeat[i].data.nr_chans = 1; - stm_heartbeat[i].data.link = stm_heartbeat_link; + stm_heartbeat[i].data.link = stm_heartbeat_link; stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); -- cgit v1.2.3-58-ga151 From a23bbec205b9b9b5f75af1811fe89d0598104b88 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 5 Oct 2018 15:43:06 +0300 Subject: stm class: Use memcat_p() Instead of a local copy, use the memcat_p() helper to merge policy node attributes. Signed-off-by: Alexander Shishkin Tested-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman --- drivers/hwtracing/stm/policy.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 5e51bed0787b..0910ec807187 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -202,33 +202,6 @@ static struct configfs_attribute *stp_policy_node_attrs[] = { static const struct config_item_type stp_policy_type; static const struct config_item_type stp_policy_node_type; -/* lifted from arch/x86/events/core.c */ -static struct configfs_attribute **merge_attr(struct configfs_attribute **a, struct configfs_attribute **b) -{ - struct configfs_attribute **new; - int j, i; - - for (j = 0; a[j]; j++) - ; - for (i = 0; b[i]; i++) - j++; - j++; - - new = kmalloc_array(j, sizeof(struct configfs_attribute *), - GFP_KERNEL); - if (!new) - return NULL; - - j = 0; - for (i = 0; a[i]; i++) - new[j++] = a[i]; - for (i = 0; b[i]; i++) - new[j++] = b[i]; - new[j] = NULL; - - return new; -} - const struct config_item_type * get_policy_node_type(struct configfs_attribute **attrs) { @@ -240,7 +213,7 @@ get_policy_node_type(struct configfs_attribute **attrs) if (!type) return NULL; - merged = merge_attr(stp_policy_node_attrs, attrs); + merged = memcat_p(stp_policy_node_attrs, attrs); if (!merged) { kfree(type); return NULL; -- cgit v1.2.3-58-ga151 From e862faa968400206aae3bf8de0d4beb766498657 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 6 Oct 2018 22:56:58 +0100 Subject: misc: mic: scif: remove redundant check on ret < 0 The check for ret < 0 is redundant as any places prior to this point where ret is set to an error value the code will exit out of the loop to the error exit label 'err'. Remove this redundant dead code. Detected by CoverityScan, CID#1339528 ("Logically dead code") Signed-off-by: Colin Ian King Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 03af8b5b7b0d..2d76c200d072 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -1035,8 +1035,6 @@ scif_rma_list_dma_copy_unaligned(struct scif_copy_work *work, } dma_async_issue_pending(chan); } - if (ret < 0) - goto err; offset += loop_len; temp += loop_len; temp_phys += loop_len; -- cgit v1.2.3-58-ga151 From 3dac3583bf1a61db6aaf31dfd752c677a4400afd Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Thu, 4 Oct 2018 13:02:53 +0800 Subject: misc: cxl: Fix possible null pointer dereference It is not safe to dereference an object before a null test. It is not needed and just remove them. Ftrace can be used instead. Signed-off-by: zhong jiang Acked-by: Andrew Donnellan Acked-by: Frederic Barrat Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/guest.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/cxl/guest.c b/drivers/misc/cxl/guest.c index 3bc0c15d4d85..5d28d9e454f5 100644 --- a/drivers/misc/cxl/guest.c +++ b/drivers/misc/cxl/guest.c @@ -1018,8 +1018,6 @@ err1: void cxl_guest_remove_afu(struct cxl_afu *afu) { - pr_devel("in %s - AFU(%d)\n", __func__, afu->slice); - if (!afu) return; -- cgit v1.2.3-58-ga151 From badcdff107cbfd7fef5f089260177efc56fe1a9f Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 3 Oct 2018 18:47:04 +0100 Subject: nvmem: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Srinivas Kandagatla Signed-off-by: Rob Herring [srinivas: rebased on top of next] Signed-off-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index cc815bb2eebd..b4c09bc20b67 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -298,6 +298,7 @@ static void nvmem_cell_drop(struct nvmem_cell *cell) mutex_lock(&nvmem_mutex); list_del(&cell->node); mutex_unlock(&nvmem_mutex); + kfree(cell->name); kfree(cell); } @@ -547,7 +548,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) cell->nvmem = nvmem; cell->offset = be32_to_cpup(addr++); cell->bytes = be32_to_cpup(addr); - cell->name = child->name; + cell->name = kasprintf(GFP_KERNEL, "%pOFn", child); addr = of_get_property(child, "bits", &len); if (addr && len == (2 * sizeof(u32))) { @@ -564,6 +565,7 @@ static int nvmem_add_cells_from_of(struct nvmem_device *nvmem) dev_err(dev, "cell %s unaligned to nvmem stride %d\n", cell->name, nvmem->stride); /* Cells already added will be freed later. */ + kfree(cell->name); kfree(cell); return -EINVAL; } -- cgit v1.2.3-58-ga151 From cccb3b19e762edc8ef0481be506967555cb9e317 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Oct 2018 09:31:11 +0200 Subject: nvmem: fix nvmem_cell_get_from_lookup() We check if the pointer returned by __nvmem_device_get() is not NULL while we should actually check if it is not IS_ERR(nvmem). Fix it. While we're at it: fix the next error path where we should assign an error value to cell before returning. Signed-off-by: Bartosz Golaszewski Reported-by: Stephen Rothwell Acked-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index b4c09bc20b67..31ca04d46c1c 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -953,9 +953,9 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) (strcmp(lookup->con_id, con_id) == 0)) { /* This is the right entry. */ nvmem = __nvmem_device_get(NULL, lookup->nvmem_name); - if (!nvmem) { + if (IS_ERR(nvmem)) { /* Provider may not be registered yet. */ - cell = ERR_PTR(-EPROBE_DEFER); + cell = ERR_CAST(nvmem); goto out; } @@ -963,6 +963,7 @@ nvmem_cell_get_from_lookup(struct device *dev, const char *con_id) lookup->cell_name); if (!cell) { __nvmem_device_put(nvmem); + cell = ERR_PTR(-ENOENT); goto out; } } -- cgit v1.2.3-58-ga151 From 098ec84f1ad2f7b9a4c88bf61fab021ac2876173 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 3 Oct 2018 09:24:30 +0200 Subject: nvmem: lpc18xx_eeprom: remove unused variable Remove a variable that's no longer used from lpc18xx_eeprom_remove(). Signed-off-by: Bartosz Golaszewski Reported-by: Stephen Rothwell Acked-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/lpc18xx_eeprom.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/nvmem/lpc18xx_eeprom.c b/drivers/nvmem/lpc18xx_eeprom.c index b42dbaddb419..66cff1e2147a 100644 --- a/drivers/nvmem/lpc18xx_eeprom.c +++ b/drivers/nvmem/lpc18xx_eeprom.c @@ -255,7 +255,6 @@ err_clk: static int lpc18xx_eeprom_remove(struct platform_device *pdev) { struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev); - int ret; clk_disable_unprepare(eeprom->clk); -- cgit v1.2.3-58-ga151 From 3c53e2352a9bf87128e7a8ddb69c7543941a3092 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Oct 2018 23:11:12 +0200 Subject: nvmem: hide unused nvmem_find_cell_by_index function nvmem_find_cell_by_index() is only called from inside an #ifdef, so we get a build warning without CONFIG_OF: drivers/nvmem/core.c:496:1: error: 'nvmem_find_cell_by_index' defined but not used [-Werror=unused-function] Move it into the same #ifdef as the caller to avoid the warning. Fixes: e888d445ac33 ("nvmem: resolve cells from DT at registration time") Signed-off-by: Arnd Bergmann Acked-by: Srinivas Kandagatla Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index 31ca04d46c1c..9b18ce90f907 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -493,22 +493,6 @@ out: return rval; } -static struct nvmem_cell * -nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) -{ - struct nvmem_cell *cell = NULL; - int i = 0; - - mutex_lock(&nvmem_mutex); - list_for_each_entry(cell, &nvmem->cells, node) { - if (index == i++) - break; - } - mutex_unlock(&nvmem_mutex); - - return cell; -} - static struct nvmem_cell * nvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id) { @@ -975,6 +959,22 @@ out: } #if IS_ENABLED(CONFIG_OF) +static struct nvmem_cell * +nvmem_find_cell_by_index(struct nvmem_device *nvmem, int index) +{ + struct nvmem_cell *cell = NULL; + int i = 0; + + mutex_lock(&nvmem_mutex); + list_for_each_entry(cell, &nvmem->cells, node) { + if (index == i++) + break; + } + mutex_unlock(&nvmem_mutex); + + return cell; +} + /** * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id * -- cgit v1.2.3-58-ga151 From 655603de68469adaff16842ac17a5aec9c9ce89b Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Fri, 12 Oct 2018 10:04:45 -0600 Subject: gsmi: Fix bug in append_to_eventlog sysfs handler The sysfs handler should return the number of bytes consumed, which in the case of a successful write is the entire buffer. Also fix a bug where param.data_len was being set to (count - (2 * sizeof(u32))) instead of just (count - sizeof(u32)). The latter is correct because we skip over the leading u32 which is our param.type, but we were also incorrectly subtracting sizeof(u32) on the line where we were actually setting param.data_len: param.data_len = count - sizeof(u32); This meant that for our example event.kernel_software_watchdog with total length 10 bytes, param.data_len was just 2 prior to this change. To test, successfully append an event to the log with gsmi sysfs. This sample event is for a "Kernel Software Watchdog" > xxd -g 1 event.kernel_software_watchdog 0000000: 01 00 00 00 ad de 06 00 00 00 > cat event.kernel_software_watchdog > /sys/firmware/gsmi/append_to_eventlog > mosys eventlog list | tail -1 14 | 2012-06-25 10:14:14 | Kernl Event | Software Watchdog Signed-off-by: Duncan Laurie Reviewed-by: Vadim Bendebury Reviewed-by: Stefan Reinauer Signed-off-by: Furquan Shaikh Tested-by: Furquan Shaikh Reviewed-by: Aaron Durbin Reviewed-by: Justin TerAvest [zwisler: updated changelog for 2nd bug fix and upstream] Signed-off-by: Ross Zwisler Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/gsmi.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 734146eec1b9..3677d511f4c4 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -480,11 +480,10 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, if (count < sizeof(u32)) return -EINVAL; param.type = *(u32 *)buf; - count -= sizeof(u32); buf += sizeof(u32); /* The remaining buffer is the data payload */ - if (count > gsmi_dev.data_buf->length) + if ((count - sizeof(u32)) > gsmi_dev.data_buf->length) return -EINVAL; param.data_len = count - sizeof(u32); @@ -504,7 +503,7 @@ static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, spin_unlock_irqrestore(&gsmi_dev.lock, flags); - return rc; + return (rc == 0) ? count : rc; } -- cgit v1.2.3-58-ga151 From 255d7447cf92821ded63079ffe4019d9f0fa0a6c Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Fri, 12 Oct 2018 10:04:46 -0600 Subject: gsmi: Add coreboot to list of matching BIOS vendors In order to use this coreboot needs board support for: CONFIG_ELOG=y CONFIG_ELOG_GSMI=y And the kernel driver needs enabled: CONFIG_GOOGLE_GSMI=y To test, verify that clean shutdown event is added to the log: > mosys eventlog list | grep 'Clean Shutdown' 11 | 2012-06-25 09:49:24 | Kernl Event | Clean Shutdown Signed-off-by: Duncan Laurie Reviewed-by: Vadim Bendebury Reviewed-by: Stefan Reinauer Signed-off-by: Furquan Shaikh Tested-by: Furquan Shaikh Reviewed-by: Aaron Durbin Reviewed-by: Justin TerAvest [zwisler: update changelog for upstream] Signed-off-by: Ross Zwisler Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/gsmi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 3677d511f4c4..bf86d3c9bbb9 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -715,6 +715,12 @@ static const struct dmi_system_id gsmi_dmi_table[] __initconst = { DMI_MATCH(DMI_BOARD_VENDOR, "Google, Inc."), }, }, + { + .ident = "Coreboot Firmware", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), + }, + }, {} }; MODULE_DEVICE_TABLE(dmi, gsmi_dmi_table); -- cgit v1.2.3-58-ga151 From d31655ba89575a2d1559a345642338eecd49c96d Mon Sep 17 00:00:00 2001 From: Duncan Laurie Date: Fri, 12 Oct 2018 10:04:47 -0600 Subject: gsmi: Remove autoselected dependency on EFI and EFI_VARS Instead of selecting EFI and EFI_VARS automatically when GSMI is enabled let that portion of the driver be conditionally compiled if EFI and EFI_VARS are enabled. This allows the rest of the driver (specifically event log) to be used if EFI_VARS is not enabled. To test: 1) verify that EFI_VARS is not automatically selected when CONFIG_GOOGLE_GSMI is enabled 2) verify that the kernel boots on Link and that GSMI event log is still available and functional 3) specifically boot the kernel on Alex to ensure it does not try to load efivars and that gsmi also does not load because it is not in the supported DMI table Signed-off-by: Duncan Laurie Reviewed-by: Olof Johansson Signed-off-by: Benson Leung Signed-off-by: Ben Zhang Signed-off-by: Filipe Brandenburger Signed-off-by: Furquan Shaikh Tested-by: Furquan Shaikh Reviewed-by: Aaron Durbin [zwisler: update changelog for upstream] Signed-off-by: Ross Zwisler Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/Kconfig | 6 +++--- drivers/firmware/google/gsmi.c | 16 ++++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 16564cfa47ef..91a0404affe2 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -10,12 +10,12 @@ if GOOGLE_FIRMWARE config GOOGLE_SMI tristate "SMI interface for Google platforms" - depends on X86 && ACPI && DMI && EFI - select EFI_VARS + depends on X86 && ACPI && DMI help Say Y here if you want to enable SMI callbacks for Google platforms. This provides an interface for writing to and - clearing the EFI event log and reading and writing NVRAM + clearing the event log. If EFI_VARS is also enabled this + driver provides an interface for reading and writing NVRAM variables. config GOOGLE_COREBOOT_TABLE diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index bf86d3c9bbb9..170996764edd 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -289,6 +289,10 @@ static int gsmi_exec(u8 func, u8 sub) return rc; } +#ifdef CONFIG_EFI_VARS + +static struct efivars efivars; + static efi_status_t gsmi_get_variable(efi_char16_t *name, efi_guid_t *vendor, u32 *attr, unsigned long *data_size, @@ -466,6 +470,8 @@ static const struct efivar_operations efivar_ops = { .get_next_variable = gsmi_get_next_variable, }; +#endif /* CONFIG_EFI_VARS */ + static ssize_t eventlog_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) @@ -767,7 +773,6 @@ static __init int gsmi_system_valid(void) } static struct kobject *gsmi_kobj; -static struct efivars efivars; static const struct platform_device_info gsmi_dev_info = { .name = "gsmi", @@ -891,11 +896,14 @@ static __init int gsmi_init(void) goto out_remove_bin_file; } +#ifdef CONFIG_EFI_VARS ret = efivars_register(&efivars, &efivar_ops, gsmi_kobj); if (ret) { printk(KERN_INFO "gsmi: Failed to register efivars\n"); - goto out_remove_sysfs_files; + sysfs_remove_files(gsmi_kobj, gsmi_attrs); + goto out_remove_bin_file; } +#endif register_reboot_notifier(&gsmi_reboot_notifier); register_die_notifier(&gsmi_die_notifier); @@ -906,8 +914,6 @@ static __init int gsmi_init(void) return 0; -out_remove_sysfs_files: - sysfs_remove_files(gsmi_kobj, gsmi_attrs); out_remove_bin_file: sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); out_err: @@ -927,7 +933,9 @@ static void __exit gsmi_exit(void) unregister_die_notifier(&gsmi_die_notifier); atomic_notifier_chain_unregister(&panic_notifier_list, &gsmi_panic_notifier); +#ifdef CONFIG_EFI_VARS efivars_unregister(&efivars); +#endif sysfs_remove_files(gsmi_kobj, gsmi_attrs); sysfs_remove_bin_file(gsmi_kobj, &eventlog_bin_attr); -- cgit v1.2.3-58-ga151 From 8942b2d5094b01e4509f0118a7940bb07350e128 Mon Sep 17 00:00:00 2001 From: Furquan Shaikh Date: Fri, 12 Oct 2018 10:04:48 -0600 Subject: gsmi: Add GSMI commands to log S0ix info Add new GSMI commands (GSMI_CMD_LOG_S0IX_SUSPEND = 0xa, GSMI_CMD_LOG_S0IX_RESUME = 0xb) that allow firmware to log any information during S0ix suspend/resume paths. Traditional ACPI suspend S3 involves BIOS both during the suspend and the resume paths. However, modern suspend type like S0ix does not involve firmware on either of the paths. This command gives the firmware an opportunity to log any required information about the suspend and resume operations e.g. wake sources. Additionally, this change adds a module parameter to allow platforms to specifically enable S0ix logging if required. This prevents any other platforms from unnecessarily making a GSMI call which could have any side-effects. Tested by verifying that wake sources are correctly logged in eventlog. Signed-off-by: Furquan Shaikh Reviewed-by: Aaron Durbin Reviewed-by: Rajat Jain Signed-off-by: Furquan Shaikh Tested-by: Furquan Shaikh Reviewed-by: Aaron Durbin [zwisler: update changelog for upstream] Signed-off-by: Ross Zwisler Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/gsmi.c | 93 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 170996764edd..82ce1e6d261e 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -29,6 +29,7 @@ #include #include #include +#include #define GSMI_SHUTDOWN_CLEAN 0 /* Clean Shutdown */ /* TODO(mikew@google.com): Tie in HARDLOCKUP_DETECTOR with NMIWDT */ @@ -70,6 +71,8 @@ #define GSMI_CMD_SET_NVRAM_VAR 0x03 #define GSMI_CMD_SET_EVENT_LOG 0x08 #define GSMI_CMD_CLEAR_EVENT_LOG 0x09 +#define GSMI_CMD_LOG_S0IX_SUSPEND 0x0a +#define GSMI_CMD_LOG_S0IX_RESUME 0x0b #define GSMI_CMD_CLEAR_CONFIG 0x20 #define GSMI_CMD_HANDSHAKE_TYPE 0xC1 @@ -122,7 +125,6 @@ struct gsmi_log_entry_type_1 { u32 instance; } __packed; - /* * Some platforms don't have explicit SMI handshake * and need to wait for SMI to complete. @@ -133,6 +135,15 @@ module_param(spincount, uint, 0600); MODULE_PARM_DESC(spincount, "The number of loop iterations to use when using the spin handshake."); +/* + * Platforms might not support S0ix logging in their GSMI handlers. In order to + * avoid any side-effects of generating an SMI for S0ix logging, use the S0ix + * related GSMI commands only for those platforms that explicitly enable this + * option. + */ +static bool s0ix_logging_enable; +module_param(s0ix_logging_enable, bool, 0600); + static struct gsmi_buf *gsmi_buf_alloc(void) { struct gsmi_buf *smibuf; @@ -781,6 +792,78 @@ static const struct platform_device_info gsmi_dev_info = { .dma_mask = DMA_BIT_MASK(32), }; +#ifdef CONFIG_PM +static void gsmi_log_s0ix_info(u8 cmd) +{ + unsigned long flags; + + /* + * If platform has not enabled S0ix logging, then no action is + * necessary. + */ + if (!s0ix_logging_enable) + return; + + spin_lock_irqsave(&gsmi_dev.lock, flags); + + memset(gsmi_dev.param_buf->start, 0, gsmi_dev.param_buf->length); + + gsmi_exec(GSMI_CALLBACK, cmd); + + spin_unlock_irqrestore(&gsmi_dev.lock, flags); +} + +static int gsmi_log_s0ix_suspend(struct device *dev) +{ + /* + * If system is not suspending via firmware using the standard ACPI Sx + * types, then make a GSMI call to log the suspend info. + */ + if (!pm_suspend_via_firmware()) + gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_SUSPEND); + + /* + * Always return success, since we do not want suspend + * to fail just because of logging failure. + */ + return 0; +} + +static int gsmi_log_s0ix_resume(struct device *dev) +{ + /* + * If system did not resume via firmware, then make a GSMI call to log + * the resume info and wake source. + */ + if (!pm_resume_via_firmware()) + gsmi_log_s0ix_info(GSMI_CMD_LOG_S0IX_RESUME); + + /* + * Always return success, since we do not want resume + * to fail just because of logging failure. + */ + return 0; +} + +static const struct dev_pm_ops gsmi_pm_ops = { + .suspend_noirq = gsmi_log_s0ix_suspend, + .resume_noirq = gsmi_log_s0ix_resume, +}; + +static int gsmi_platform_driver_probe(struct platform_device *dev) +{ + return 0; +} + +static struct platform_driver gsmi_driver_info = { + .driver = { + .name = "gsmi", + .pm = &gsmi_pm_ops, + }, + .probe = gsmi_platform_driver_probe, +}; +#endif + static __init int gsmi_init(void) { unsigned long flags; @@ -792,6 +875,14 @@ static __init int gsmi_init(void) gsmi_dev.smi_cmd = acpi_gbl_FADT.smi_command; +#ifdef CONFIG_PM + ret = platform_driver_register(&gsmi_driver_info); + if (unlikely(ret)) { + printk(KERN_ERR "gsmi: unable to register platform driver\n"); + return ret; + } +#endif + /* register device */ gsmi_dev.pdev = platform_device_register_full(&gsmi_dev_info); if (IS_ERR(gsmi_dev.pdev)) { -- cgit v1.2.3-58-ga151 From c394cc3b470c0adfee2dff4b8b2a907a73a0ee81 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Wed, 3 Oct 2018 10:17:06 -0700 Subject: platform: goldfish: pipe: Move the file-scope goldfish_interrupt_tasklet variable into the driver state This is a series of patches to move mutable file-scope variables into the driver state. This change will help to introduce another version of the pipe driver (with different state) for the older host interface or having several instances of this device. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 56665e879e5a..ba9aede17d57 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -208,6 +208,9 @@ struct goldfish_pipe_dev { int irq; int version; unsigned char __iomem *base; + + /* an irq tasklet to run goldfish_interrupt_task */ + struct tasklet_struct irq_tasklet; }; static struct goldfish_pipe_dev goldfish_pipe_dev; @@ -582,14 +585,14 @@ static struct goldfish_pipe *signalled_pipes_pop_front( return pipe; } -static void goldfish_interrupt_task(unsigned long unused) +static void goldfish_interrupt_task(unsigned long dev_addr) { /* Iterate over the signalled pipes and wake them one by one */ + struct goldfish_pipe_dev *dev = (struct goldfish_pipe_dev *)dev_addr; struct goldfish_pipe *pipe; int wakes; - while ((pipe = signalled_pipes_pop_front(&goldfish_pipe_dev, &wakes)) != - NULL) { + while ((pipe = signalled_pipes_pop_front(dev, &wakes)) != NULL) { if (wakes & PIPE_WAKE_CLOSED) { pipe->flags = 1 << BIT_CLOSED_ON_HOST; } else { @@ -605,7 +608,6 @@ static void goldfish_interrupt_task(unsigned long unused) wake_up_interruptible(&pipe->wake_queue); } } -static DECLARE_TASKLET(goldfish_interrupt_tasklet, goldfish_interrupt_task, 0); /* * The general idea of the interrupt handling: @@ -648,7 +650,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) spin_unlock_irqrestore(&dev->lock, flags); - tasklet_schedule(&goldfish_interrupt_tasklet); + tasklet_schedule(&dev->irq_tasklet); return IRQ_HANDLED; } @@ -800,9 +802,14 @@ static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth) static int goldfish_pipe_device_init(struct platform_device *pdev) { struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; - int err = devm_request_irq(&pdev->dev, dev->irq, - goldfish_pipe_interrupt, - IRQF_SHARED, "goldfish_pipe", dev); + int err; + + tasklet_init(&dev->irq_tasklet, &goldfish_interrupt_task, + (unsigned long)dev); + + err = devm_request_irq(&pdev->dev, dev->irq, + goldfish_pipe_interrupt, + IRQF_SHARED, "goldfish_pipe", dev); if (err) { dev_err(&pdev->dev, "unable to allocate IRQ for v2\n"); return err; @@ -854,6 +861,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) static void goldfish_pipe_device_deinit(struct platform_device *pdev) { misc_deregister(&goldfish_pipe_miscdev); + tasklet_kill(&goldfish_pipe_dev.irq_tasklet); kfree(goldfish_pipe_dev.pipes); free_page((unsigned long)goldfish_pipe_dev.buffers); } -- cgit v1.2.3-58-ga151 From 43c2cc2864bca0cceff331f27fe762fcf0d804b2 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Wed, 3 Oct 2018 10:17:07 -0700 Subject: platform: goldfish: pipe: Move the file-scope goldfish_pipe_miscdev variable into the driver state This is a series of patches to move mutable file-scope variables into the driver state. This change will help to introduce another version of the pipe driver (with different state) for the older host interface or having several instances of this device. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index ba9aede17d57..8ca709b45e1f 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -211,6 +211,8 @@ struct goldfish_pipe_dev { /* an irq tasklet to run goldfish_interrupt_task */ struct tasklet_struct irq_tasklet; + + struct miscdevice miscdev; }; static struct goldfish_pipe_dev goldfish_pipe_dev; @@ -785,11 +787,14 @@ static const struct file_operations goldfish_pipe_fops = { .release = goldfish_pipe_release, }; -static struct miscdevice goldfish_pipe_miscdev = { - .minor = MISC_DYNAMIC_MINOR, - .name = "goldfish_pipe", - .fops = &goldfish_pipe_fops, -}; +static void init_miscdevice(struct miscdevice *miscdev) +{ + memset(miscdev, 0, sizeof(*miscdev)); + + miscdev->minor = MISC_DYNAMIC_MINOR; + miscdev->name = "goldfish_pipe"; + miscdev->fops = &goldfish_pipe_fops; +} static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth) { @@ -815,7 +820,8 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) return err; } - err = misc_register(&goldfish_pipe_miscdev); + init_miscdevice(&dev->miscdev); + err = misc_register(&dev->miscdev); if (err) { dev_err(&pdev->dev, "unable to register v2 device\n"); return err; @@ -860,7 +866,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) static void goldfish_pipe_device_deinit(struct platform_device *pdev) { - misc_deregister(&goldfish_pipe_miscdev); + misc_deregister(&goldfish_pipe_dev.miscdev); tasklet_kill(&goldfish_pipe_dev.irq_tasklet); kfree(goldfish_pipe_dev.pipes); free_page((unsigned long)goldfish_pipe_dev.buffers); -- cgit v1.2.3-58-ga151 From 08360e26f2a8eba0105ca7cd598547a1749b1c68 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Wed, 3 Oct 2018 10:17:08 -0700 Subject: platform: goldfish: pipe: Move the file-scope goldfish_pipe_dev variable into the driver state This is the last patch in the series of patches to move file-scope variables into the driver state. This change will help to introduce another version of the pipe driver (with different state) for the older host interface or having several instances of this device. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 66 +++++++++++++++++-------------- 1 file changed, 37 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 8ca709b45e1f..4013832f38fb 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -169,6 +169,9 @@ struct goldfish_pipe { * waiting to be awoken. */ struct goldfish_pipe_dev { + /* A magic number to check if this is an instance of this struct */ + void *magic; + /* * Global device spinlock. Protects the following members: * - pipes, pipes_capacity @@ -215,8 +218,6 @@ struct goldfish_pipe_dev { struct miscdevice miscdev; }; -static struct goldfish_pipe_dev goldfish_pipe_dev; - static int goldfish_pipe_cmd_locked(struct goldfish_pipe *pipe, enum PipeCmdCode cmd) { @@ -611,6 +612,9 @@ static void goldfish_interrupt_task(unsigned long dev_addr) } } +static void goldfish_pipe_device_deinit(struct platform_device *pdev, + struct goldfish_pipe_dev *dev); + /* * The general idea of the interrupt handling: * @@ -631,7 +635,7 @@ static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) unsigned long flags; struct goldfish_pipe_dev *dev = dev_id; - if (dev != &goldfish_pipe_dev) + if (dev->magic != &goldfish_pipe_device_deinit) return IRQ_NONE; /* Request the signalled pipes from the device */ @@ -683,6 +687,14 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) return id; } +/* A helper function to get the instance of goldfish_pipe_dev from file */ +static struct goldfish_pipe_dev *to_goldfish_pipe_dev(struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + + return container_of(miscdev, struct goldfish_pipe_dev, miscdev); +} + /** * goldfish_pipe_open - open a channel to the AVD * @inode: inode of device @@ -696,7 +708,7 @@ static int get_free_pipe_id_locked(struct goldfish_pipe_dev *dev) */ static int goldfish_pipe_open(struct inode *inode, struct file *file) { - struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; + struct goldfish_pipe_dev *dev = to_goldfish_pipe_dev(file); unsigned long flags; int id; int status; @@ -804,9 +816,9 @@ static void write_pa_addr(void *addr, void __iomem *portl, void __iomem *porth) writel(lower_32_bits(paddr), portl); } -static int goldfish_pipe_device_init(struct platform_device *pdev) +static int goldfish_pipe_device_init(struct platform_device *pdev, + struct goldfish_pipe_dev *dev) { - struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; int err; tasklet_init(&dev->irq_tasklet, &goldfish_interrupt_task, @@ -861,26 +873,29 @@ static int goldfish_pipe_device_init(struct platform_device *pdev) dev->base + PIPE_REG_OPEN_BUFFER, dev->base + PIPE_REG_OPEN_BUFFER_HIGH); + platform_set_drvdata(pdev, dev); return 0; } -static void goldfish_pipe_device_deinit(struct platform_device *pdev) +static void goldfish_pipe_device_deinit(struct platform_device *pdev, + struct goldfish_pipe_dev *dev) { - misc_deregister(&goldfish_pipe_dev.miscdev); - tasklet_kill(&goldfish_pipe_dev.irq_tasklet); - kfree(goldfish_pipe_dev.pipes); - free_page((unsigned long)goldfish_pipe_dev.buffers); + misc_deregister(&dev->miscdev); + tasklet_kill(&dev->irq_tasklet); + kfree(dev->pipes); + free_page((unsigned long)dev->buffers); } static int goldfish_pipe_probe(struct platform_device *pdev) { - int err; struct resource *r; - struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; + struct goldfish_pipe_dev *dev; - /* not thread safe, but this should not happen */ - WARN_ON(dev->base); + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + dev->magic = &goldfish_pipe_device_deinit; spin_lock_init(&dev->lock); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -895,10 +910,9 @@ static int goldfish_pipe_probe(struct platform_device *pdev) } r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!r) { - err = -EINVAL; - goto error; - } + if (!r) + return -EINVAL; + dev->irq = r->start; /* @@ -913,20 +927,14 @@ static int goldfish_pipe_probe(struct platform_device *pdev) if (WARN_ON(dev->version < PIPE_CURRENT_DEVICE_VERSION)) return -EINVAL; - err = goldfish_pipe_device_init(pdev); - if (!err) - return 0; - -error: - dev->base = NULL; - return err; + return goldfish_pipe_device_init(pdev, dev); } static int goldfish_pipe_remove(struct platform_device *pdev) { - struct goldfish_pipe_dev *dev = &goldfish_pipe_dev; - goldfish_pipe_device_deinit(pdev); - dev->base = NULL; + struct goldfish_pipe_dev *dev = platform_get_drvdata(pdev); + + goldfish_pipe_device_deinit(pdev, dev); return 0; } -- cgit v1.2.3-58-ga151 From 60a6e5233fcbe82a89b5b83b95b5d87e04b3be60 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Wed, 3 Oct 2018 10:17:09 -0700 Subject: platform: goldfish: pipe: Call misc_deregister if init fails Undo effects of misc_register if driver's init fails after misc_register. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index 4013832f38fb..c386aaf40034 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -844,8 +844,10 @@ static int goldfish_pipe_device_init(struct platform_device *pdev, dev->pipes_capacity = INITIAL_PIPES_CAPACITY; dev->pipes = kcalloc(dev->pipes_capacity, sizeof(*dev->pipes), GFP_KERNEL); - if (!dev->pipes) + if (!dev->pipes) { + misc_deregister(&dev->miscdev); return -ENOMEM; + } /* * We're going to pass two buffers, open_command_params and @@ -858,6 +860,7 @@ static int goldfish_pipe_device_init(struct platform_device *pdev, __get_free_page(GFP_KERNEL); if (!dev->buffers) { kfree(dev->pipes); + misc_deregister(&dev->miscdev); return -ENOMEM; } -- cgit v1.2.3-58-ga151 From e6fb3193307dc6bd1de9354a7937385e73fd06cb Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Wed, 3 Oct 2018 10:17:10 -0700 Subject: platform: goldfish: pipe: Remove redundant casting This casting is not required. Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index c386aaf40034..bc431f04c4cf 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -925,7 +925,7 @@ static int goldfish_pipe_probe(struct platform_device *pdev) * reading device version back: this allows the host implementation to * detect the old driver (if there was no version write before read). */ - writel((u32)PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION); + writel(PIPE_DRIVER_VERSION, dev->base + PIPE_REG_VERSION); dev->version = readl(dev->base + PIPE_REG_VERSION); if (WARN_ON(dev->version < PIPE_CURRENT_DEVICE_VERSION)) return -EINVAL; -- cgit v1.2.3-58-ga151 From 6d7d725b2e62b00f6b0b36639ba597efe9cc8ea9 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Wed, 3 Oct 2018 10:17:13 -0700 Subject: platform: goldfish: pipe: Add a blank line to separate varibles and code checkpacth: Missing a blank line after declarations Signed-off-by: Roman Kiryanov Signed-off-by: Greg Kroah-Hartman --- drivers/platform/goldfish/goldfish_pipe.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c index bc431f04c4cf..7c639006252e 100644 --- a/drivers/platform/goldfish/goldfish_pipe.c +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -715,6 +715,7 @@ static int goldfish_pipe_open(struct inode *inode, struct file *file) /* Allocate new pipe kernel object */ struct goldfish_pipe *pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); + if (!pipe) return -ENOMEM; -- cgit v1.2.3-58-ga151 From 6b995f4eec34745f6cb20d66d5277611f0b3c3fa Mon Sep 17 00:00:00 2001 From: Wenwen Wang Date: Wed, 10 Oct 2018 18:38:28 -0500 Subject: misc: mic: fix a DMA pool free failure In _scif_prog_signal(), the boolean variable 'x100' is used to indicate whether the MIC Coprocessor is X100. If 'x100' is true, the status descriptor will be used to write the value to the destination. Otherwise, a DMA pool will be allocated for this purpose. Specifically, if the DMA pool is allocated successfully, two memory addresses will be returned. One is for the CPU and the other is for the device to access the DMA pool. The former is stored to the variable 'status' and the latter is stored to the variable 'src'. After the allocation, the address in 'src' is saved to 'status->src_dma_addr', which is actually in the DMA pool, and 'src' is then modified. Later on, if an error occurs, the execution flow will transfer to the label 'dma_fail', which will check 'x100' and free up the allocated DMA pool if 'x100' is false. The point here is that 'status->src_dma_addr' is used for freeing up the DMA pool. As mentioned before, 'status->src_dma_addr' is in the DMA pool. And thus, the device is able to modify this data. This can potentially cause failures when freeing up the DMA pool because of the modified device address. This patch avoids the above issue by using the variable 'src' (with necessary calculation) to free up the DMA pool. Signed-off-by: Wenwen Wang Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_fence.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/misc/mic/scif/scif_fence.c b/drivers/misc/mic/scif/scif_fence.c index cac3bcc308a7..7bb929f05d85 100644 --- a/drivers/misc/mic/scif/scif_fence.c +++ b/drivers/misc/mic/scif/scif_fence.c @@ -272,7 +272,7 @@ static int _scif_prog_signal(scif_epd_t epd, dma_addr_t dst, u64 val) dma_fail: if (!x100) dma_pool_free(ep->remote_dev->signal_pool, status, - status->src_dma_addr); + src - offsetof(struct scif_status, val)); alloc_fail: return err; } -- cgit v1.2.3-58-ga151 From 3c3f76248e79d69f3b8ceaa31b68051dad45e92b Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 12 Oct 2018 03:25:56 +0000 Subject: misc: mic: scif: remove set but not used variables 'src_dma_addr, dst_dma_addr' Fixes gcc '-Wunused-but-set-variable' warning: drivers/misc/mic/scif/scif_dma.c: In function 'scif_rma_list_dma_copy_wrapper': drivers/misc/mic/scif/scif_dma.c:1558:27: warning: variable 'dst_dma_addr' set but not used [-Wunused-but-set-variable] drivers/misc/mic/scif/scif_dma.c:1558:13: warning: variable 'src_dma_addr' set but not used [-Wunused-but-set-variable] They never used since introduction in commit 7cc31cd27752 ("misc: mic: SCIF DMA and CPU copy interface") Signed-off-by: YueHaibing Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/scif/scif_dma.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/mic/scif/scif_dma.c b/drivers/misc/mic/scif/scif_dma.c index 2d76c200d072..18b8ed57c4ac 100644 --- a/drivers/misc/mic/scif/scif_dma.c +++ b/drivers/misc/mic/scif/scif_dma.c @@ -1553,7 +1553,6 @@ static int scif_rma_list_dma_copy_wrapper(struct scif_endpt *epd, u8 *temp = NULL; bool src_local = true; struct scif_dma_comp_cb *comp_cb; - dma_addr_t src_dma_addr, dst_dma_addr; int err; if (is_dma_copy_aligned(chan->device, 1, 1, 1)) @@ -1567,8 +1566,6 @@ static int scif_rma_list_dma_copy_wrapper(struct scif_endpt *epd, if (work->loopback) return scif_rma_list_cpu_copy(work); - src_dma_addr = __scif_off_to_dma_addr(work->src_window, src_offset); - dst_dma_addr = __scif_off_to_dma_addr(work->dst_window, dst_offset); src_local = work->src_window->type == SCIF_WINDOW_SELF; /* Allocate dma_completion cb */ -- cgit v1.2.3-58-ga151 From 6eaafbb6998e999467cf78a76e155ee00e372b14 Mon Sep 17 00:00:00 2001 From: Julien Folly Date: Tue, 9 Oct 2018 19:47:45 +0200 Subject: w1: IAD Register is yet readable trough iad sys file. Fix snprintf (%u for unsigned, count for max size). IAD Register is yet readable trough the "iad" sys file. A write to the "iad" sys file enables or disables the current measurement, but it was not possible to get the measured value by reading it. Fix: %u in snprintf for unsigned values (vdd and vad) Fix: Avoid possibles overflows (Usage of the 'count' variables) Signed-off-by: Julien Folly Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_ds2438.c | 66 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/slaves/w1_ds2438.c b/drivers/w1/slaves/w1_ds2438.c index bf641a191d07..7c4e33dbee4d 100644 --- a/drivers/w1/slaves/w1_ds2438.c +++ b/drivers/w1/slaves/w1_ds2438.c @@ -186,8 +186,8 @@ static int w1_ds2438_change_config_bit(struct w1_slave *sl, u8 mask, u8 value) return -1; } -static uint16_t w1_ds2438_get_voltage(struct w1_slave *sl, - int adc_input, uint16_t *voltage) +static int w1_ds2438_get_voltage(struct w1_slave *sl, + int adc_input, uint16_t *voltage) { unsigned int retries = W1_DS2438_RETRIES; u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; @@ -235,6 +235,25 @@ post_unlock: return ret; } +static int w1_ds2438_get_current(struct w1_slave *sl, int16_t *voltage) +{ + u8 w1_buf[DS2438_PAGE_SIZE + 1 /*for CRC*/]; + int ret; + + mutex_lock(&sl->master->bus_mutex); + + if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { + /* The voltage measured across current sense resistor RSENS. */ + *voltage = (((int16_t) w1_buf[DS2438_CURRENT_MSB]) << 8) | ((int16_t) w1_buf[DS2438_CURRENT_LSB]); + ret = 0; + } else + ret = -1; + + mutex_unlock(&sl->master->bus_mutex); + + return ret; +} + static ssize_t iad_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -257,6 +276,27 @@ static ssize_t iad_write(struct file *filp, struct kobject *kobj, return ret; } +static ssize_t iad_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t off, size_t count) +{ + struct w1_slave *sl = kobj_to_w1_slave(kobj); + int ret; + int16_t voltage; + + if (off != 0) + return 0; + if (!buf) + return -EINVAL; + + if (w1_ds2438_get_current(sl, &voltage) == 0) { + ret = snprintf(buf, count, "%i\n", voltage); + } else + ret = -EIO; + + return ret; +} + static ssize_t page0_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -272,9 +312,13 @@ static ssize_t page0_read(struct file *filp, struct kobject *kobj, mutex_lock(&sl->master->bus_mutex); + /* Read no more than page0 size */ + if (count > DS2438_PAGE_SIZE) + count = DS2438_PAGE_SIZE; + if (w1_ds2438_get_page(sl, 0, w1_buf) == 0) { - memcpy(buf, &w1_buf, DS2438_PAGE_SIZE); - ret = DS2438_PAGE_SIZE; + memcpy(buf, &w1_buf, count); + ret = count; } else ret = -EIO; @@ -289,7 +333,6 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj, { struct w1_slave *sl = kobj_to_w1_slave(kobj); int ret; - ssize_t c = PAGE_SIZE; int16_t temp; if (off != 0) @@ -298,8 +341,7 @@ static ssize_t temperature_read(struct file *filp, struct kobject *kobj, return -EINVAL; if (w1_ds2438_get_temperature(sl, &temp) == 0) { - c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", temp); - ret = PAGE_SIZE - c; + ret = snprintf(buf, count, "%i\n", temp); } else ret = -EIO; @@ -312,7 +354,6 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj, { struct w1_slave *sl = kobj_to_w1_slave(kobj); int ret; - ssize_t c = PAGE_SIZE; uint16_t voltage; if (off != 0) @@ -321,8 +362,7 @@ static ssize_t vad_read(struct file *filp, struct kobject *kobj, return -EINVAL; if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VAD, &voltage) == 0) { - c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage); - ret = PAGE_SIZE - c; + ret = snprintf(buf, count, "%u\n", voltage); } else ret = -EIO; @@ -335,7 +375,6 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj, { struct w1_slave *sl = kobj_to_w1_slave(kobj); int ret; - ssize_t c = PAGE_SIZE; uint16_t voltage; if (off != 0) @@ -344,15 +383,14 @@ static ssize_t vdd_read(struct file *filp, struct kobject *kobj, return -EINVAL; if (w1_ds2438_get_voltage(sl, DS2438_ADC_INPUT_VDD, &voltage) == 0) { - c -= snprintf(buf + PAGE_SIZE - c, c, "%d\n", voltage); - ret = PAGE_SIZE - c; + ret = snprintf(buf, count, "%u\n", voltage); } else ret = -EIO; return ret; } -static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, NULL, iad_write, 1); +static BIN_ATTR(iad, S_IRUGO | S_IWUSR | S_IWGRP, iad_read, iad_write, 0); static BIN_ATTR_RO(page0, DS2438_PAGE_SIZE); static BIN_ATTR_RO(temperature, 0/* real length varies */); static BIN_ATTR_RO(vad, 0/* real length varies */); -- cgit v1.2.3-58-ga151 From 5fe9f6ccbb7bc82a92e5d32b3eb68eebb3ea4a93 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 5 Oct 2018 06:19:12 +0000 Subject: eeprom: at25: remove unneeded 'at25_remove' Fixes gcc '-Wunused-but-set-variable' warning: drivers/misc/eeprom/at25.c: In function 'at25_remove': drivers/misc/eeprom/at25.c:384:20: warning: variable 'at25' set but not used [-Wunused-but-set-variable] Since commit 96d08fb43e30 ("eeprom: at25: use devm_nvmem_register()"), at25_remove is do nothing, so can be removed. Signed-off-by: YueHaibing Acked-by: Arnd Bergmann Reviewed-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index d874df72853f..99de6939cd5a 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -379,15 +379,6 @@ static int at25_probe(struct spi_device *spi) return 0; } -static int at25_remove(struct spi_device *spi) -{ - struct at25_data *at25; - - at25 = spi_get_drvdata(spi); - - return 0; -} - /*-------------------------------------------------------------------------*/ static const struct of_device_id at25_of_match[] = { @@ -402,7 +393,6 @@ static struct spi_driver at25_driver = { .of_match_table = at25_of_match, }, .probe = at25_probe, - .remove = at25_remove, }; module_spi_driver(at25_driver); -- cgit v1.2.3-58-ga151 From 3b7584a296c4fac45e59ab3a264880e1a2fedbfe Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sun, 7 Oct 2018 19:05:00 +0200 Subject: eeprom: New ee1004 driver for DDR4 memory The EEPROMs which hold the SPD data on DDR4 memory modules are no longer standard AT24C02-compatible EEPROMs. They are 512-byte EEPROMs which use only 1 I2C address for data access. You need to switch between the lower page and the upper page of data by sending commands on the SMBus. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/Kconfig | 11 ++ drivers/misc/eeprom/Makefile | 1 + drivers/misc/eeprom/ee1004.c | 281 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 drivers/misc/eeprom/ee1004.c (limited to 'drivers') diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 68a1ac929917..fe7a1d27a017 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -111,4 +111,15 @@ config EEPROM_IDT_89HPESX This driver can also be built as a module. If so, the module will be called idt_89hpesx. +config EEPROM_EE1004 + tristate "SPD EEPROMs on DDR4 memory modules" + depends on I2C && SYSFS + help + Enable this driver to get read support to SPD EEPROMs following + the JEDEC EE1004 standard. These are typically found on DDR4 + SDRAM memory modules. + + This driver can also be built as a module. If so, the module + will be called ee1004. + endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 2aab60ef3e3e..a9b4b6579b75 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o obj-$(CONFIG_EEPROM_IDT_89HPESX) += idt_89hpesx.o +obj-$(CONFIG_EEPROM_EE1004) += ee1004.o diff --git a/drivers/misc/eeprom/ee1004.c b/drivers/misc/eeprom/ee1004.c new file mode 100644 index 000000000000..276c1690ea1b --- /dev/null +++ b/drivers/misc/eeprom/ee1004.c @@ -0,0 +1,281 @@ +/* + * ee1004 - driver for DDR4 SPD EEPROMs + * + * Copyright (C) 2017 Jean Delvare + * + * Based on the at24 driver: + * Copyright (C) 2005-2007 David Brownell + * Copyright (C) 2008 Wolfram Sang, Pengutronix + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +/* + * DDR4 memory modules use special EEPROMs following the Jedec EE1004 + * specification. These are 512-byte EEPROMs using a single I2C address + * in the 0x50-0x57 range for data. One of two 256-byte page is selected + * by writing a command to I2C address 0x36 or 0x37 on the same I2C bus. + * + * Therefore we need to request these 2 additional addresses, and serialize + * access to all such EEPROMs with a single mutex. + * + * We assume it is safe to read up to 32 bytes at once from these EEPROMs. + * We use SMBus access even if I2C is available, these EEPROMs are small + * enough, and reading from them infrequent enough, that we favor simplicity + * over performance. + */ + +#define EE1004_ADDR_SET_PAGE 0x36 +#define EE1004_EEPROM_SIZE 512 +#define EE1004_PAGE_SIZE 256 +#define EE1004_PAGE_SHIFT 8 + +/* + * Mutex protects ee1004_set_page and ee1004_dev_count, and must be held + * from page selection to end of read. + */ +static DEFINE_MUTEX(ee1004_bus_lock); +static struct i2c_client *ee1004_set_page[2]; +static unsigned int ee1004_dev_count; +static int ee1004_current_page; + +static const struct i2c_device_id ee1004_ids[] = { + { "ee1004", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ee1004_ids); + +/*-------------------------------------------------------------------------*/ + +static ssize_t ee1004_eeprom_read(struct i2c_client *client, char *buf, + unsigned int offset, size_t count) +{ + int status; + + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + /* Can't cross page boundaries */ + if (unlikely(offset + count > EE1004_PAGE_SIZE)) + count = EE1004_PAGE_SIZE - offset; + + status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset, + count, buf); + dev_dbg(&client->dev, "read %zu@%d --> %d\n", count, offset, status); + + return status; +} + +static ssize_t ee1004_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct i2c_client *client = to_i2c_client(dev); + size_t requested = count; + int page; + + if (unlikely(!count)) + return count; + + page = off >> EE1004_PAGE_SHIFT; + if (unlikely(page > 1)) + return 0; + off &= (1 << EE1004_PAGE_SHIFT) - 1; + + /* + * Read data from chip, protecting against concurrent access to + * other EE1004 SPD EEPROMs on the same adapter. + */ + mutex_lock(&ee1004_bus_lock); + + while (count) { + int status; + + /* Select page */ + if (page != ee1004_current_page) { + /* Data is ignored */ + status = i2c_smbus_write_byte(ee1004_set_page[page], + 0x00); + if (status < 0) { + dev_err(dev, "Failed to select page %d (%d)\n", + page, status); + mutex_unlock(&ee1004_bus_lock); + return status; + } + dev_dbg(dev, "Selected page %d\n", page); + ee1004_current_page = page; + } + + status = ee1004_eeprom_read(client, buf, off, count); + if (status < 0) { + mutex_unlock(&ee1004_bus_lock); + return status; + } + buf += status; + off += status; + count -= status; + + if (off == EE1004_PAGE_SIZE) { + page++; + off = 0; + } + } + + mutex_unlock(&ee1004_bus_lock); + + return requested; +} + +static const struct bin_attribute eeprom_attr = { + .attr = { + .name = "eeprom", + .mode = 0444, + }, + .size = EE1004_EEPROM_SIZE, + .read = ee1004_read, +}; + +static int ee1004_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err, cnr = 0; + const char *slow = NULL; + + /* Make sure we can operate on this adapter */ + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_WORD_DATA)) + slow = "word"; + else if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_READ_BYTE_DATA)) + slow = "byte"; + else + return -EPFNOSUPPORT; + } + + /* Use 2 dummy devices for page select command */ + mutex_lock(&ee1004_bus_lock); + if (++ee1004_dev_count == 1) { + for (cnr = 0; cnr < 2; cnr++) { + ee1004_set_page[cnr] = i2c_new_dummy(client->adapter, + EE1004_ADDR_SET_PAGE + cnr); + if (!ee1004_set_page[cnr]) { + dev_err(&client->dev, + "address 0x%02x unavailable\n", + EE1004_ADDR_SET_PAGE + cnr); + err = -EADDRINUSE; + goto err_clients; + } + } + } else if (i2c_adapter_id(client->adapter) != + i2c_adapter_id(ee1004_set_page[0]->adapter)) { + dev_err(&client->dev, + "Driver only supports devices on a single I2C bus\n"); + err = -EOPNOTSUPP; + goto err_clients; + } + + /* Remember current page to avoid unneeded page select */ + err = i2c_smbus_read_byte(ee1004_set_page[0]); + if (err == -ENXIO) { + /* Nack means page 1 is selected */ + ee1004_current_page = 1; + } else if (err < 0) { + /* Anything else is a real error, bail out */ + goto err_clients; + } else { + /* Ack means page 0 is selected, returned value meaningless */ + ee1004_current_page = 0; + } + dev_dbg(&client->dev, "Currently selected page: %d\n", + ee1004_current_page); + mutex_unlock(&ee1004_bus_lock); + + /* Create the sysfs eeprom file */ + err = sysfs_create_bin_file(&client->dev.kobj, &eeprom_attr); + if (err) + goto err_clients_lock; + + dev_info(&client->dev, + "%u byte EE1004-compliant SPD EEPROM, read-only\n", + EE1004_EEPROM_SIZE); + if (slow) + dev_notice(&client->dev, + "Falling back to %s reads, performance will suffer\n", + slow); + + return 0; + + err_clients_lock: + mutex_lock(&ee1004_bus_lock); + err_clients: + if (--ee1004_dev_count == 0) { + for (cnr--; cnr >= 0; cnr--) { + i2c_unregister_device(ee1004_set_page[cnr]); + ee1004_set_page[cnr] = NULL; + } + } + mutex_unlock(&ee1004_bus_lock); + + return err; +} + +static int ee1004_remove(struct i2c_client *client) +{ + int i; + + sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr); + + /* Remove page select clients if this is the last device */ + mutex_lock(&ee1004_bus_lock); + if (--ee1004_dev_count == 0) { + for (i = 0; i < 2; i++) { + i2c_unregister_device(ee1004_set_page[i]); + ee1004_set_page[i] = NULL; + } + } + mutex_unlock(&ee1004_bus_lock); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct i2c_driver ee1004_driver = { + .driver = { + .name = "ee1004", + }, + .probe = ee1004_probe, + .remove = ee1004_remove, + .id_table = ee1004_ids, +}; + +static int __init ee1004_init(void) +{ + return i2c_add_driver(&ee1004_driver); +} +module_init(ee1004_init); + +static void __exit ee1004_exit(void) +{ + i2c_del_driver(&ee1004_driver); +} +module_exit(ee1004_exit); + +MODULE_DESCRIPTION("Driver for EE1004-compliant DDR4 SPD EEPROMs"); +MODULE_AUTHOR("Jean Delvare"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From f21996255f42eee28048ede652a592d6048b06b3 Mon Sep 17 00:00:00 2001 From: Lance Roy Date: Tue, 2 Oct 2018 22:38:49 -0700 Subject: sgi-xp: Replace spin_is_locked() with lockdep lockdep_assert_held() is better suited to checking locking requirements, since it won't get confused when someone else holds the lock. This is also a step towards possibly removing spin_is_locked(). Signed-off-by: Lance Roy Cc: Cliff Whickman Cc: Arnd Bergmann Acked-by: Robin Holt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sgi-xp/xpc_channel.c | 6 +++--- drivers/misc/sgi-xp/xpc_sn2.c | 2 +- drivers/misc/sgi-xp/xpc_uv.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/misc/sgi-xp/xpc_channel.c b/drivers/misc/sgi-xp/xpc_channel.c index 05a890ce2ab8..8e6607fc8a67 100644 --- a/drivers/misc/sgi-xp/xpc_channel.c +++ b/drivers/misc/sgi-xp/xpc_channel.c @@ -28,7 +28,7 @@ xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags) { enum xp_retval ret; - DBUG_ON(!spin_is_locked(&ch->lock)); + lockdep_assert_held(&ch->lock); if (!(ch->flags & XPC_C_OPENREQUEST) || !(ch->flags & XPC_C_ROPENREQUEST)) { @@ -82,7 +82,7 @@ xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) struct xpc_partition *part = &xpc_partitions[ch->partid]; u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); - DBUG_ON(!spin_is_locked(&ch->lock)); + lockdep_assert_held(&ch->lock); if (!(ch->flags & XPC_C_DISCONNECTING)) return; @@ -755,7 +755,7 @@ xpc_disconnect_channel(const int line, struct xpc_channel *ch, { u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED); - DBUG_ON(!spin_is_locked(&ch->lock)); + lockdep_assert_held(&ch->lock); if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) return; diff --git a/drivers/misc/sgi-xp/xpc_sn2.c b/drivers/misc/sgi-xp/xpc_sn2.c index 5a12d2a54049..0ae69b9390ce 100644 --- a/drivers/misc/sgi-xp/xpc_sn2.c +++ b/drivers/misc/sgi-xp/xpc_sn2.c @@ -1671,7 +1671,7 @@ xpc_teardown_msg_structures_sn2(struct xpc_channel *ch) { struct xpc_channel_sn2 *ch_sn2 = &ch->sn.sn2; - DBUG_ON(!spin_is_locked(&ch->lock)); + lockdep_assert_held(&ch->lock); ch_sn2->remote_msgqueue_pa = 0; diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c index 340b44d9e8cf..0441abe87880 100644 --- a/drivers/misc/sgi-xp/xpc_uv.c +++ b/drivers/misc/sgi-xp/xpc_uv.c @@ -1183,7 +1183,7 @@ xpc_teardown_msg_structures_uv(struct xpc_channel *ch) { struct xpc_channel_uv *ch_uv = &ch->sn.uv; - DBUG_ON(!spin_is_locked(&ch->lock)); + lockdep_assert_held(&ch->lock); kfree(ch_uv->cached_notify_gru_mq_desc); ch_uv->cached_notify_gru_mq_desc = NULL; -- cgit v1.2.3-58-ga151 From 1c87dc897b8c8ace3aa4480fa29ef6439dabb3ab Mon Sep 17 00:00:00 2001 From: Lance Roy Date: Tue, 2 Oct 2018 22:38:48 -0700 Subject: hv_balloon: Replace spin_is_locked() with lockdep lockdep_assert_held() is better suited to checking locking requirements, since it won't get confused when someone else holds the lock. This is also a step towards possibly removing spin_is_locked(). Signed-off-by: Lance Roy Cc: "K. Y. Srinivasan" Cc: Haiyang Zhang Cc: Stephen Hemminger Reviewed-by: Vitaly Kuznetsov Signed-off-by: Greg Kroah-Hartman --- drivers/hv/hv_balloon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index b1b788082793..41631512ae97 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -689,7 +689,7 @@ static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) __online_page_increment_counters(pg); __online_page_free(pg); - WARN_ON_ONCE(!spin_is_locked(&dm_device.ha_lock)); + lockdep_assert_held(&dm_device.ha_lock); dm_device.num_pages_onlined++; } -- cgit v1.2.3-58-ga151 From 084181fe8cc7472695b907f0018f4cd00934cb12 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Mon, 15 Oct 2018 17:20:01 -0500 Subject: fpga: mgr: add devm_fpga_mgr_create Add devm_fpga_mgr_create() which is the managed version of fpga_mgr_create(). Change current FPGA manager drivers to use devm_fpga_mgr_create() Signed-off-by: Alan Tull Suggested-by: Federico Vaga Acked-by: Moritz Fischer Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/fpga/fpga-mgr.rst | 13 +++--- drivers/fpga/altera-cvp.c | 8 ++-- drivers/fpga/altera-pr-ip-core.c | 9 +---- drivers/fpga/altera-ps-spi.c | 11 ++--- drivers/fpga/dfl-fme-mgr.c | 11 ++--- drivers/fpga/fpga-mgr.c | 64 ++++++++++++++++++++++++++---- drivers/fpga/ice40-spi.c | 10 ++--- drivers/fpga/machxo2-spi.c | 11 ++--- drivers/fpga/socfpga-a10.c | 5 +-- drivers/fpga/socfpga.c | 10 ++--- drivers/fpga/ts73xx-fpga.c | 11 ++--- drivers/fpga/xilinx-spi.c | 12 ++---- drivers/fpga/zynq-fpga.c | 5 +-- include/linux/fpga/fpga-mgr.h | 4 ++ 14 files changed, 97 insertions(+), 87 deletions(-) (limited to 'drivers') diff --git a/Documentation/driver-api/fpga/fpga-mgr.rst b/Documentation/driver-api/fpga/fpga-mgr.rst index 82b6dbbd31cd..db8885efbc22 100644 --- a/Documentation/driver-api/fpga/fpga-mgr.rst +++ b/Documentation/driver-api/fpga/fpga-mgr.rst @@ -49,18 +49,14 @@ probe function calls fpga_mgr_register(), such as:: * them in priv */ - mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", - &socfpga_fpga_ops, priv); + mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", + &socfpga_fpga_ops, priv); if (!mgr) return -ENOMEM; platform_set_drvdata(pdev, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int socfpga_fpga_remove(struct platform_device *pdev) @@ -169,6 +165,9 @@ API for implementing a new FPGA Manager driver .. kernel-doc:: include/linux/fpga/fpga-mgr.h :functions: fpga_manager_ops +.. kernel-doc:: drivers/fpga/fpga-mgr.c + :functions: devm_fpga_mgr_create + .. kernel-doc:: drivers/fpga/fpga-mgr.c :functions: fpga_mgr_create diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index 7fa793672a7a..610a1558e0ed 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -453,8 +453,8 @@ static int altera_cvp_probe(struct pci_dev *pdev, snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", ALTERA_CVP_MGR_NAME, pci_name(pdev)); - mgr = fpga_mgr_create(&pdev->dev, conf->mgr_name, - &altera_cvp_ops, conf); + mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name, + &altera_cvp_ops, conf); if (!mgr) { ret = -ENOMEM; goto err_unmap; @@ -463,10 +463,8 @@ static int altera_cvp_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, mgr); ret = fpga_mgr_register(mgr); - if (ret) { - fpga_mgr_free(mgr); + if (ret) goto err_unmap; - } ret = driver_create_file(&altera_cvp_driver.driver, &driver_attr_chkcfg); diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c index 65e0b6a2c031..a7a3bf0b5202 100644 --- a/drivers/fpga/altera-pr-ip-core.c +++ b/drivers/fpga/altera-pr-ip-core.c @@ -177,7 +177,6 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) { struct alt_pr_priv *priv; struct fpga_manager *mgr; - int ret; u32 val; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -192,17 +191,13 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT, (int)(val & ALT_PR_CSR_PR_START)); - mgr = fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv); + mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv); if (!mgr) return -ENOMEM; dev_set_drvdata(dev, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } EXPORT_SYMBOL_GPL(alt_pr_register); diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c index 24b25c626036..33aafda50af5 100644 --- a/drivers/fpga/altera-ps-spi.c +++ b/drivers/fpga/altera-ps-spi.c @@ -239,7 +239,6 @@ static int altera_ps_probe(struct spi_device *spi) struct altera_ps_conf *conf; const struct of_device_id *of_id; struct fpga_manager *mgr; - int ret; conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); if (!conf) @@ -275,18 +274,14 @@ static int altera_ps_probe(struct spi_device *spi) snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", dev_driver_string(&spi->dev), dev_name(&spi->dev)); - mgr = fpga_mgr_create(&spi->dev, conf->mgr_name, - &altera_ps_ops, conf); + mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name, + &altera_ps_ops, conf); if (!mgr) return -ENOMEM; spi_set_drvdata(spi, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int altera_ps_remove(struct spi_device *spi) diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c index 9f045d058cfd..76f37709dd1a 100644 --- a/drivers/fpga/dfl-fme-mgr.c +++ b/drivers/fpga/dfl-fme-mgr.c @@ -287,7 +287,6 @@ static int fme_mgr_probe(struct platform_device *pdev) struct fme_mgr_priv *priv; struct fpga_manager *mgr; struct resource *res; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -309,19 +308,15 @@ static int fme_mgr_probe(struct platform_device *pdev) fme_mgr_get_compat_id(priv->ioaddr, compat_id); - mgr = fpga_mgr_create(dev, "DFL FME FPGA Manager", - &fme_mgr_ops, priv); + mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager", + &fme_mgr_ops, priv); if (!mgr) return -ENOMEM; mgr->compat_id = compat_id; platform_set_drvdata(pdev, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int fme_mgr_remove(struct platform_device *pdev) diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index a41b07e37884..c3866816456a 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -558,6 +558,9 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unlock); * @mops: pointer to structure of fpga manager ops * @priv: fpga manager private data * + * The caller of this function is responsible for freeing the struct with + * fpga_mgr_free(). Using devm_fpga_mgr_create() instead is recommended. + * * Return: pointer to struct fpga_manager or NULL */ struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, @@ -618,8 +621,8 @@ error_kfree: EXPORT_SYMBOL_GPL(fpga_mgr_create); /** - * fpga_mgr_free - deallocate a FPGA manager - * @mgr: fpga manager struct created by fpga_mgr_create + * fpga_mgr_free - free a FPGA manager created with fpga_mgr_create() + * @mgr: fpga manager struct */ void fpga_mgr_free(struct fpga_manager *mgr) { @@ -628,9 +631,55 @@ void fpga_mgr_free(struct fpga_manager *mgr) } EXPORT_SYMBOL_GPL(fpga_mgr_free); +static void devm_fpga_mgr_release(struct device *dev, void *res) +{ + struct fpga_manager *mgr = *(struct fpga_manager **)res; + + fpga_mgr_free(mgr); +} + +/** + * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct + * @dev: fpga manager device from pdev + * @name: fpga manager name + * @mops: pointer to structure of fpga manager ops + * @priv: fpga manager private data + * + * This function is intended for use in a FPGA manager driver's probe function. + * After the manager driver creates the manager struct with + * devm_fpga_mgr_create(), it should register it with fpga_mgr_register(). The + * manager driver's remove function should call fpga_mgr_unregister(). The + * manager struct allocated with this function will be freed automatically on + * driver detach. This includes the case of a probe function returning error + * before calling fpga_mgr_register(), the struct will still get cleaned up. + * + * Return: pointer to struct fpga_manager or NULL + */ +struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name, + const struct fpga_manager_ops *mops, + void *priv) +{ + struct fpga_manager **ptr, *mgr; + + ptr = devres_alloc(devm_fpga_mgr_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + mgr = fpga_mgr_create(dev, name, mops, priv); + if (!mgr) { + devres_free(ptr); + } else { + *ptr = mgr; + devres_add(dev, ptr); + } + + return mgr; +} +EXPORT_SYMBOL_GPL(devm_fpga_mgr_create); + /** * fpga_mgr_register - register a FPGA manager - * @mgr: fpga manager struct created by fpga_mgr_create + * @mgr: fpga manager struct * * Return: 0 on success, negative error code otherwise. */ @@ -661,8 +710,10 @@ error_device: EXPORT_SYMBOL_GPL(fpga_mgr_register); /** - * fpga_mgr_unregister - unregister and free a FPGA manager - * @mgr: fpga manager struct + * fpga_mgr_unregister - unregister a FPGA manager + * @mgr: fpga manager struct + * + * This function is intended for use in a FPGA manager driver's remove function. */ void fpga_mgr_unregister(struct fpga_manager *mgr) { @@ -681,9 +732,6 @@ EXPORT_SYMBOL_GPL(fpga_mgr_unregister); static void fpga_mgr_dev_release(struct device *dev) { - struct fpga_manager *mgr = to_fpga_manager(dev); - - fpga_mgr_free(mgr); } static int __init fpga_mgr_class_init(void) diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c index 5981c7ee7a7d..6154661b8f76 100644 --- a/drivers/fpga/ice40-spi.c +++ b/drivers/fpga/ice40-spi.c @@ -175,18 +175,14 @@ static int ice40_fpga_probe(struct spi_device *spi) return ret; } - mgr = fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager", - &ice40_fpga_ops, priv); + mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager", + &ice40_fpga_ops, priv); if (!mgr) return -ENOMEM; spi_set_drvdata(spi, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int ice40_fpga_remove(struct spi_device *spi) diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c index a582e0000c97..4d8a87641587 100644 --- a/drivers/fpga/machxo2-spi.c +++ b/drivers/fpga/machxo2-spi.c @@ -356,25 +356,20 @@ static int machxo2_spi_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct fpga_manager *mgr; - int ret; if (spi->max_speed_hz > MACHXO2_MAX_SPEED) { dev_err(dev, "Speed is too high\n"); return -EINVAL; } - mgr = fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager", - &machxo2_ops, spi); + mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager", + &machxo2_ops, spi); if (!mgr) return -ENOMEM; spi_set_drvdata(spi, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int machxo2_spi_remove(struct spi_device *spi) diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c index be30c48eb6e4..573d88bdf730 100644 --- a/drivers/fpga/socfpga-a10.c +++ b/drivers/fpga/socfpga-a10.c @@ -508,8 +508,8 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) return -EBUSY; } - mgr = fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager", - &socfpga_a10_fpga_mgr_ops, priv); + mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager", + &socfpga_a10_fpga_mgr_ops, priv); if (!mgr) return -ENOMEM; @@ -517,7 +517,6 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) ret = fpga_mgr_register(mgr); if (ret) { - fpga_mgr_free(mgr); clk_disable_unprepare(priv->clk); return ret; } diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c index 959d71f26896..4a8a2fcd4e6c 100644 --- a/drivers/fpga/socfpga.c +++ b/drivers/fpga/socfpga.c @@ -571,18 +571,14 @@ static int socfpga_fpga_probe(struct platform_device *pdev) if (ret) return ret; - mgr = fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", - &socfpga_fpga_ops, priv); + mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", + &socfpga_fpga_ops, priv); if (!mgr) return -ENOMEM; platform_set_drvdata(pdev, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int socfpga_fpga_remove(struct platform_device *pdev) diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c index 08efd1895b1b..dc22a5842609 100644 --- a/drivers/fpga/ts73xx-fpga.c +++ b/drivers/fpga/ts73xx-fpga.c @@ -118,7 +118,6 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) struct ts73xx_fpga_priv *priv; struct fpga_manager *mgr; struct resource *res; - int ret; priv = devm_kzalloc(kdev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -133,18 +132,14 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) return PTR_ERR(priv->io_base); } - mgr = fpga_mgr_create(kdev, "TS-73xx FPGA Manager", - &ts73xx_fpga_ops, priv); + mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager", + &ts73xx_fpga_ops, priv); if (!mgr) return -ENOMEM; platform_set_drvdata(pdev, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int ts73xx_fpga_remove(struct platform_device *pdev) diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index 8d1945966533..469486be20c4 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -144,7 +144,6 @@ static int xilinx_spi_probe(struct spi_device *spi) { struct xilinx_spi_conf *conf; struct fpga_manager *mgr; - int ret; conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); if (!conf) @@ -167,18 +166,15 @@ static int xilinx_spi_probe(struct spi_device *spi) return PTR_ERR(conf->done); } - mgr = fpga_mgr_create(&spi->dev, "Xilinx Slave Serial FPGA Manager", - &xilinx_spi_ops, conf); + mgr = devm_fpga_mgr_create(&spi->dev, + "Xilinx Slave Serial FPGA Manager", + &xilinx_spi_ops, conf); if (!mgr) return -ENOMEM; spi_set_drvdata(spi, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - fpga_mgr_free(mgr); - - return ret; + return fpga_mgr_register(mgr); } static int xilinx_spi_remove(struct spi_device *spi) diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 3110e00121ca..bb82efeebb9d 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -614,8 +614,8 @@ static int zynq_fpga_probe(struct platform_device *pdev) clk_disable(priv->clk); - mgr = fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager", - &zynq_fpga_ops, priv); + mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager", + &zynq_fpga_ops, priv); if (!mgr) return -ENOMEM; @@ -624,7 +624,6 @@ static int zynq_fpga_probe(struct platform_device *pdev) err = fpga_mgr_register(mgr); if (err) { dev_err(dev, "unable to register FPGA manager\n"); - fpga_mgr_free(mgr); clk_unprepare(priv->clk); return err; } diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h index 8ab5df769923..e8ca62b2cb5b 100644 --- a/include/linux/fpga/fpga-mgr.h +++ b/include/linux/fpga/fpga-mgr.h @@ -198,4 +198,8 @@ void fpga_mgr_free(struct fpga_manager *mgr); int fpga_mgr_register(struct fpga_manager *mgr); void fpga_mgr_unregister(struct fpga_manager *mgr); +struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name, + const struct fpga_manager_ops *mops, + void *priv); + #endif /*_LINUX_FPGA_MGR_H */ -- cgit v1.2.3-58-ga151 From 213befe049c70cfcfcbb4f6010bd5276dbc1f7b9 Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Mon, 15 Oct 2018 17:20:02 -0500 Subject: fpga: bridge: add devm_fpga_bridge_create Add devm_fpga_bridge_create() which is the managed version of fpga_bridge_create(). Change current bridge drivers to use devm_fpga_bridge_create(). Signed-off-by: Alan Tull Suggested-by: Federico Vaga Acked-by: Moritz Fischer Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/fpga/fpga-bridge.rst | 3 ++ drivers/fpga/altera-fpga2sdram.c | 8 ++-- drivers/fpga/altera-freeze-bridge.c | 13 ++--- drivers/fpga/altera-hps2fpga.c | 7 ++- drivers/fpga/dfl-fme-br.c | 11 ++--- drivers/fpga/fpga-bridge.c | 68 +++++++++++++++++++++++---- drivers/fpga/xilinx-pr-decoupler.c | 4 +- include/linux/fpga/fpga-bridge.h | 4 ++ 8 files changed, 80 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst index 2c2aaca894bf..ebbcbde687b3 100644 --- a/Documentation/driver-api/fpga/fpga-bridge.rst +++ b/Documentation/driver-api/fpga/fpga-bridge.rst @@ -10,6 +10,9 @@ API to implement a new FPGA bridge .. kernel-doc:: include/linux/fpga/fpga-bridge.h :functions: fpga_bridge_ops +.. kernel-doc:: drivers/fpga/fpga-bridge.c + :functions: devm_fpga_bridge_create + .. kernel-doc:: drivers/fpga/fpga-bridge.c :functions: fpga_bridge_create diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c index 23660ccd634b..a78e49c63c64 100644 --- a/drivers/fpga/altera-fpga2sdram.c +++ b/drivers/fpga/altera-fpga2sdram.c @@ -121,18 +121,16 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) /* Get f2s bridge configuration saved in handoff register */ regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask); - br = fpga_bridge_create(dev, F2S_BRIDGE_NAME, - &altera_fpga2sdram_br_ops, priv); + br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME, + &altera_fpga2sdram_br_ops, priv); if (!br) return -ENOMEM; platform_set_drvdata(pdev, br); ret = fpga_bridge_register(br); - if (ret) { - fpga_bridge_free(br); + if (ret) return ret; - } dev_info(dev, "driver initialized with handoff %08x\n", priv->mask); diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c index ffd586c48ecf..dd58c4aea92e 100644 --- a/drivers/fpga/altera-freeze-bridge.c +++ b/drivers/fpga/altera-freeze-bridge.c @@ -213,7 +213,6 @@ static int altera_freeze_br_probe(struct platform_device *pdev) struct fpga_bridge *br; struct resource *res; u32 status, revision; - int ret; if (!np) return -ENODEV; @@ -245,20 +244,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev) priv->base_addr = base_addr; - br = fpga_bridge_create(dev, FREEZE_BRIDGE_NAME, - &altera_freeze_br_br_ops, priv); + br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME, + &altera_freeze_br_br_ops, priv); if (!br) return -ENOMEM; platform_set_drvdata(pdev, br); - ret = fpga_bridge_register(br); - if (ret) { - fpga_bridge_free(br); - return ret; - } - - return 0; + return fpga_bridge_register(br); } static int altera_freeze_br_remove(struct platform_device *pdev) diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index a974d3f60321..77b95f251821 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c @@ -180,7 +180,8 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) } } - br = fpga_bridge_create(dev, priv->name, &altera_hps2fpga_br_ops, priv); + br = devm_fpga_bridge_create(dev, priv->name, + &altera_hps2fpga_br_ops, priv); if (!br) { ret = -ENOMEM; goto err; @@ -190,12 +191,10 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) ret = fpga_bridge_register(br); if (ret) - goto err_free; + goto err; return 0; -err_free: - fpga_bridge_free(br); err: clk_disable_unprepare(priv->clk); diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c index 7cc041def8b3..3ff9f3a687ce 100644 --- a/drivers/fpga/dfl-fme-br.c +++ b/drivers/fpga/dfl-fme-br.c @@ -61,7 +61,6 @@ static int fme_br_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct fme_br_priv *priv; struct fpga_bridge *br; - int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -69,18 +68,14 @@ static int fme_br_probe(struct platform_device *pdev) priv->pdata = dev_get_platdata(dev); - br = fpga_bridge_create(dev, "DFL FPGA FME Bridge", - &fme_bridge_ops, priv); + br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge", + &fme_bridge_ops, priv); if (!br) return -ENOMEM; platform_set_drvdata(pdev, br); - ret = fpga_bridge_register(br); - if (ret) - fpga_bridge_free(br); - - return ret; + return fpga_bridge_register(br); } static int fme_br_remove(struct platform_device *pdev) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index c983dac97501..80bd8f1b2aa6 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -324,6 +324,9 @@ ATTRIBUTE_GROUPS(fpga_bridge); * @br_ops: pointer to structure of fpga bridge ops * @priv: FPGA bridge private data * + * The caller of this function is responsible for freeing the bridge with + * fpga_bridge_free(). Using devm_fpga_bridge_create() instead is recommended. + * * Return: struct fpga_bridge or NULL */ struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, @@ -378,8 +381,8 @@ error_kfree: EXPORT_SYMBOL_GPL(fpga_bridge_create); /** - * fpga_bridge_free - free a fpga bridge and its id - * @bridge: FPGA bridge struct created by fpga_bridge_create + * fpga_bridge_free - free a fpga bridge created by fpga_bridge_create() + * @bridge: FPGA bridge struct */ void fpga_bridge_free(struct fpga_bridge *bridge) { @@ -388,9 +391,56 @@ void fpga_bridge_free(struct fpga_bridge *bridge) } EXPORT_SYMBOL_GPL(fpga_bridge_free); +static void devm_fpga_bridge_release(struct device *dev, void *res) +{ + struct fpga_bridge *bridge = *(struct fpga_bridge **)res; + + fpga_bridge_free(bridge); +} + /** - * fpga_bridge_register - register a fpga bridge - * @bridge: FPGA bridge struct created by fpga_bridge_create + * devm_fpga_bridge_create - create and init a managed struct fpga_bridge + * @dev: FPGA bridge device from pdev + * @name: FPGA bridge name + * @br_ops: pointer to structure of fpga bridge ops + * @priv: FPGA bridge private data + * + * This function is intended for use in a FPGA bridge driver's probe function. + * After the bridge driver creates the struct with devm_fpga_bridge_create(), it + * should register the bridge with fpga_bridge_register(). The bridge driver's + * remove function should call fpga_bridge_unregister(). The bridge struct + * allocated with this function will be freed automatically on driver detach. + * This includes the case of a probe function returning error before calling + * fpga_bridge_register(), the struct will still get cleaned up. + * + * Return: struct fpga_bridge or NULL + */ +struct fpga_bridge +*devm_fpga_bridge_create(struct device *dev, const char *name, + const struct fpga_bridge_ops *br_ops, void *priv) +{ + struct fpga_bridge **ptr, *bridge; + + ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + bridge = fpga_bridge_create(dev, name, br_ops, priv); + if (!bridge) { + devres_free(ptr); + } else { + *ptr = bridge; + devres_add(dev, ptr); + } + + return bridge; +} +EXPORT_SYMBOL_GPL(devm_fpga_bridge_create); + +/** + * fpga_bridge_register - register a FPGA bridge + * + * @bridge: FPGA bridge struct * * Return: 0 for success, error code otherwise. */ @@ -412,8 +462,11 @@ int fpga_bridge_register(struct fpga_bridge *bridge) EXPORT_SYMBOL_GPL(fpga_bridge_register); /** - * fpga_bridge_unregister - unregister and free a fpga bridge - * @bridge: FPGA bridge struct created by fpga_bridge_create + * fpga_bridge_unregister - unregister a FPGA bridge + * + * @bridge: FPGA bridge struct + * + * This function is intended for use in a FPGA bridge driver's remove function. */ void fpga_bridge_unregister(struct fpga_bridge *bridge) { @@ -430,9 +483,6 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister); static void fpga_bridge_dev_release(struct device *dev) { - struct fpga_bridge *bridge = to_fpga_bridge(dev); - - fpga_bridge_free(bridge); } static int __init fpga_bridge_dev_init(void) diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index 07ba1539e82c..641036135207 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -121,8 +121,8 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) clk_disable(priv->clk); - br = fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler", - &xlnx_pr_decoupler_br_ops, priv); + br = devm_fpga_bridge_create(&pdev->dev, "Xilinx PR Decoupler", + &xlnx_pr_decoupler_br_ops, priv); if (!br) { err = -ENOMEM; goto err_clk; diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h index ce550fcf6360..817600a32c93 100644 --- a/include/linux/fpga/fpga-bridge.h +++ b/include/linux/fpga/fpga-bridge.h @@ -69,4 +69,8 @@ void fpga_bridge_free(struct fpga_bridge *br); int fpga_bridge_register(struct fpga_bridge *br); void fpga_bridge_unregister(struct fpga_bridge *br); +struct fpga_bridge +*devm_fpga_bridge_create(struct device *dev, const char *name, + const struct fpga_bridge_ops *br_ops, void *priv); + #endif /* _LINUX_FPGA_BRIDGE_H */ -- cgit v1.2.3-58-ga151 From fea82b7f6670002ff36bf1bc77d0345b0b2f2d1c Mon Sep 17 00:00:00 2001 From: Alan Tull Date: Mon, 15 Oct 2018 17:20:03 -0500 Subject: fpga: add devm_fpga_region_create Add devm_fpga_region_create() which is the managed version of fpga_region_create(). Change current region drivers to use devm_fpga_region_create(). Signed-off-by: Alan Tull Suggested-by: Federico Vaga Acked-by: Moritz Fischer Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/fpga/fpga-region.rst | 3 ++ drivers/fpga/dfl-fme-region.c | 6 +-- drivers/fpga/dfl.c | 6 +-- drivers/fpga/fpga-region.c | 65 +++++++++++++++++++++++---- drivers/fpga/of-fpga-region.c | 6 +-- include/linux/fpga/fpga-region.h | 4 ++ 6 files changed, 70 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/Documentation/driver-api/fpga/fpga-region.rst b/Documentation/driver-api/fpga/fpga-region.rst index f30333ce828e..dc9f75cc3352 100644 --- a/Documentation/driver-api/fpga/fpga-region.rst +++ b/Documentation/driver-api/fpga/fpga-region.rst @@ -89,6 +89,9 @@ API to add a new FPGA region .. kernel-doc:: include/linux/fpga/fpga-region.h :functions: fpga_region +.. kernel-doc:: drivers/fpga/fpga-region.c + :functions: devm_fpga_region_create + .. kernel-doc:: drivers/fpga/fpga-region.c :functions: fpga_region_create diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c index 51a5ac2293a7..ec134ec93f08 100644 --- a/drivers/fpga/dfl-fme-region.c +++ b/drivers/fpga/dfl-fme-region.c @@ -39,7 +39,7 @@ static int fme_region_probe(struct platform_device *pdev) if (IS_ERR(mgr)) return -EPROBE_DEFER; - region = fpga_region_create(dev, mgr, fme_region_get_bridges); + region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges); if (!region) { ret = -ENOMEM; goto eprobe_mgr_put; @@ -51,14 +51,12 @@ static int fme_region_probe(struct platform_device *pdev) ret = fpga_region_register(region); if (ret) - goto region_free; + goto eprobe_mgr_put; dev_dbg(dev, "DFL FME FPGA Region probed\n"); return 0; -region_free: - fpga_region_free(region); eprobe_mgr_put: fpga_mgr_put(mgr); return ret; diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index a9b521bccb06..2c09e502e721 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -899,7 +899,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) if (!cdev) return ERR_PTR(-ENOMEM); - cdev->region = fpga_region_create(info->dev, NULL, NULL); + cdev->region = devm_fpga_region_create(info->dev, NULL, NULL); if (!cdev->region) { ret = -ENOMEM; goto free_cdev_exit; @@ -911,7 +911,7 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) ret = fpga_region_register(cdev->region); if (ret) - goto free_region_exit; + goto free_cdev_exit; /* create and init build info for enumeration */ binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL); @@ -942,8 +942,6 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) unregister_region_exit: fpga_region_unregister(cdev->region); -free_region_exit: - fpga_region_free(cdev->region); free_cdev_exit: devm_kfree(info->dev, cdev); return ERR_PTR(ret); diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index 0d65220d5ec5..bde5a9d460c5 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c @@ -185,6 +185,10 @@ ATTRIBUTE_GROUPS(fpga_region); * @mgr: manager that programs this region * @get_bridges: optional function to get bridges to a list * + * The caller of this function is responsible for freeing the resulting region + * struct with fpga_region_free(). Using devm_fpga_region_create() instead is + * recommended. + * * Return: struct fpga_region or NULL */ struct fpga_region @@ -230,8 +234,8 @@ err_free: EXPORT_SYMBOL_GPL(fpga_region_create); /** - * fpga_region_free - free a struct fpga_region - * @region: FPGA region created by fpga_region_create + * fpga_region_free - free a FPGA region created by fpga_region_create() + * @region: FPGA region */ void fpga_region_free(struct fpga_region *region) { @@ -240,21 +244,69 @@ void fpga_region_free(struct fpga_region *region) } EXPORT_SYMBOL_GPL(fpga_region_free); +static void devm_fpga_region_release(struct device *dev, void *res) +{ + struct fpga_region *region = *(struct fpga_region **)res; + + fpga_region_free(region); +} + +/** + * devm_fpga_region_create - create and initialize a managed FPGA region struct + * @dev: device parent + * @mgr: manager that programs this region + * @get_bridges: optional function to get bridges to a list + * + * This function is intended for use in a FPGA region driver's probe function. + * After the region driver creates the region struct with + * devm_fpga_region_create(), it should register it with fpga_region_register(). + * The region driver's remove function should call fpga_region_unregister(). + * The region struct allocated with this function will be freed automatically on + * driver detach. This includes the case of a probe function returning error + * before calling fpga_region_register(), the struct will still get cleaned up. + * + * Return: struct fpga_region or NULL + */ +struct fpga_region +*devm_fpga_region_create(struct device *dev, + struct fpga_manager *mgr, + int (*get_bridges)(struct fpga_region *)) +{ + struct fpga_region **ptr, *region; + + ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return NULL; + + region = fpga_region_create(dev, mgr, get_bridges); + if (!region) { + devres_free(ptr); + } else { + *ptr = region; + devres_add(dev, ptr); + } + + return region; +} +EXPORT_SYMBOL_GPL(devm_fpga_region_create); + /** * fpga_region_register - register a FPGA region - * @region: FPGA region created by fpga_region_create + * @region: FPGA region + * * Return: 0 or -errno */ int fpga_region_register(struct fpga_region *region) { return device_add(®ion->dev); - } EXPORT_SYMBOL_GPL(fpga_region_register); /** - * fpga_region_unregister - unregister and free a FPGA region + * fpga_region_unregister - unregister a FPGA region * @region: FPGA region + * + * This function is intended for use in a FPGA region driver's remove function. */ void fpga_region_unregister(struct fpga_region *region) { @@ -264,9 +316,6 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister); static void fpga_region_dev_release(struct device *dev) { - struct fpga_region *region = to_fpga_region(dev); - - fpga_region_free(region); } /** diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index 052a1342ab7e..122286fd255a 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -410,7 +410,7 @@ static int of_fpga_region_probe(struct platform_device *pdev) if (IS_ERR(mgr)) return -EPROBE_DEFER; - region = fpga_region_create(dev, mgr, of_fpga_region_get_bridges); + region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges); if (!region) { ret = -ENOMEM; goto eprobe_mgr_put; @@ -418,7 +418,7 @@ static int of_fpga_region_probe(struct platform_device *pdev) ret = fpga_region_register(region); if (ret) - goto eprobe_free; + goto eprobe_mgr_put; of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); dev_set_drvdata(dev, region); @@ -427,8 +427,6 @@ static int of_fpga_region_probe(struct platform_device *pdev) return 0; -eprobe_free: - fpga_region_free(region); eprobe_mgr_put: fpga_mgr_put(mgr); return ret; diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h index 0521b7f577a4..27cb706275db 100644 --- a/include/linux/fpga/fpga-region.h +++ b/include/linux/fpga/fpga-region.h @@ -44,4 +44,8 @@ void fpga_region_free(struct fpga_region *region); int fpga_region_register(struct fpga_region *region); void fpga_region_unregister(struct fpga_region *region); +struct fpga_region +*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr, + int (*get_bridges)(struct fpga_region *)); + #endif /* _FPGA_REGION_H */ -- cgit v1.2.3-58-ga151