aboutsummaryrefslogtreecommitdiffstats
path: root/net/lapb/lapb_iface.c
diff options
context:
space:
mode:
authorPaul E. McKenney <paulmck@linux.vnet.ibm.com>2014-12-13 23:32:04 -0500
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2015-01-16 02:33:14 -0500
commit5cd37193ce8539be1e6ef76be226f4bcc984e0f5 (patch)
tree7bba4e3fd57b669d60e37fd32011db4860016505 /net/lapb/lapb_iface.c
parenta94844b22a2e2b9155bbc0878c507850477221c2 (diff)
rcu: Make cond_resched_rcu_qs() apply to normal RCU flavors
Although cond_resched_rcu_qs() only applies to TASKS_RCU, it is used in places where it would be useful for it to apply to the normal RCU flavors, rcu_preempt, rcu_sched, and rcu_bh. This is especially the case for workloads that aggressively overload the system, particularly those that generate large numbers of RCU updates on systems running NO_HZ_FULL CPUs. This commit therefore communicates quiescent states from cond_resched_rcu_qs() to the normal RCU flavors. Note that it is unfortunately necessary to leave the old ->passed_quiesce mechanism in place to allow quiescent states that apply to only one flavor to be recorded. (Yes, we could decrement ->rcu_qs_ctr_snap in that case, but that is not so good for debugging of RCU internals.) In addition, if one of the RCU flavor's grace period has stalled, this will invoke rcu_momentary_dyntick_idle(), resulting in a heavy-weight quiescent state visible from other CPUs. Reported-by: Sasha Levin <sasha.levin@oracle.com> Reported-by: Dave Jones <davej@redhat.com> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> [ paulmck: Merge commit from Sasha Levin fixing a bug where __this_cpu() was used in preemptible code. ]
Diffstat (limited to 'net/lapb/lapb_iface.c')
0 files changed, 0 insertions, 0 deletions
n122' href='#n122'>122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
/*
 * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
 *
 * Copyright 2009 Analog Devices Inc.
 *
 * Loosely derived from leds-da903x:
 * Copyright (C) 2008 Compulab, Ltd.
 *	Mike Rapoport <mike@compulab.co.il>
 *
 * Copyright (C) 2006-2008 Marvell International Ltd.
 *	Eric Miao <eric.miao@marvell.com>
 *
 * Licensed under the GPL-2 or later.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/workqueue.h>
#include <linux/mfd/adp5520.h>
#include <linux/slab.h>

struct adp5520_led {
	struct led_classdev	cdev;
	struct work_struct	work;
	struct device		*master;
	enum led_brightness	new_brightness;
	int			id;
	int			flags;
};

static void adp5520_led_work(struct work_struct *work)
{
	struct adp5520_led *led = container_of(work, struct adp5520_led, work);
	adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
			 led->new_brightness >> 2);
}

static void adp5520_led_set(struct led_classdev *led_cdev,
			   enum led_brightness value)
{
	struct adp5520_led *led;

	led = container_of(led_cdev, struct adp5520_led, cdev);
	led->new_brightness = value;
	schedule_work(&led->work);
}

static int adp5520_led_setup(struct adp5520_led *led)
{
	struct device *dev = led->master;
	int flags = led->flags;
	int ret = 0;

	switch (led->id) {
	case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
		ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
					(flags >> ADP5520_FLAG_OFFT_SHIFT) &
					ADP5520_FLAG_OFFT_MASK);
		ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
					ADP5520_LED1_EN);
		break;
	case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
		ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
					((flags >> ADP5520_FLAG_OFFT_SHIFT) &
					ADP5520_FLAG_OFFT_MASK) << 2);
		ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
					 ADP5520_R3_MODE);
		ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
					ADP5520_LED2_EN);
		break;
	case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
		ret |= adp5520_set_bits(dev,  ADP5520_LED_TIME,
					((flags >> ADP5520_FLAG_OFFT_SHIFT) &
					ADP5520_FLAG_OFFT_MASK) << 4);
		ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
					ADP5520_C3_MODE);
		ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
					ADP5520_LED3_EN);
		break;
	}

	return ret;
}

static int adp5520_led_prepare(struct platform_device *pdev)
{
	struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
	struct device *dev = pdev->dev.parent;
	int ret = 0;

	ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
	ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
	ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
	ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
	ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
		 pdata->fade_out));

	return ret;
}

static int adp5520_led_probe(struct platform_device *pdev)
{
	struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
	struct adp5520_led *led, *led_dat;
	struct led_info *cur_led;
	int ret, i;

	if (pdata == NULL) {
		dev_err(&pdev->dev, "missing platform data\n");
		return -ENODEV;
	}

	if (pdata->num_leds > ADP5520_01_MAXLEDS) {
		dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
				 ADP5520_01_MAXLEDS);
		return -EFAULT;
	}

	led = devm_kzalloc(&pdev->dev, sizeof(*led) * pdata->num_leds,
				GFP_KERNEL);
	if (!led)
		return -ENOMEM;

	ret = adp5520_led_prepare(pdev);
	if (ret) {