android_kernel_sony_sdm845/mm/backing-dev.c
Michael Bestas e09f411323
Merge remote-tracking branch 'common/android-4.9-q' into lineage-18.1-caf-sdm845
* common/android-4.9-q:
  Linux 4.9.294
  xen/netback: don't queue unlimited number of packages
  xen/netback: fix rx queue stall detection
  xen/console: harden hvc_xen against event channel storms
  xen/netfront: harden netfront against event channel storms
  xen/blkfront: harden blkfront against event channel storms
  Input: touchscreen - avoid bitwise vs logical OR warning
  mwifiex: Remove unnecessary braces from HostCmd_SET_SEQ_NO_BSS_INFO
  ARM: 8805/2: remove unneeded naked function usage
  net: lan78xx: Avoid unnecessary self assignment
  scsi: scsi_debug: Sanity check block descriptor length in resp_mode_select()
  fuse: annotate lock in fuse_reverse_inval_entry()
  firmware: arm_scpi: Fix string overflow in SCPI genpd driver
  net: systemport: Add global locking for descriptor lifecycle
  timekeeping: Really make sure wall_to_monotonic isn't positive
  USB: serial: option: add Telit FN990 compositions
  PCI/MSI: Clear PCI_MSIX_FLAGS_MASKALL on error
  USB: gadget: bRequestType is a bitfield, not a enum
  ixgbe: set X550 MDIO speed before talking to PHY
  igbvf: fix double free in `igbvf_probe`
  soc/tegra: fuse: Fix bitwise vs. logical OR warning
  nfsd: fix use-after-free due to delegation race
  dm btree remove: fix use after free in rebalance_children()
  recordmcount.pl: look for jgnop instruction as well as bcrl on s390
  mac80211: send ADDBA requests using the tid/queue of the aggregation session
  hwmon: (dell-smm) Fix warning on /proc/i8k creation error
  tracing: Fix a kmemleak false positive in tracing_map
  net: netlink: af_netlink: Prevent empty skb by adding a check on len.
  i2c: rk3x: Handle a spurious start completion interrupt flag
  parisc/agp: Annotate parisc agp init functions with __init
  net/mlx4_en: Update reported link modes for 1/10G
  nfc: fix segfault in nfc_genl_dump_devices_done
  FROMGIT: USB: gadget: bRequestType is a bitfield, not a enum
  Linux 4.9.293
  irqchip: nvic: Fix offset for Interrupt Priority Offsets
  irqchip/irq-gic-v3-its.c: Force synchronisation when issuing INVALL
  irqchip/armada-370-xp: Fix support for Multi-MSI interrupts
  irqchip/armada-370-xp: Fix return value of armada_370_xp_msi_alloc()
  iio: accel: kxcjk-1013: Fix possible memory leak in probe and remove
  iio: itg3200: Call iio_trigger_notify_done() on error
  iio: kxsd9: Don't return error code in trigger handler
  iio: ltr501: Don't return error code in trigger handler
  iio: mma8452: Fix trigger reference couting
  iio: stk3310: Don't return error code in interrupt handler
  usb: core: config: using bit mask instead of individual bits
  usb: core: config: fix validation of wMaxPacketValue entries
  USB: gadget: zero allocate endpoint 0 buffers
  USB: gadget: detect too-big endpoint 0 requests
  net/qla3xxx: fix an error code in ql_adapter_up()
  net, neigh: clear whole pneigh_entry at alloc time
  net: fec: only clear interrupt of handling queue in fec_enet_rx_queue()
  net: altera: set a couple error code in probe()
  net: cdc_ncm: Allow for dwNtbOutMaxSize to be unset or zero
  block: fix ioprio_get(IOPRIO_WHO_PGRP) vs setuid(2)
  tracefs: Set all files to the same group ownership as the mount option
  signalfd: use wake_up_pollfree()
  binder: use wake_up_pollfree()
  wait: add wake_up_pollfree()
  libata: add horkage for ASMedia 1092
  can: pch_can: pch_can_rx_normal: fix use after free
  tracefs: Have new files inherit the ownership of their parent
  ALSA: pcm: oss: Handle missing errors in snd_pcm_oss_change_params*()
  ALSA: pcm: oss: Limit the period size to 16MB
  ALSA: pcm: oss: Fix negative period/buffer sizes
  ALSA: ctl: Fix copy of updated id with element read/write
  mm: bdi: initialize bdi_min_ratio when bdi is unregistered
  IB/hfi1: Correct guard on eager buffer deallocation
  nfc: fix potential NULL pointer deref in nfc_genl_dump_ses_done
  can: sja1000: fix use after free in ems_pcmcia_add_card()
  HID: check for valid USB device for many HID drivers
  HID: wacom: fix problems when device is not a valid USB device
  HID: add USB_HID dependancy on some USB HID drivers
  HID: add USB_HID dependancy to hid-chicony
  HID: add USB_HID dependancy to hid-prodikeys
  HID: add hid_is_usb() function to make it simpler for USB detection
  HID: introduce hid_is_using_ll_driver
  UPSTREAM: USB: gadget: zero allocate endpoint 0 buffers
  UPSTREAM: USB: gadget: detect too-big endpoint 0 requests
  Linux 4.9.292
  serial: core: fix transmit-buffer reset and memleak
  serial: pl011: Add ACPI SBSA UART match id
  tty: serial: msm_serial: Deactivate RX DMA for polling support
  vgacon: Propagate console boot parameters before calling `vc_resize'
  parisc: Fix "make install" on newer debian releases
  net/rds: correct socket tunable error in rds_tcp_tune()
  siphash: use _unaligned version by default
  net: qlogic: qlcnic: Fix a NULL pointer dereference in qlcnic_83xx_add_rings()
  natsemi: xtensa: fix section mismatch warnings
  fget: check that the fd still exists after getting a ref to it
  fs: add fget_many() and fput_many()
  sata_fsl: fix warning in remove_proc_entry when rmmod sata_fsl
  sata_fsl: fix UAF in sata_fsl_port_stop when rmmod sata_fsl
  kprobes: Limit max data_size of the kretprobe instances
  vrf: Reset IPCB/IP6CB when processing outbound pkts in vrf dev xmit
  net: ethernet: dec: tulip: de4x5: fix possible array overflows in type3_infoblock()
  net: tulip: de4x5: fix the problem that the array 'lp->phy[8]' may be out of bound
  ethernet: hisilicon: hns: hns_dsaf_misc: fix a possible array overflow in hns_dsaf_ge_srst_by_port()
  scsi: iscsi: Unblock session then wake up error handler
  thermal: core: Reset previous low and high trip during thermal zone init
  s390/setup: avoid using memblock_enforce_memory_limit
  platform/x86: thinkpad_acpi: Fix WWAN device disabled issue after S3 deep
  net: return correct error code
  hugetlb: take PMD sharing into account when flushing tlb/caches
  NFSv42: Fix pagecache invalidation after COPY/CLONE
  shm: extend forced shm destroy to support objects from several IPC nses
  tty: hvc: replace BUG_ON() with negative return value
  xen/netfront: don't trust the backend response data blindly
  xen/netfront: disentangle tx_skb_freelist
  xen/netfront: don't read data from request on the ring page
  xen/netfront: read response from backend only once
  xen/blkfront: don't trust the backend response data blindly
  xen/blkfront: don't take local copy of a request from the ring page
  xen/blkfront: read response from backend only once
  xen: sync include/xen/interface/io/ring.h with Xen's newest version
  fuse: release pipe buf after last use
  NFC: add NCI_UNREG flag to eliminate the race
  proc/vmcore: fix clearing user buffer by properly using clear_user()
  vhost/vsock: fix incorrect used length reported to the guest
  hugetlbfs: flush TLBs correctly after huge_pmd_unshare
  tracing: Check pid filtering when creating events
  tcp_cubic: fix spurious Hystart ACK train detections for not-cwnd-limited flows
  PM: hibernate: use correct mode for swsusp_close()
  drm/vc4: fix error code in vc4_create_object()
  scsi: mpt3sas: Fix kernel panic during drive powercycle test
  ARM: socfpga: Fix crash with CONFIG_FORTIRY_SOURCE
  NFSv42: Don't fail clone() unless the OP_CLONE operation failed
  net: ieee802154: handle iftypes as u32
  ASoC: topology: Add missing rwsem around snd_ctl_remove() calls
  ARM: dts: BCM5301X: Add interrupt properties to GPIO node
  tracing: Fix pid filtering when triggers are attached
  xen: detect uninitialized xenbus in xenbus_init
  xen: don't continue xenstore initialization in case of errors
  fuse: fix page stealing
  staging: rtl8192e: Fix use after free in _rtl92e_pci_disconnect()
  ALSA: ctxfi: Fix out-of-range access
  binder: fix test regression due to sender_euid change
  usb: hub: Fix locking issues with address0_mutex
  usb: hub: Fix usb enumeration issue due to address0 race
  USB: serial: option: add Fibocom FM101-GL variants
  USB: serial: option: add Telit LE910S1 0x9200 composition
  staging: ion: Prevent incorrect reference counting behavour
  Linux 4.9.291
  soc/tegra: pmc: Fix imbalanced clock disabling in error code path
  usb: max-3421: Use driver data instead of maintaining a list of bound devices
  ASoC: DAPM: Cover regression by kctl change notification fix
  batman-adv: Don't always reallocate the fragmentation skb head
  batman-adv: Reserve needed_*room for fragments
  batman-adv: Consider fragmentation for needed_headroom
  batman-adv: mcast: fix duplicate mcast packets from BLA backbone to mesh
  batman-adv: mcast: fix duplicate mcast packets in BLA backbone from LAN
  batman-adv: Fix own OGM check in aggregated OGMs
  batman-adv: Keep fragments equally sized
  drm/amdgpu: fix set scaling mode Full/Full aspect/Center not works on vga and dvi connectors
  drm/udl: fix control-message timeout
  cfg80211: call cfg80211_stop_ap when switch from P2P_GO type
  parisc/sticon: fix reverse colors
  btrfs: fix memory ordering between normal and ordered work functions
  mm: kmemleak: slob: respect SLAB_NOLEAKTRACE flag
  hexagon: export raw I/O routines for modules
  tun: fix bonding active backup with arp monitoring
  perf/x86/intel/uncore: Fix IIO event constraints for Skylake Server
  perf/x86/intel/uncore: Fix filter_tid mask for CHA events on Skylake Server
  NFC: reorder the logic in nfc_{un,}register_device
  NFC: reorganize the functions in nci_request
  platform/x86: hp_accel: Fix an error handling path in 'lis3lv02d_probe()'
  mips: bcm63xx: add support for clk_get_parent()
  iavf: Fix for the false positive ASQ/ARQ errors while issuing VF reset
  net: bnx2x: fix variable dereferenced before check
  sched/core: Mitigate race cpus_share_cache()/update_top_cache_domain()
  mips: BCM63XX: ensure that CPU_SUPPORTS_32BIT_KERNEL is set
  sh: define __BIG_ENDIAN for math-emu
  sh: fix kconfig unmet dependency warning for FRAME_POINTER
  maple: fix wrong return value of maple_bus_init().
  sh: check return code of request_irq
  powerpc/dcr: Use cmplwi instead of 3-argument cmpli
  ALSA: gus: fix null pointer dereference on pointer block
  powerpc/5200: dts: fix memory node unit name
  scsi: target: Fix alua_tg_pt_gps_count tracking
  scsi: target: Fix ordered tag handling
  MIPS: sni: Fix the build
  tty: tty_buffer: Fix the softlockup issue in flush_to_ldisc
  usb: host: ohci-tmio: check return value after calling platform_get_resource()
  ARM: dts: omap: fix gpmc,mux-add-data type
  scsi: advansys: Fix kernel pointer leak
  usb: musb: tusb6010: check return value after calling platform_get_resource()
  scsi: lpfc: Fix list_add() corruption in lpfc_drain_txq()
  PCI/MSI: Destroy sysfs before freeing entries
  parisc/entry: fix trace test in syscall exit path
  net: mdio-mux: fix unbalanced put_device
  PCI: Add PCI_EXP_DEVCTL_PAYLOAD_* macros
  mm, oom: do not trigger out_of_memory from the #PF
  mm, oom: pagefault_out_of_memory: don't force global OOM for dying tasks
  powerpc/bpf: Fix BPF_SUB when imm == 0x80000000
  powerpc/bpf: Validate branch ranges
  ARM: 9156/1: drop cc-option fallbacks for architecture selection
  USB: chipidea: fix interrupt deadlock
  vsock: prevent unnecessary refcnt inc for nonblocking connect
  nfc: pn533: Fix double free when pn533_fill_fragment_skbs() fails
  llc: fix out-of-bound array index in llc_sk_dev_hash()
  mm/zsmalloc.c: close race window between zs_pool_dec_isolated() and zs_unregister_migration()
  bonding: Fix a use-after-free problem when bond_sysfs_slave_add() failed
  ACPI: PMIC: Fix intel_pmic_regs_handler() read accesses
  net: davinci_emac: Fix interrupt pacing disable
  xen-pciback: Fix return in pm_ctrl_init()
  i2c: xlr: Fix a resource leak in the error handling path of 'xlr_i2c_probe()'
  scsi: qla2xxx: Turn off target reset during issue_lip
  watchdog: f71808e_wdt: fix inaccurate report in WDIOC_GETTIMEOUT
  m68k: set a default value for MEMORY_RESERVE
  dmaengine: dmaengine_desc_callback_valid(): Check for `callback_result`
  netfilter: nfnetlink_queue: fix OOB when mac header was cleared
  auxdisplay: img-ascii-lcd: Fix lock-up when displaying empty string
  dmaengine: at_xdmac: fix AT_XDMAC_CC_PERID() macro
  mtd: spi-nor: hisi-sfc: Remove excessive clk_disable_unprepare()
  fs: orangefs: fix error return code of orangefs_revalidate_lookup()
  PCI: aardvark: Don't spam about PIO Response Status
  drm/plane-helper: fix uninitialized variable reference
  pnfs/flexfiles: Fix misplaced barrier in nfs4_ff_layout_prepare_ds
  power: supply: bq27xxx: Fix kernel crash on IRQ handler register error
  serial: xilinx_uartps: Fix race condition causing stuck TX
  RDMA/mlx4: Return missed an error if device doesn't support steering
  scsi: csiostor: Uninitialized data in csio_ln_vnp_read_cbfn()
  power: supply: rt5033_battery: Change voltage values to µV
  usb: gadget: hid: fix error code in do_config()
  serial: 8250_dw: Drop wrong use of ACPI_PTR()
  video: fbdev: chipsfb: use memset_io() instead of memset()
  memory: fsl_ifc: fix leak of irq and nand_irq in fsl_ifc_ctrl_probe
  soc/tegra: Fix an error handling path in tegra_powergate_power_up()
  arm: dts: omap3-gta04a4: accelerometer irq fix
  JFS: fix memleak in jfs_mount
  MIPS: loongson64: make CPU_LOONGSON64 depends on MIPS_FP_SUPPORT
  scsi: dc395: Fix error case unwinding
  ARM: s3c: irq-s3c24xx: Fix return value check for s3c24xx_init_intc()
  RDMA/rxe: Fix wrong port_cap_flags
  crypto: pcrypt - Delay write to padata->info
  libertas: Fix possible memory leak in probe and disconnect
  libertas_tf: Fix possible memory leak in probe and disconnect
  samples/kretprobes: Fix return value if register_kretprobe() failed
  irq: mips: avoid nested irq_enter()
  s390/gmap: don't unconditionally call pte_unmap_unlock() in __gmap_zap()
  smackfs: use netlbl_cfg_cipsov4_del() for deleting cipso_v4_doi
  phy: micrel: ksz8041nl: do not use power down mode
  mwifiex: Send DELBA requests according to spec
  platform/x86: thinkpad_acpi: Fix bitwise vs. logical warning
  mmc: mxs-mmc: disable regulator on error and in the remove function
  net: stream: don't purge sk_error_queue in sk_stream_kill_queues()
  drm/msm: uninitialized variable in msm_gem_import()
  ath10k: fix max antenna gain unit
  hwmon: Fix possible memleak in __hwmon_device_register()
  memstick: jmb38x_ms: use appropriate free function in jmb38x_ms_alloc_host()
  memstick: avoid out-of-range warning
  b43: fix a lower bounds test
  b43legacy: fix a lower bounds test
  crypto: qat - disregard spurious PFVF interrupts
  crypto: qat - detect PFVF collision after ACK
  ath9k: Fix potential interrupt storm on queue reset
  cpuidle: Fix kobject memory leaks in error paths
  media: si470x: Avoid card name truncation
  media: mtk-vpu: Fix a resource leak in the error handling path of 'mtk_vpu_probe()'
  media: dvb-usb: fix ununit-value in az6027_rc_query
  cgroup: Make rebind_subsystems() disable v2 controllers all at once
  parisc/kgdb: add kgdb_roundup() to make kgdb work with idle polling
  parisc: fix warning in flush_tlb_all
  spi: bcm-qspi: Fix missing clk_disable_unprepare() on error in bcm_qspi_probe()
  ARM: 9136/1: ARMv7-M uses BE-8, not BE-32
  ARM: clang: Do not rely on lr register for stacktrace
  smackfs: use __GFP_NOFAIL for smk_cipso_doi()
  iwlwifi: mvm: disable RX-diversity in powersave
  PM: hibernate: Get block device exclusively in swsusp_check()
  mwl8k: Fix use-after-free in mwl8k_fw_state_machine()
  tracing/cfi: Fix cmp_entries_* functions signature mismatch
  lib/xz: Validate the value before assigning it to an enum variable
  lib/xz: Avoid overlapping memcpy() with invalid input with in-place decompression
  memstick: r592: Fix a UAF bug when removing the driver
  ACPI: battery: Accept charges over the design capacity as full
  ath: dfs_pattern_detector: Fix possible null-pointer dereference in channel_detector_create()
  tracefs: Have tracefs directories not set OTH permission bits by default
  media: usb: dvd-usb: fix uninit-value bug in dibusb_read_eeprom_byte()
  ACPICA: Avoid evaluating methods too early during system resume
  ia64: don't do IA64_CMPXCHG_DEBUG without CONFIG_PRINTK
  media: mceusb: return without resubmitting URB in case of -EPROTO error.
  media: s5p-mfc: fix possible null-pointer dereference in s5p_mfc_probe()
  media: uvcvideo: Set capability in s_param
  media: netup_unidvb: handle interrupt properly according to the firmware
  media: mt9p031: Fix corrupted frame after restarting stream
  x86: Increase exception stack sizes
  smackfs: Fix use-after-free in netlbl_catmap_walk()
  locking/lockdep: Avoid RCU-induced noinstr fail
  MIPS: lantiq: dma: reset correct number of channel
  MIPS: lantiq: dma: add small delay after reset
  platform/x86: wmi: do not fail if disabling fails
  Bluetooth: fix use-after-free error in lock_sock_nested()
  Bluetooth: sco: Fix lock_sock() blockage by memcpy_from_msg()
  USB: iowarrior: fix control-message timeouts
  USB: serial: keyspan: fix memleak on probe errors
  iio: dac: ad5446: Fix ad5622_write() return value
  quota: correct error number in free_dqentry()
  quota: check block number when reading the block in quota file
  PCI: aardvark: Read all 16-bits from PCIE_MSI_PAYLOAD_REG
  ALSA: mixer: fix deadlock in snd_mixer_oss_set_volume
  ALSA: mixer: oss: Fix racy access to slots
  serial: core: Fix initializing and restoring termios speed
  powerpc/85xx: Fix oops when mpc85xx_smp_guts_ids node cannot be found
  power: supply: max17042_battery: use VFSOC for capacity when no rsns
  power: supply: max17042_battery: Prevent int underflow in set_soc_threshold
  signal/mips: Update (_save|_restore)_fp_context to fail with -EFAULT
  signal: Remove the bogus sigkill_pending in ptrace_stop
  RDMA/qedr: Fix NULL deref for query_qp on the GSI QP
  wcn36xx: handle connection loss indication
  mwifiex: Read a PCI register after writing the TX ring write pointer
  wcn36xx: Fix HT40 capability for 2Ghz band
  evm: mark evm_fixmode as __ro_after_init
  rtl8187: fix control-message timeouts
  PCI: Mark Atheros QCA6174 to avoid bus reset
  ath6kl: fix control-message timeout
  ath6kl: fix division by zero in send path
  mwifiex: fix division by zero in fw download path
  EDAC/sb_edac: Fix top-of-high-memory value for Broadwell/Haswell
  regulator: dt-bindings: samsung,s5m8767: correct s5m8767,pmic-buck-default-dvs-idx property
  regulator: s5m8767: do not use reset value as DVS voltage if GPIO DVS is disabled
  hwmon: (pmbus/lm25066) Add offset coefficients
  btrfs: fix lost error handling when replaying directory deletes
  vmxnet3: do not stop tx queues after netif_device_detach()
  watchdog: Fix OMAP watchdog early handling
  spi: spl022: fix Microwire full duplex mode
  xen/netfront: stop tx queues during live migration
  bpf: Prevent increasing bpf_jit_limit above max
  mmc: winbond: don't build on M68K
  hyperv/vmbus: include linux/bitops.h
  sfc: Don't use netif_info before net_device setup
  x86/irq: Ensure PI wakeup handler is unregistered before module unload
  ALSA: timer: Unconditionally unlink slave instances, too
  ALSA: timer: Fix use-after-free problem
  ALSA: synth: missing check for possible NULL after the call to kstrdup
  ALSA: line6: fix control and interrupt message timeouts
  ALSA: 6fire: fix control and bulk message timeouts
  ALSA: ua101: fix division by zero at probe
  media: ite-cir: IR receiver stop working after receive overflow
  parisc: Fix ptrace check on syscall return
  mmc: dw_mmc: Dont wait for DRTO on Write RSP error
  ocfs2: fix data corruption on truncate
  libata: fix read log timeout value
  Input: i8042 - Add quirk for Fujitsu Lifebook T725
  Input: elantench - fix misreporting trackpoint coordinates
  xhci: Fix USB 3.1 enumeration issues by increasing roothub power-on-good delay
  binder: use cred instead of task for selinux checks
  binder: use euid from cred instead of using task
  ANDROID: arm64: process: Match upstream formatting when dumping memory areas
  FROMGIT: binder: fix test regression due to sender_euid change
  BACKPORT: binder: use cred instead of task for selinux checks
  UPSTREAM: binder: use euid from cred instead of using task
  Linux 4.9.290
  rsi: fix control-message timeout
  staging: rtl8192u: fix control-message timeouts
  staging: r8712u: fix control-message timeout
  comedi: vmk80xx: fix bulk and interrupt message timeouts
  comedi: vmk80xx: fix bulk-buffer overflow
  comedi: vmk80xx: fix transfer-buffer overflows
  comedi: ni_usb6501: fix NULL-deref in command paths
  comedi: dt9812: fix DMA buffers on stack
  isofs: Fix out of bound access for corrupted isofs image
  printk/console: Allow to disable console output by using console="" or console=null
  usb-storage: Add compatibility quirk flags for iODD 2531/2541
  usb: musb: Balance list entry in musb_gadget_queue
  usb: gadget: Mark USB_FSL_QE broken on 64-bit
  IB/qib: Protect from buffer overflow in struct qib_user_sdma_pkt fields
  IB/qib: Use struct_size() helper
  Revert "x86/kvm: fix vcpu-id indexed array sizes"
  usb: hso: fix error handling code of hso_create_net_device
  net: hso: register netdev later to avoid a race condition
  ARM: 9120/1: Revert "amba: make use of -1 IRQs warn"
  arch: pgtable: define MAX_POSSIBLE_PHYSMEM_BITS where needed
  mm/zsmalloc: Prepare to variable MAX_PHYSMEM_BITS
  scsi: core: Put LLD module refcnt after SCSI device is released
  UPSTREAM: security: selinux: allow per-file labeling for bpffs

 Conflicts:
	drivers/net/usb/lan78xx.c
	drivers/usb/core/hub.c
	drivers/usb/gadget/composite.c

Change-Id: Ifd5d502576f0a988fa885b4b1abe7d825cc40094
2021-12-27 02:07:39 +02:00

1134 lines
28 KiB
C
Executable file

#include <linux/wait.h>
#include <linux/backing-dev.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/writeback.h>
#include <linux/device.h>
#include <trace/events/writeback.h>
static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0);
struct backing_dev_info noop_backing_dev_info = {
.name = "noop",
.capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK,
};
EXPORT_SYMBOL_GPL(noop_backing_dev_info);
static struct class *bdi_class;
const char *bdi_unknown_name = "(unknown)";
/*
* bdi_lock protects updates to bdi_list. bdi_list has RCU reader side
* locking.
*/
DEFINE_SPINLOCK(bdi_lock);
LIST_HEAD(bdi_list);
/* bdi_wq serves all asynchronous writeback tasks */
struct workqueue_struct *bdi_wq;
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
#include <linux/seq_file.h>
static struct dentry *bdi_debug_root;
static void bdi_debug_init(void)
{
bdi_debug_root = debugfs_create_dir("bdi", NULL);
}
static int bdi_debug_stats_show(struct seq_file *m, void *v)
{
struct backing_dev_info *bdi = m->private;
struct bdi_writeback *wb = &bdi->wb;
unsigned long background_thresh;
unsigned long dirty_thresh;
unsigned long wb_thresh;
unsigned long nr_dirty, nr_io, nr_more_io, nr_dirty_time;
struct inode *inode;
nr_dirty = nr_io = nr_more_io = nr_dirty_time = 0;
spin_lock(&wb->list_lock);
list_for_each_entry(inode, &wb->b_dirty, i_io_list)
nr_dirty++;
list_for_each_entry(inode, &wb->b_io, i_io_list)
nr_io++;
list_for_each_entry(inode, &wb->b_more_io, i_io_list)
nr_more_io++;
list_for_each_entry(inode, &wb->b_dirty_time, i_io_list)
if (inode->i_state & I_DIRTY_TIME)
nr_dirty_time++;
spin_unlock(&wb->list_lock);
global_dirty_limits(&background_thresh, &dirty_thresh);
wb_thresh = wb_calc_thresh(wb, dirty_thresh);
#define K(x) ((x) << (PAGE_SHIFT - 10))
seq_printf(m,
"BdiWriteback: %10lu kB\n"
"BdiReclaimable: %10lu kB\n"
"BdiDirtyThresh: %10lu kB\n"
"DirtyThresh: %10lu kB\n"
"BackgroundThresh: %10lu kB\n"
"BdiDirtied: %10lu kB\n"
"BdiWritten: %10lu kB\n"
"BdiWriteBandwidth: %10lu kBps\n"
"b_dirty: %10lu\n"
"b_io: %10lu\n"
"b_more_io: %10lu\n"
"b_dirty_time: %10lu\n"
"bdi_list: %10u\n"
"state: %10lx\n",
(unsigned long) K(wb_stat(wb, WB_WRITEBACK)),
(unsigned long) K(wb_stat(wb, WB_RECLAIMABLE)),
K(wb_thresh),
K(dirty_thresh),
K(background_thresh),
(unsigned long) K(wb_stat(wb, WB_DIRTIED)),
(unsigned long) K(wb_stat(wb, WB_WRITTEN)),
(unsigned long) K(wb->write_bandwidth),
nr_dirty,
nr_io,
nr_more_io,
nr_dirty_time,
!list_empty(&bdi->bdi_list), bdi->wb.state);
#undef K
return 0;
}
static int bdi_debug_stats_open(struct inode *inode, struct file *file)
{
return single_open(file, bdi_debug_stats_show, inode->i_private);
}
static const struct file_operations bdi_debug_stats_fops = {
.open = bdi_debug_stats_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void bdi_debug_register(struct backing_dev_info *bdi, const char *name)
{
bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root);
bdi->debug_stats = debugfs_create_file("stats", 0444, bdi->debug_dir,
bdi, &bdi_debug_stats_fops);
}
static void bdi_debug_unregister(struct backing_dev_info *bdi)
{
debugfs_remove(bdi->debug_stats);
debugfs_remove(bdi->debug_dir);
}
#else
static inline void bdi_debug_init(void)
{
}
static inline void bdi_debug_register(struct backing_dev_info *bdi,
const char *name)
{
}
static inline void bdi_debug_unregister(struct backing_dev_info *bdi)
{
}
#endif
static ssize_t read_ahead_kb_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct backing_dev_info *bdi = dev_get_drvdata(dev);
unsigned long read_ahead_kb;
ssize_t ret;
ret = kstrtoul(buf, 10, &read_ahead_kb);
if (ret < 0)
return ret;
bdi->ra_pages = read_ahead_kb >> (PAGE_SHIFT - 10);
return count;
}
#define K(pages) ((pages) << (PAGE_SHIFT - 10))
#define BDI_SHOW(name, expr) \
static ssize_t name##_show(struct device *dev, \
struct device_attribute *attr, char *page) \
{ \
struct backing_dev_info *bdi = dev_get_drvdata(dev); \
\
return snprintf(page, PAGE_SIZE-1, "%lld\n", (long long)expr); \
} \
static DEVICE_ATTR_RW(name);
BDI_SHOW(read_ahead_kb, K(bdi->ra_pages))
static ssize_t min_ratio_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct backing_dev_info *bdi = dev_get_drvdata(dev);
unsigned int ratio;
ssize_t ret;
ret = kstrtouint(buf, 10, &ratio);
if (ret < 0)
return ret;
ret = bdi_set_min_ratio(bdi, ratio);
if (!ret)
ret = count;
return ret;
}
BDI_SHOW(min_ratio, bdi->min_ratio)
static ssize_t max_ratio_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct backing_dev_info *bdi = dev_get_drvdata(dev);
unsigned int ratio;
ssize_t ret;
ret = kstrtouint(buf, 10, &ratio);
if (ret < 0)
return ret;
ret = bdi_set_max_ratio(bdi, ratio);
if (!ret)
ret = count;
return ret;
}
BDI_SHOW(max_ratio, bdi->max_ratio)
static ssize_t stable_pages_required_show(struct device *dev,
struct device_attribute *attr,
char *page)
{
struct backing_dev_info *bdi = dev_get_drvdata(dev);
return snprintf(page, PAGE_SIZE-1, "%d\n",
bdi_cap_stable_pages_required(bdi) ? 1 : 0);
}
static DEVICE_ATTR_RO(stable_pages_required);
static struct attribute *bdi_dev_attrs[] = {
&dev_attr_read_ahead_kb.attr,
&dev_attr_min_ratio.attr,
&dev_attr_max_ratio.attr,
&dev_attr_stable_pages_required.attr,
NULL,
};
ATTRIBUTE_GROUPS(bdi_dev);
static __init int bdi_class_init(void)
{
bdi_class = class_create(THIS_MODULE, "bdi");
if (IS_ERR(bdi_class))
return PTR_ERR(bdi_class);
bdi_class->dev_groups = bdi_dev_groups;
bdi_debug_init();
return 0;
}
postcore_initcall(bdi_class_init);
static int __init default_bdi_init(void)
{
int err;
bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | WQ_FREEZABLE |
WQ_UNBOUND | WQ_SYSFS, 0);
if (!bdi_wq)
return -ENOMEM;
err = bdi_init(&noop_backing_dev_info);
return err;
}
subsys_initcall(default_bdi_init);
/*
* This function is used when the first inode for this wb is marked dirty. It
* wakes-up the corresponding bdi thread which should then take care of the
* periodic background write-out of dirty inodes. Since the write-out would
* starts only 'dirty_writeback_interval' centisecs from now anyway, we just
* set up a timer which wakes the bdi thread up later.
*
* Note, we wouldn't bother setting up the timer, but this function is on the
* fast-path (used by '__mark_inode_dirty()'), so we save few context switches
* by delaying the wake-up.
*
* We have to be careful not to postpone flush work if it is scheduled for
* earlier. Thus we use queue_delayed_work().
*/
void wb_wakeup_delayed(struct bdi_writeback *wb)
{
unsigned long timeout;
timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
spin_lock_bh(&wb->work_lock);
if (test_bit(WB_registered, &wb->state))
queue_delayed_work(bdi_wq, &wb->dwork, timeout);
spin_unlock_bh(&wb->work_lock);
}
/*
* Initial write bandwidth: 100 MB/s
*/
#define INIT_BW (100 << (20 - PAGE_SHIFT))
static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,
int blkcg_id, gfp_t gfp)
{
int i, err;
memset(wb, 0, sizeof(*wb));
if (wb != &bdi->wb)
bdi_get(bdi);
wb->bdi = bdi;
wb->last_old_flush = jiffies;
INIT_LIST_HEAD(&wb->b_dirty);
INIT_LIST_HEAD(&wb->b_io);
INIT_LIST_HEAD(&wb->b_more_io);
INIT_LIST_HEAD(&wb->b_dirty_time);
spin_lock_init(&wb->list_lock);
wb->bw_time_stamp = jiffies;
wb->balanced_dirty_ratelimit = INIT_BW;
wb->dirty_ratelimit = INIT_BW;
wb->write_bandwidth = INIT_BW;
wb->avg_write_bandwidth = INIT_BW;
spin_lock_init(&wb->work_lock);
INIT_LIST_HEAD(&wb->work_list);
INIT_DELAYED_WORK(&wb->dwork, wb_workfn);
wb->congested = wb_congested_get_create(bdi, blkcg_id, gfp);
if (!wb->congested) {
err = -ENOMEM;
goto out_put_bdi;
}
err = fprop_local_init_percpu(&wb->completions, gfp);
if (err)
goto out_put_cong;
for (i = 0; i < NR_WB_STAT_ITEMS; i++) {
err = percpu_counter_init(&wb->stat[i], 0, gfp);
if (err)
goto out_destroy_stat;
}
return 0;
out_destroy_stat:
while (i--)
percpu_counter_destroy(&wb->stat[i]);
fprop_local_destroy_percpu(&wb->completions);
out_put_cong:
wb_congested_put(wb->congested);
out_put_bdi:
if (wb != &bdi->wb)
bdi_put(bdi);
return err;
}
static void cgwb_remove_from_bdi_list(struct bdi_writeback *wb);
/*
* Remove bdi from the global list and shutdown any threads we have running
*/
static void wb_shutdown(struct bdi_writeback *wb)
{
/* Make sure nobody queues further work */
spin_lock_bh(&wb->work_lock);
if (!test_and_clear_bit(WB_registered, &wb->state)) {
spin_unlock_bh(&wb->work_lock);
/*
* Wait for wb shutdown to finish if someone else is just
* running wb_shutdown(). Otherwise we could proceed to wb /
* bdi destruction before wb_shutdown() is finished.
*/
wait_on_bit(&wb->state, WB_shutting_down, TASK_UNINTERRUPTIBLE);
return;
}
set_bit(WB_shutting_down, &wb->state);
spin_unlock_bh(&wb->work_lock);
cgwb_remove_from_bdi_list(wb);
/*
* Drain work list and shutdown the delayed_work. !WB_registered
* tells wb_workfn() that @wb is dying and its work_list needs to
* be drained no matter what.
*/
mod_delayed_work(bdi_wq, &wb->dwork, 0);
flush_delayed_work(&wb->dwork);
WARN_ON(!list_empty(&wb->work_list));
/*
* Make sure bit gets cleared after shutdown is finished. Matches with
* the barrier provided by test_and_clear_bit() above.
*/
smp_wmb();
clear_bit(WB_shutting_down, &wb->state);
}
static void wb_exit(struct bdi_writeback *wb)
{
int i;
WARN_ON(delayed_work_pending(&wb->dwork));
for (i = 0; i < NR_WB_STAT_ITEMS; i++)
percpu_counter_destroy(&wb->stat[i]);
fprop_local_destroy_percpu(&wb->completions);
wb_congested_put(wb->congested);
if (wb != &wb->bdi->wb)
bdi_put(wb->bdi);
}
#ifdef CONFIG_CGROUP_WRITEBACK
#include <linux/memcontrol.h>
/*
* cgwb_lock protects bdi->cgwb_tree, bdi->cgwb_congested_tree,
* blkcg->cgwb_list, and memcg->cgwb_list. bdi->cgwb_tree is also RCU
* protected.
*/
static DEFINE_SPINLOCK(cgwb_lock);
/**
* wb_congested_get_create - get or create a wb_congested
* @bdi: associated bdi
* @blkcg_id: ID of the associated blkcg
* @gfp: allocation mask
*
* Look up the wb_congested for @blkcg_id on @bdi. If missing, create one.
* The returned wb_congested has its reference count incremented. Returns
* NULL on failure.
*/
struct bdi_writeback_congested *
wb_congested_get_create(struct backing_dev_info *bdi, int blkcg_id, gfp_t gfp)
{
struct bdi_writeback_congested *new_congested = NULL, *congested;
struct rb_node **node, *parent;
unsigned long flags;
retry:
spin_lock_irqsave(&cgwb_lock, flags);
node = &bdi->cgwb_congested_tree.rb_node;
parent = NULL;
while (*node != NULL) {
parent = *node;
congested = container_of(parent, struct bdi_writeback_congested,
rb_node);
if (congested->blkcg_id < blkcg_id)
node = &parent->rb_left;
else if (congested->blkcg_id > blkcg_id)
node = &parent->rb_right;
else
goto found;
}
if (new_congested) {
/* !found and storage for new one already allocated, insert */
congested = new_congested;
new_congested = NULL;
rb_link_node(&congested->rb_node, parent, node);
rb_insert_color(&congested->rb_node, &bdi->cgwb_congested_tree);
goto found;
}
spin_unlock_irqrestore(&cgwb_lock, flags);
/* allocate storage for new one and retry */
new_congested = kzalloc(sizeof(*new_congested), gfp);
if (!new_congested)
return NULL;
atomic_set(&new_congested->refcnt, 0);
new_congested->__bdi = bdi;
new_congested->blkcg_id = blkcg_id;
goto retry;
found:
atomic_inc(&congested->refcnt);
spin_unlock_irqrestore(&cgwb_lock, flags);
kfree(new_congested);
return congested;
}
/**
* wb_congested_put - put a wb_congested
* @congested: wb_congested to put
*
* Put @congested and destroy it if the refcnt reaches zero.
*/
void wb_congested_put(struct bdi_writeback_congested *congested)
{
unsigned long flags;
local_irq_save(flags);
if (!atomic_dec_and_lock(&congested->refcnt, &cgwb_lock)) {
local_irq_restore(flags);
return;
}
/* bdi might already have been destroyed leaving @congested unlinked */
if (congested->__bdi) {
rb_erase(&congested->rb_node,
&congested->__bdi->cgwb_congested_tree);
congested->__bdi = NULL;
}
spin_unlock_irqrestore(&cgwb_lock, flags);
kfree(congested);
}
static void cgwb_release_workfn(struct work_struct *work)
{
struct bdi_writeback *wb = container_of(work, struct bdi_writeback,
release_work);
wb_shutdown(wb);
css_put(wb->memcg_css);
css_put(wb->blkcg_css);
fprop_local_destroy_percpu(&wb->memcg_completions);
percpu_ref_exit(&wb->refcnt);
wb_exit(wb);
kfree_rcu(wb, rcu);
}
static void cgwb_release(struct percpu_ref *refcnt)
{
struct bdi_writeback *wb = container_of(refcnt, struct bdi_writeback,
refcnt);
schedule_work(&wb->release_work);
}
static void cgwb_kill(struct bdi_writeback *wb)
{
lockdep_assert_held(&cgwb_lock);
WARN_ON(!radix_tree_delete(&wb->bdi->cgwb_tree, wb->memcg_css->id));
list_del(&wb->memcg_node);
list_del(&wb->blkcg_node);
percpu_ref_kill(&wb->refcnt);
}
static void cgwb_remove_from_bdi_list(struct bdi_writeback *wb)
{
spin_lock_irq(&cgwb_lock);
list_del_rcu(&wb->bdi_node);
spin_unlock_irq(&cgwb_lock);
}
static int cgwb_create(struct backing_dev_info *bdi,
struct cgroup_subsys_state *memcg_css, gfp_t gfp)
{
struct mem_cgroup *memcg;
struct cgroup_subsys_state *blkcg_css;
struct blkcg *blkcg;
struct list_head *memcg_cgwb_list, *blkcg_cgwb_list;
struct bdi_writeback *wb;
unsigned long flags;
int ret = 0;
memcg = mem_cgroup_from_css(memcg_css);
blkcg_css = cgroup_get_e_css(memcg_css->cgroup, &io_cgrp_subsys);
blkcg = css_to_blkcg(blkcg_css);
memcg_cgwb_list = mem_cgroup_cgwb_list(memcg);
blkcg_cgwb_list = &blkcg->cgwb_list;
/* look up again under lock and discard on blkcg mismatch */
spin_lock_irqsave(&cgwb_lock, flags);
wb = radix_tree_lookup(&bdi->cgwb_tree, memcg_css->id);
if (wb && wb->blkcg_css != blkcg_css) {
cgwb_kill(wb);
wb = NULL;
}
spin_unlock_irqrestore(&cgwb_lock, flags);
if (wb)
goto out_put;
/* need to create a new one */
wb = kmalloc(sizeof(*wb), gfp);
if (!wb)
return -ENOMEM;
ret = wb_init(wb, bdi, blkcg_css->id, gfp);
if (ret)
goto err_free;
ret = percpu_ref_init(&wb->refcnt, cgwb_release, 0, gfp);
if (ret)
goto err_wb_exit;
ret = fprop_local_init_percpu(&wb->memcg_completions, gfp);
if (ret)
goto err_ref_exit;
wb->memcg_css = memcg_css;
wb->blkcg_css = blkcg_css;
INIT_WORK(&wb->release_work, cgwb_release_workfn);
set_bit(WB_registered, &wb->state);
/*
* The root wb determines the registered state of the whole bdi and
* memcg_cgwb_list and blkcg_cgwb_list's next pointers indicate
* whether they're still online. Don't link @wb if any is dead.
* See wb_memcg_offline() and wb_blkcg_offline().
*/
ret = -ENODEV;
spin_lock_irqsave(&cgwb_lock, flags);
if (test_bit(WB_registered, &bdi->wb.state) &&
blkcg_cgwb_list->next && memcg_cgwb_list->next) {
/* we might have raced another instance of this function */
ret = radix_tree_insert(&bdi->cgwb_tree, memcg_css->id, wb);
if (!ret) {
list_add_tail_rcu(&wb->bdi_node, &bdi->wb_list);
list_add(&wb->memcg_node, memcg_cgwb_list);
list_add(&wb->blkcg_node, blkcg_cgwb_list);
css_get(memcg_css);
css_get(blkcg_css);
}
}
spin_unlock_irqrestore(&cgwb_lock, flags);
if (ret) {
if (ret == -EEXIST)
ret = 0;
goto err_fprop_exit;
}
goto out_put;
err_fprop_exit:
fprop_local_destroy_percpu(&wb->memcg_completions);
err_ref_exit:
percpu_ref_exit(&wb->refcnt);
err_wb_exit:
wb_exit(wb);
err_free:
kfree(wb);
out_put:
css_put(blkcg_css);
return ret;
}
/**
* wb_get_create - get wb for a given memcg, create if necessary
* @bdi: target bdi
* @memcg_css: cgroup_subsys_state of the target memcg (must have positive ref)
* @gfp: allocation mask to use
*
* Try to get the wb for @memcg_css on @bdi. If it doesn't exist, try to
* create one. The returned wb has its refcount incremented.
*
* This function uses css_get() on @memcg_css and thus expects its refcnt
* to be positive on invocation. IOW, rcu_read_lock() protection on
* @memcg_css isn't enough. try_get it before calling this function.
*
* A wb is keyed by its associated memcg. As blkcg implicitly enables
* memcg on the default hierarchy, memcg association is guaranteed to be
* more specific (equal or descendant to the associated blkcg) and thus can
* identify both the memcg and blkcg associations.
*
* Because the blkcg associated with a memcg may change as blkcg is enabled
* and disabled closer to root in the hierarchy, each wb keeps track of
* both the memcg and blkcg associated with it and verifies the blkcg on
* each lookup. On mismatch, the existing wb is discarded and a new one is
* created.
*/
struct bdi_writeback *wb_get_create(struct backing_dev_info *bdi,
struct cgroup_subsys_state *memcg_css,
gfp_t gfp)
{
struct bdi_writeback *wb;
might_sleep_if(gfpflags_allow_blocking(gfp));
if (!memcg_css->parent)
return &bdi->wb;
do {
rcu_read_lock();
wb = radix_tree_lookup(&bdi->cgwb_tree, memcg_css->id);
if (wb) {
struct cgroup_subsys_state *blkcg_css;
/* see whether the blkcg association has changed */
blkcg_css = cgroup_get_e_css(memcg_css->cgroup,
&io_cgrp_subsys);
if (unlikely(wb->blkcg_css != blkcg_css ||
!wb_tryget(wb)))
wb = NULL;
css_put(blkcg_css);
}
rcu_read_unlock();
} while (!wb && !cgwb_create(bdi, memcg_css, gfp));
return wb;
}
static int cgwb_bdi_init(struct backing_dev_info *bdi)
{
int ret;
INIT_RADIX_TREE(&bdi->cgwb_tree, GFP_ATOMIC);
bdi->cgwb_congested_tree = RB_ROOT;
atomic_set(&bdi->usage_cnt, 1);
init_rwsem(&bdi->wb_switch_rwsem);
ret = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL);
if (!ret) {
bdi->wb.memcg_css = &root_mem_cgroup->css;
bdi->wb.blkcg_css = blkcg_root_css;
}
return ret;
}
static void cgwb_bdi_unregister(struct backing_dev_info *bdi)
{
struct radix_tree_iter iter;
void **slot;
struct bdi_writeback *wb;
WARN_ON(test_bit(WB_registered, &bdi->wb.state));
spin_lock_irq(&cgwb_lock);
radix_tree_for_each_slot(slot, &bdi->cgwb_tree, &iter, 0)
cgwb_kill(*slot);
while (!list_empty(&bdi->wb_list)) {
wb = list_first_entry(&bdi->wb_list, struct bdi_writeback,
bdi_node);
spin_unlock_irq(&cgwb_lock);
wb_shutdown(wb);
spin_lock_irq(&cgwb_lock);
}
spin_unlock_irq(&cgwb_lock);
}
/**
* wb_memcg_offline - kill all wb's associated with a memcg being offlined
* @memcg: memcg being offlined
*
* Also prevents creation of any new wb's associated with @memcg.
*/
void wb_memcg_offline(struct mem_cgroup *memcg)
{
LIST_HEAD(to_destroy);
struct list_head *memcg_cgwb_list = mem_cgroup_cgwb_list(memcg);
struct bdi_writeback *wb, *next;
spin_lock_irq(&cgwb_lock);
list_for_each_entry_safe(wb, next, memcg_cgwb_list, memcg_node)
cgwb_kill(wb);
memcg_cgwb_list->next = NULL; /* prevent new wb's */
spin_unlock_irq(&cgwb_lock);
}
/**
* wb_blkcg_offline - kill all wb's associated with a blkcg being offlined
* @blkcg: blkcg being offlined
*
* Also prevents creation of any new wb's associated with @blkcg.
*/
void wb_blkcg_offline(struct blkcg *blkcg)
{
LIST_HEAD(to_destroy);
struct bdi_writeback *wb, *next;
spin_lock_irq(&cgwb_lock);
list_for_each_entry_safe(wb, next, &blkcg->cgwb_list, blkcg_node)
cgwb_kill(wb);
blkcg->cgwb_list.next = NULL; /* prevent new wb's */
spin_unlock_irq(&cgwb_lock);
}
static void cgwb_bdi_exit(struct backing_dev_info *bdi)
{
struct rb_node *rbn;
spin_lock_irq(&cgwb_lock);
while ((rbn = rb_first(&bdi->cgwb_congested_tree))) {
struct bdi_writeback_congested *congested =
rb_entry(rbn, struct bdi_writeback_congested, rb_node);
rb_erase(rbn, &bdi->cgwb_congested_tree);
congested->__bdi = NULL; /* mark @congested unlinked */
}
spin_unlock_irq(&cgwb_lock);
}
static void cgwb_bdi_register(struct backing_dev_info *bdi)
{
spin_lock_irq(&cgwb_lock);
list_add_tail_rcu(&bdi->wb.bdi_node, &bdi->wb_list);
spin_unlock_irq(&cgwb_lock);
}
#else /* CONFIG_CGROUP_WRITEBACK */
static int cgwb_bdi_init(struct backing_dev_info *bdi)
{
int err;
bdi->wb_congested = kzalloc(sizeof(*bdi->wb_congested), GFP_KERNEL);
if (!bdi->wb_congested)
return -ENOMEM;
atomic_set(&bdi->wb_congested->refcnt, 1);
err = wb_init(&bdi->wb, bdi, 1, GFP_KERNEL);
if (err) {
wb_congested_put(bdi->wb_congested);
return err;
}
return 0;
}
static void cgwb_bdi_unregister(struct backing_dev_info *bdi) { }
static void cgwb_bdi_exit(struct backing_dev_info *bdi)
{
wb_congested_put(bdi->wb_congested);
}
static void cgwb_bdi_register(struct backing_dev_info *bdi)
{
list_add_tail_rcu(&bdi->wb.bdi_node, &bdi->wb_list);
}
static void cgwb_remove_from_bdi_list(struct bdi_writeback *wb)
{
list_del_rcu(&wb->bdi_node);
}
#endif /* CONFIG_CGROUP_WRITEBACK */
int bdi_init(struct backing_dev_info *bdi)
{
int ret;
bdi->dev = NULL;
kref_init(&bdi->refcnt);
bdi->min_ratio = 0;
bdi->max_ratio = 100;
bdi->max_prop_frac = FPROP_FRAC_BASE;
INIT_LIST_HEAD(&bdi->bdi_list);
INIT_LIST_HEAD(&bdi->wb_list);
init_waitqueue_head(&bdi->wb_waitq);
ret = cgwb_bdi_init(bdi);
return ret;
}
EXPORT_SYMBOL(bdi_init);
struct backing_dev_info *bdi_alloc_node(gfp_t gfp_mask, int node_id)
{
struct backing_dev_info *bdi;
bdi = kmalloc_node(sizeof(struct backing_dev_info),
gfp_mask | __GFP_ZERO, node_id);
if (!bdi)
return NULL;
if (bdi_init(bdi)) {
kfree(bdi);
return NULL;
}
return bdi;
}
int bdi_register(struct backing_dev_info *bdi, struct device *parent,
const char *fmt, ...)
{
va_list args;
struct device *dev;
if (bdi->dev) /* The driver needs to use separate queues per device */
return 0;
va_start(args, fmt);
dev = device_create_vargs(bdi_class, parent, MKDEV(0, 0), bdi, fmt, args);
va_end(args);
if (IS_ERR(dev))
return PTR_ERR(dev);
cgwb_bdi_register(bdi);
bdi->dev = dev;
bdi_debug_register(bdi, dev_name(dev));
set_bit(WB_registered, &bdi->wb.state);
spin_lock_bh(&bdi_lock);
list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
spin_unlock_bh(&bdi_lock);
trace_writeback_bdi_register(bdi);
return 0;
}
EXPORT_SYMBOL(bdi_register);
int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev)
{
return bdi_register(bdi, NULL, "%u:%u", MAJOR(dev), MINOR(dev));
}
EXPORT_SYMBOL(bdi_register_dev);
int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner)
{
int rc;
rc = bdi_register(bdi, NULL, "%u:%u", MAJOR(owner->devt),
MINOR(owner->devt));
if (rc)
return rc;
/* Leaking owner reference... */
WARN_ON(bdi->owner);
bdi->owner = owner;
get_device(owner);
return 0;
}
EXPORT_SYMBOL(bdi_register_owner);
/*
* Remove bdi from bdi_list, and ensure that it is no longer visible
*/
static void bdi_remove_from_list(struct backing_dev_info *bdi)
{
spin_lock_bh(&bdi_lock);
list_del_rcu(&bdi->bdi_list);
spin_unlock_bh(&bdi_lock);
synchronize_rcu_expedited();
}
void bdi_unregister(struct backing_dev_info *bdi)
{
/* make sure nobody finds us on the bdi_list anymore */
bdi_remove_from_list(bdi);
wb_shutdown(&bdi->wb);
cgwb_bdi_unregister(bdi);
/*
* If this BDI's min ratio has been set, use bdi_set_min_ratio() to
* update the global bdi_min_ratio.
*/
if (bdi->min_ratio)
bdi_set_min_ratio(bdi, 0);
if (bdi->dev) {
bdi_debug_unregister(bdi);
device_unregister(bdi->dev);
bdi->dev = NULL;
}
if (bdi->owner) {
put_device(bdi->owner);
bdi->owner = NULL;
}
}
static void bdi_exit(struct backing_dev_info *bdi)
{
WARN_ON_ONCE(bdi->dev);
wb_exit(&bdi->wb);
cgwb_bdi_exit(bdi);
}
static void release_bdi(struct kref *ref)
{
struct backing_dev_info *bdi =
container_of(ref, struct backing_dev_info, refcnt);
bdi_exit(bdi);
kfree(bdi);
}
void bdi_put(struct backing_dev_info *bdi)
{
kref_put(&bdi->refcnt, release_bdi);
}
void bdi_destroy(struct backing_dev_info *bdi)
{
bdi_unregister(bdi);
bdi_exit(bdi);
}
EXPORT_SYMBOL(bdi_destroy);
/*
* For use from filesystems to quickly init and register a bdi associated
* with dirty writeback
*/
int bdi_setup_and_register(struct backing_dev_info *bdi, char *name)
{
int err;
bdi->name = name;
bdi->capabilities = 0;
err = bdi_init(bdi);
if (err)
return err;
err = bdi_register(bdi, NULL, "%.28s-%ld", name,
atomic_long_inc_return(&bdi_seq));
if (err) {
bdi_destroy(bdi);
return err;
}
return 0;
}
EXPORT_SYMBOL(bdi_setup_and_register);
static wait_queue_head_t congestion_wqh[2] = {
__WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[0]),
__WAIT_QUEUE_HEAD_INITIALIZER(congestion_wqh[1])
};
static atomic_t nr_wb_congested[2];
void clear_wb_congested(struct bdi_writeback_congested *congested, int sync)
{
wait_queue_head_t *wqh = &congestion_wqh[sync];
enum wb_congested_state bit;
bit = sync ? WB_sync_congested : WB_async_congested;
if (test_and_clear_bit(bit, &congested->state))
atomic_dec(&nr_wb_congested[sync]);
smp_mb__after_atomic();
if (waitqueue_active(wqh))
wake_up(wqh);
}
EXPORT_SYMBOL(clear_wb_congested);
void set_wb_congested(struct bdi_writeback_congested *congested, int sync)
{
enum wb_congested_state bit;
bit = sync ? WB_sync_congested : WB_async_congested;
if (!test_and_set_bit(bit, &congested->state))
atomic_inc(&nr_wb_congested[sync]);
}
EXPORT_SYMBOL(set_wb_congested);
/**
* congestion_wait - wait for a backing_dev to become uncongested
* @sync: SYNC or ASYNC IO
* @timeout: timeout in jiffies
*
* Waits for up to @timeout jiffies for a backing_dev (any backing_dev) to exit
* write congestion. If no backing_devs are congested then just wait for the
* next write to be completed.
*/
long congestion_wait(int sync, long timeout)
{
long ret;
unsigned long start = jiffies;
DEFINE_WAIT(wait);
wait_queue_head_t *wqh = &congestion_wqh[sync];
prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
ret = io_schedule_timeout(timeout);
finish_wait(wqh, &wait);
trace_writeback_congestion_wait(jiffies_to_usecs(timeout),
jiffies_to_usecs(jiffies - start));
return ret;
}
EXPORT_SYMBOL(congestion_wait);
/**
* wait_iff_congested - Conditionally wait for a backing_dev to become uncongested or a pgdat to complete writes
* @pgdat: A pgdat to check if it is heavily congested
* @sync: SYNC or ASYNC IO
* @timeout: timeout in jiffies
*
* In the event of a congested backing_dev (any backing_dev) and the given
* @pgdat has experienced recent congestion, this waits for up to @timeout
* jiffies for either a BDI to exit congestion of the given @sync queue
* or a write to complete.
*
* In the absence of pgdat congestion, cond_resched() is called to yield
* the processor if necessary but otherwise does not sleep.
*
* The return value is 0 if the sleep is for the full timeout. Otherwise,
* it is the number of jiffies that were still remaining when the function
* returned. return_value == timeout implies the function did not sleep.
*/
long wait_iff_congested(struct pglist_data *pgdat, int sync, long timeout)
{
long ret;
unsigned long start = jiffies;
DEFINE_WAIT(wait);
wait_queue_head_t *wqh = &congestion_wqh[sync];
/*
* If there is no congestion, or heavy congestion is not being
* encountered in the current pgdat, yield if necessary instead
* of sleeping on the congestion queue
*/
if (atomic_read(&nr_wb_congested[sync]) == 0 ||
!test_bit(PGDAT_CONGESTED, &pgdat->flags)) {
cond_resched();
/* In case we scheduled, work out time remaining */
ret = timeout - (jiffies - start);
if (ret < 0)
ret = 0;
goto out;
}
/* Sleep until uncongested or a write happens */
prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
ret = io_schedule_timeout(timeout);
finish_wait(wqh, &wait);
out:
trace_writeback_wait_iff_congested(jiffies_to_usecs(timeout),
jiffies_to_usecs(jiffies - start));
return ret;
}
EXPORT_SYMBOL(wait_iff_congested);
int pdflush_proc_obsolete(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
char kbuf[] = "0\n";
if (*ppos || *lenp < sizeof(kbuf)) {
*lenp = 0;
return 0;
}
if (copy_to_user(buffer, kbuf, sizeof(kbuf)))
return -EFAULT;
pr_warn_once("%s exported in /proc is scheduled for removal\n",
table->procname);
*lenp = 2;
*ppos += *lenp;
return 2;
}