Files
android_kernel_motorola_sm6225/mm/backing-dev.c
Thomas Turner b49f61e801 Merge tag 'v4.19.325-cip134' of https://git.kernel.org/pub/scm/linux/kernel/git/cip/linux-cip into android13-4.19-kona
version 4.19.325-cip134

* tag 'v4.19.325-cip134' of https://git.kernel.org/pub/scm/linux/kernel/git/cip/linux-cip:
  CIP: Bump version suffix to -cip134 after merge from cip/linux-4.19.y-st tree
  Update localversion-st, tree is up-to-date with 5.10.258.
  tipc: fix double-free in tipc_buf_append()
  slip: reject VJ receive packets on instances with no rstate array
  netfilter: nfnetlink_osf: fix potential NULL dereference in ttl check
  wifi: brcmfmac: Fix error pointer dereference
  bpf: fix end-of-list detection in cgroup_storage_get_next_key()
  crypto: ccp - copy IV using skcipher ivsize
  netfilter: ipset: stop hash:* range iteration at end
  net/sched: netem: fix queue limit check to include reordered packets
  cdrom, scsi: sr: propagate read-only status to block layer via set_disk_ro()
  btrfs: fix double-decrement of bytes_may_use in submit_one_async_extent()
  crypto: authencesn - reject short ahash digests during instance creation
  crypto: ccp: Don't attempt to copy PDH cert to userspace if PSP command failed
  crypto: ccp: Don't attempt to copy CSR to userspace if PSP command failed
  x86/uprobes: Fix XOL allocation failure for 32-bit tasks
  blk-mq: use quiesced elevator switch when reinitializing queues
  scsi: ufs: core: Improve SCSI abort handling
  bcache: fix cached_dev.sb_bio use-after-free and crash
  rxrpc: proc: size address buffers for %pISpc output
  can: raw: fix ro->uniq use-after-free in raw_rcv()
  can: af_can: export can_sock_destruct()
  batman-adv: hold claim backbone gateways by reference
  l2tp: Drop large packets with UDP encap
  xsk: tighten UMEM headroom validation to account for tailroom and min frame
  can: mcp251x: add error handling for power enable in open and resume
  net: skbuff: propagate shared-frag marker through frag-transfer helpers
  Revert "i2c: fsi: Fix a potential leak in fsi_i2c_probe()"
  net: usb: lan78xx: Fix double free issue with interrupt buffer allocation
  string: add mem_is_zero() helper to check if memory area is all zeros
  tracing: Avoid NULL return from hist_field_name() on truncation
  platform/x86: intel-hid: Check ACPI_HANDLE() against NULL
  HID: quirks: really enable the intended work around for appledisplay
  net: ethernet: cs89x0: remove stale CONFIG_MACH_MX31ADS reference
  net: ethernet: cortina: Carry over frag counter
  net: ethernet: cortina: Drop half-assembled SKB
  net: ethernet: cortina: Make RX SKB per-port
  irqchip/ath79-cpu: Remove unused function
  ARM: integrator: Fix early initialization
  batman-adv: tt: fix negative tt_buff_len
  batman-adv: tt: fix negative last_changeset_len
  batman-adv: tp_meter: avoid use of uninit sender vars
  batman-adv: bla: fix report_work leak on backbone_gw purge
  batman-adv: frag: disallow unicast fragment in fragment
  batman-adv: fix tp_meter counter underflow during shutdown
  batman-adv: fix fragment reassembly length accounting
  batman-adv: dat: handle forward allocation error
  batman-adv: clear current gateway during teardown
  batman-adv: mcast: fix use-after-free in orig_node RCU release
  drm/amd/display: Fix integer overflow in bios_get_image()
  spi: ti-qspi: fix use-after-free after DMA setup failure
  scsi: isci: Fix use-after-free in device removal path
  tracing: Do not call map->ops->elt_free() if elt_alloc() fails
  ixgbevf: fix use-after-free in VEPA multicast source pruning
  ipv4: raw: reject IP_HDRINCL packets with ihl < 5
  vsock/vmci: fix UAF when peer resets connection during handshake
  netfilter: ip6t_hbh: reject oversized option lists
  net: bcmgenet: keep RBUF EEE/PM disabled
  phonet/pep: disable BH around forwarded sk_receive_skb()
  Bluetooth: hci_uart: fix UAFs and race conditions in close and init paths
  Bluetooth: bnep: Fix UAF read of dev->name
  ALSA: asihpi: Fix potential OOB array access at reading cache
  ALSA: ua101: Reject too-short USB descriptors
  sysfs: don't remove existing directory on update failure
  smb: client: reject userspace cifs.spnego descriptions
  Revert "s390/cio: Fix device lifecycle handling in css_alloc_subchannel()"
  selftests: lib.mk: Also install "config" and "settings"
  s390/debug: Reject zero-length input before trimming a newline
  net/rds: reset op_nents when zerocopy page pin fails
  drm/gma500/oaktrail_hdmi: fix i2c adapter leak on setup
  libceph: Fix potential out-of-bounds access in crush_decode()
  libceph: Fix potential null-ptr-deref in decode_choose_args()
  powerpc/warp: Fix error handling in pika_dtm_thread
  ceph: fix a buffer leak in __ceph_setxattr()
  ALSA: usb-audio: Bound MIDI endpoint descriptor scans
  audit: enforce AUDIT_LOCKED for AUDIT_TRIM and AUDIT_MAKE_EQUIV
  audit: fix incorrect inheritable capability in CAPSET records
  crypto: af_alg - Cap AEAD AD length to 0x80000000
  btrfs: tracepoints: fix sleep while in atomic context in btrfs_sync_file()
  drm/amd/display: Read EDID from VBIOS embedded panel info
  drm/amd/display: Allow DCE link encoder without AUX registers
  net/sched: sch_cake: annotate data-races in cake_dump_stats() (V)
  sctp: discard stale INIT after handshake completion
  ASoC: codecs: ab8500: Fix casting of private data
  NFC: trf7970a: Ignore antenna noise when checking for RF field
  net: usb: rtl8150: free skb on usb_submit_urb() failure in xmit
  net: usb: rtl8150: fix use-after-free in rtl8150_start_xmit()
  vrf: Fix a potential NPD when removing a port from a VRF
  net/sched: sch_choke: annotate data-races in choke_dump_stats()
  net: sched: choke: remove unused variables in struct choke_sched_data
  net/sched: netem: validate slot configuration
  net/sched: netem: fix probability gaps in 4-state loss model
  net: sched: sch_netem: Refactor code in 4-state loss generator
  scsi: sr: Add memory allocation failure handling for get_capabilities()
  netfilter: nf_conntrack_sip: don't use simple_strtoul
  netfilter: xt_policy: fix strict mode inbound policy matching
  drm/amdgpu/gfx6: Support harvested SI chips with disabled TCCs (v2)
  netfilter: arp_tables: fix IEEE1394 ARP payload parsing
  tracing: branch: Fix inverted check on stat tracer registration
  mailbox: mailbox-test: initialize struct earlier
  mailbox: mailbox-test: don't free the reused channel
  mailbox: add sanity check for channel array
  cgroup/rdma: fix integer overflow in rdmacg_try_charge()
  mailbox: mailbox-test: free channels on probe error
  fbdev: offb: fix PCI device reference leak on probe failure
  net/sched: sch_sfb: annotate data-races in sfb_dump_stats()
  net/sched: sch_fq_codel: remove data-races from fq_codel_dump_stats()
  net_sched: sch_hhf: annotate data-races in hhf_dump_stats()
  net/rds: zero per-item info buffer before handing it to visitors
  slip: bound decode() reads against the compressed packet length
  netfilter: nfnetlink_osf: fix out-of-bounds read on option matching
  ipvs: fix MTU check for GSO packets in tunnel mode
  netfilter: xtables: restrict several matches to inet family
  netfilter: conntrack: remove sprintf usage
  netfilter: nfnetlink_osf: fix divide-by-zero in OSF_WSS_MODULO
  netfilter: nft_osf: restrict it to ipv4
  openvswitch: cap upcall PID array size and pre-size vport replies
  dissector: do not set invalid PPP protocol
  sctp: fix OOB write to userspace in sctp_getsockopt_peer_auth_chunks
  ipv6: fix possible UAF in icmpv6_rcv()
  e1000e: Unroll PTP in probe error handling
  i40e: don't advertise IFF_SUPP_NOFCS
  PCMCIA: Fix garbled log messages for KERN_CONT
  clk: xgene: Fix mapping leak in xgene_pllclk_init()
  clk: qoriq: avoid format string warning
  clk: imx: imx6q: Fix device node reference leak in of_assigned_ldb_sels()
  scsi: target: core: Fix integer overflow in UNMAP bounds check
  scsi: sg: Resolve soft lockup issue when opening /dev/sgX
  RDMA/core: Prefer NLA_NUL_STRING
  nfs/blocklayout: Fix compilation error (`make W=1`) in bl_write_pagelist()
  mfd: mc13xxx-core: Fix memory leak in mc13xxx_add_subdevice_pdata()
  tty: hvc_iucv: fix off-by-one in number of supported devices
  tty: hvc: remove HVC_IUCV_MAGIC
  platform/surface: surfacepro3_button: Drop wakeup source on remove
  driver core: device.h: remove extern from function prototypes
  perf util: Kill die() prototype, dead for a long time
  pinctrl: abx500: Fix type of 'argument' variable
  pinctrl: pinctrl-pic32: Fix resource leak
  bpf: Fix precedence bug in convert_bpf_ld_abs alignment check
  HID: usbhid: fix deadlock in hid_post_reset()
  mtd: rawnand: sunxi: fix sunxi_nfc_hw_ecc_read_extra_oob
  HID: asus: do not abort probe when not necessary
  HID: asus: make asus_resume adhere to linux kernel coding standards
  ima: check return value of crypto_shash_final() in boot aggregate
  tracing: Rebuild full_name on each hist_field_name() call
  ocfs2: validate group add input before caching
  ocfs2: validate bg_bits during freefrag scan
  ocfs2: fix listxattr handling when the buffer is full
  ocfs2/dlm: fix off-by-one in dlm_match_regions() region comparison
  ocfs2/dlm: validate qr_numregions in dlm_match_regions()
  memory: tegra124-emc: Fix dll_change check
  ARM: dts: mediatek: mt7623: fix efuse fallback compatible
  efi/capsule-loader: fix incorrect sizeof in phys array reallocation
  quota: Fix race of dquot_scan_active() with quota deactivation
  ktest: Honor empty per-test option overrides
  ktest: Avoid undef warning when WARNINGS_FILE is unset
  selftest: memcg: skip memcg_sock test if address family not supported
  Documentation: fix a hugetlbfs reservation statement
  ALSA: hda/realtek: fix code style (ERROR: else should follow close brace '}')
  ALSA: hda/realtek: Whitespace fix
  drm/amd/pm/ci: Clear EnabledForActivity field for memory levels
  drm/amd/pm/ci: Fix powertune defaults for Hawaii 0x67B0
  drm/amd/pm/ci: Use highest MCLK on CI when MCLK DPM is disabled
  ALSA: core: Validate compress device numbers without dynamic minors
  ALSA: compress: Drop unused functions
  fbdev: matroxfb: Mark variable with __maybe_unused to avoid W=1 build break
  drm/sun4i: Fix resource leaks
  dm log: fix out-of-bounds write due to region_count overflow
  dm cache metadata: fix memory leak on metadata abort retry
  dm cache: fix concurrent write failure in passthrough mode
  dm cache policy smq: fix missing locks in invalidating cache blocks
  dm cache: fix write path cache coherency in passthrough mode
  dm cache: fix null-deref with concurrent writes in passthrough mode
  ASoC: sti: use managed regmap_field allocations
  ASoC: sti: Return errors from regmap_field_alloc()
  Bluetooth: hci_ldisc: Clear HCI_UART_PROTO_INIT on error
  Bluetooth: L2CAP: Fix printing wrong information if SDU length exceeds MTU
  ppp: require CAP_NET_ADMIN in target netns for unattached ioctls
  net/rds: Optimize rds_ib_laddr_check
  net: hamradio: 6pack: fix uninit-value in sixpack_receive_buf
  6pack: propagage new tty types
  netfilter: nft_fwd_netdev: check ttl/hl before forwarding
  net: bcmgenet: fix off-by-one in bcmgenet_put_txcb
  wifi: rtlwifi: pci: fix possible use-after-free caused by unfinished irq_prepare_bcn_tasklet
  firmware: dmi: Correct an indexing error in dmi.h
  locking: Fix rwlock support in <linux/spinlock_up.h>
  irqchip/irq-pic32-evic: Address warning related to wrong printf() formatter
  thermal/drivers/spear: Fix error condition for reading st,thermal-flags
  pstore/ram: fix resource leak when ioremap() fails
  nilfs2: reject zero bd_oblocknr in nilfs_ioctl_mark_blocks_dirty()
  drbd: Balance RCU calls in drbd_adm_dump_devices()
  fs/omfs: reject s_sys_blocksize smaller than OMFS_DIR_START
  Bluetooth: L2CAP: Fix null-ptr-deref in l2cap_sock_get_sndtimeo_cb()
  batman-adv: bla: put backbone reference on failed claim hash insert
  batman-adv: bla: only purge non-released claims
  batman-adv: bla: prevent use-after-free when deleting claims
  batman-adv: reject new tp_meter sessions during teardown
  batman-adv: fix integer overflow on buff_pos
  sctp: revalidate list cursor after sctp_sendmsg_to_asoc() in SCTP_SENDALL
  drm/amdgpu/pm: align Hawaii mclk workaround with radeon
  drm/amdgpu/pm: add missing revision check for CI
  drm/amdgpu/sdma4: replace BUG_ON with WARN_ON in fence emission
  drm/amdgpu/gfx9: drop unnecessary 64-bit fence flag check in KIQ
  drm/radeon: add missing revision check for CI
  spi: mpc52xx: fix use-after-free on unbind
  media: dib8000: avoid division by 0 in dib8000_set_dds()
  media: rc: streamzap: Error handling in probe
  media: uvcvideo: Enable VB2_DMABUF for metadata stream
  RDMA/rxe: Reject unknown opcodes before ICRC processing
  RDMA/ocrdma: Don't NULL deref uctx on errors in ocrdma_copy_pd_uresp()
  power: supply: max17042: avoid overflow when determining health
  PCI/AER: Stop ruling out unbound devices as error source
  s390/debug: Reject zero-length input in debug_input_flush_fn()
  nvmet: avoid recursive nvmet-wq flush in nvmet_ctrl_free
  md/raid10: fix divide-by-zero in setup_geo() with zero far_copies
  isofs: validate block number from NFS file handle in isofs_export_iget
  isofs: validate Rock Ridge CE continuation extent against volume size
  dm-verity-fec: correctly reject too-small hash devices
  dm-verity-fec: correctly reject too-small FEC devices
  dm: fix a buffer overflow in ioctl processing
  dm: don't report warning when doing deferred remove
  cpuidle: powerpc: avoid double clear when breaking snooze
  spi: topcliff-pch: fix use-after-free on unbind
  udf: reject descriptors with oversized CRC length
  ibmveth: Disable GSO for packets with small MSS
  parisc: Fix IRQ leak in LASI driver
  net/rds: handle zerocopy send cleanup before the message is queued
  ip6_gre: Use cached t->net in ip6erspan_changelink().
  sound: ua101: fix division by zero at probe
  Bluetooth: L2CAP: Fix null-ptr-deref in l2cap_sock_state_change_cb()
  Bluetooth: L2CAP: Fix null-ptr-deref in l2cap_sock_new_connection_cb()
  usb: ulpi: fix memory leak on ulpi_register() error paths
  USB: serial: option: add Telit Cinterion LE910Cx compositions
  USB: omap_udc: DMA: Don't enable burst 4 mode
  ALSA: usb-audio: Fix UAC3 cluster descriptor size check
  ALSA: usb-audio: Avoid potential endless loop in convert_chmap_v3()
  usb: usblp: fix uninitialized heap leak via LPGETSTATUS ioctl
  usb: usblp: fix heap leak in IEEE 1284 device ID via short response
  wifi: b43: enforce bounds check on firmware key index in b43_rx()
  wifi: ath5k: do not access array OOB
  wifi: rsi: fix kthread lifetime race between self-exit and external-stop
  wifi: b43legacy: enforce bounds check on firmware key index in RX path
  ipmi:ssif: NULL thread on error
  ipmi:ssif: Remove unnecessary indention
  ipmi:ssif: Clean up kthread on errors
  ipmi:ssif: Fix a shutdown race
  net/sched: sch_red: Replace direct dequeue call with peek and qdisc_dequeue_peeked
  fbdev: udlfb: add vm_ops to dlfb_ops_mmap to prevent use-after-free
  ipmi:si: Return state to normal if message allocation fails
  ipmi: Check event message buffer response for bad data
  ipmi: Add limits to event and receive message requests
  scsi: target: configfs: Bound snprintf() return in tg_pt_gp_members_show()
  ALSA: caiaq: fix usb_dev refcount leak on probe failure
  ALSA: caiaq: Don't abort when no input device is available
  ALSA: caiaq: Fix potentially leftover ep1_in_urb at error path
  dm mirror: fix integer overflow in create_dirty_log()
  crypto: atmel-tdes - fix DMA sync direction
  crypto: ccree - fix a memory leak in cc_mac_digest()
  crypto: hisilicon - Fix dma_unmap_single() direction
  crypto: atmel-ecc - Release client on allocation failure
  crypto: atmel-aes - Fix 3-page memory leak in atmel_aes_buff_cleanup
  taskstats: set version in TGID exit notifications
  inotify: fix watch count leak when fsnotify_add_inode_mark_locked() fails
  md/raid5: validate payload size before accessing journal metadata
  md/raid5: fix soft lockup in retry_aligned_read()
  ext4: fix missing brelse() in ext4_xattr_inode_dec_ref_all()
  userfaultfd: allow registration of ranges below mmap_min_addr
  tpm: tpm_tis: add error logging for data transfer
  mmc: block: use single block write in retry
  RDMA/rxe: Validate pad and ICRC before payload_size() in rxe_rcv
  ALSA: 6fire: Fix input volume change detection
  ALSA: caiaq: Handle probe errors properly
  ALSA: caiaq: Fix control_put() result and cache rollback
  ALSA: seq_oss: return full count for successful SEQ_FULLSIZE writes
  ALSA: ctxfi: Add fallback to default RSR for S/PDIF
  lib/ts_kmp: fix integer overflow in pattern length calculation
  Revert "ALSA: usb: Increase volume range that triggers a warning"
  net: strparser: fix skb_head leak in strp_abort_strp()
  net: caif: clear client service pointer on teardown
  ALSA: control: Validate buf_len before strnlen() in snd_ctl_elem_init_enum_names()
  crypto: pcrypt - Fix handling of MAY_BACKLOG requests
  um: drivers: call kernel_strrchr() explicitly in cow_user.c
  firmware: google: framebuffer: Do not mark framebuffer as busy
  ibmasm: fix heap over-read in ibmasm_send_i2o_message()
  ibmasm: fix OOB reads in command_file_write due to missing size checks
  misc: ibmasm: fix OOB MMIO read in ibmasm_handle_mouse_interrupt()
  ALSA: usb-audio: Fix Audio Advantage Micro II SPDIF switch
  ALSA: usb-audio: Avoid false E-MU sample-rate notifications
  ALSA: usb-audio: stop parsing UAC2 rates at MAX_NR_RATES
  rxrpc: Fix missing validation of ticket length in non-XDR key preparsing
  ALSA: caiaq: take a reference on the USB device in create_card()
  ALSA: usb-audio: apply quirk for MOONDROP JU Jiu
  rxrpc: Fix anonymous key handling
  scripts/dtc: Remove unused dts_version in dtc-lexer.l
  gfs2: Validate i_depth for exhash directories
  mm: blk-cgroup: fix use-after-free in cgwb_release_workfn()
  ocfs2: fix possible deadlock between unlink and dio_end_io_write
  fs/ocfs2: fix comments mentioning i_mutex
  rxrpc: reject undecryptable rxkad response tickets
  ocfs2: fix out-of-bounds write in ocfs2_write_end_inline
  ocfs2: validate inline data i_size during inode read
  ocfs2: add inline inode consistency check to ocfs2_validate_inode_block()
  xfrm: clear trailing padding in build_polexpire()
  rxrpc: fix reference count leak in rxrpc_server_keyring()
  mailbox: Prevent out-of-bounds access in of_mbox_index_xlate()
  wifi: mac80211: always free skb on ieee80211_tx_prepare_skb() failure
  ipv6: add NULL checks for idev in SRv6 paths
  net: tap: NULL pointer derefence in dev_parse_header_protocol when skb->dev is null
  media: hackrf: fix to not free memory after the device is registered in hackrf_probe()
  nilfs2: fix NULL i_assoc_inode dereference in nilfs_mdt_save_to_shadow_map
  media: as102: fix to not free memory after the device is registered in as102_usb_probe()
  ALSA: 6fire: fix use-after-free on disconnect
  media: em28xx: fix use-after-free in em28xx_v4l2_open()
  mm/kasan: fix double free for kasan pXds
  KVM: x86: Use scratch field in MMIO fragment to hold small write values
  media: uvcvideo: Allow extra entities
  Revert "wifi: cfg80211: stop NAN and P2P in cfg80211_leave"
  rxrpc: Fix call removal to use RCU safe deletion
  ACPI: property: Constify stubs for CONFIG_ACPI=n case
  ocfs2: handle invalid dinode in ocfs2_group_extend
  ocfs2: fix use-after-free in ocfs2_fault() when VM_FAULT_RETRY
  ALSA: ctxfi: Limit PTP to a single page
  USB: serial: option: add Telit Cinterion FN990A MBIM composition
  fbdev: udlfb: avoid divide-by-zero on FBIOPUT_VSCREENINFO
  usb: storage: Expand range of matched versions for VL817 quirks entry
  usbip: validate number_of_packets in usbip_pack_ret_submit()
  usb: gadget: renesas_usb3: validate endpoint index in standard request handlers
  usb: gadget: f_phonet: fix skb frags[] overflow in pn_rx_complete()
  usb: gadget: f_ncm: validate minimum block_len in ncm_unwrap_ntb()
  fbdev: tdfxfb: avoid divide-by-zero on FBIOPUT_VSCREENINFO
  ALSA: fireworks: bound device-supplied status before string array lookup
  NFC: digital: Bounds check NFC-A cascade depth in SDD response handler
  net: usb: cdc-phonet: fix skb frags[] overflow in rx_complete()
  HID: core: clamp report_size in s32ton() to avoid undefined shift
  HID: alps: fix NULL pointer dereference in alps_raw_event()
  staging: rtl8723bs: initialize le_tmp64 in rtw_BIP_verify()
  i2c: s3c24xx: check the size of the SMBUS message before using it
  nfc: llcp: add missing return after LLCP_CLOSED checks
  MIPS: mm: Suppress TLB uniquification on EHINV hardware
  af_unix: read UNIX_DIAG_VFS data under unix_state_lock
  netfilter: ip6t_eui64: reject invalid MAC header for all packets
  netfilter: xt_multiport: validate range encoding in checkentry
  netfilter: nfnetlink_log: initialize nfgenmsg in NLMSG_DONE terminator
  xfrm_user: fix info leak in build_mapping()
  e1000: check return value of e1000_read_eeprom
  net: lapbether: replace comparison to NULL with "lapbeth_get_x25_dev"
  net: lapbether: Close the LAPB device before its underlying Ethernet device closes
  net: sched: act_csum: validate nested VLAN headers
  drm/vc4: Protect madv read in vc4_gem_object_mmap() with madv_lock
  drm/vc4: Fix a memory leak in hang state error path
  drm/vc4: Fix memory leak of BO array in hang state
  ASoC: stm32_sai: fix incorrect BCLK polarity for DSP_A/B, LEFT_J
  wifi: brcmfmac: validate bsscfg indices in IF events
  ata: ahci: force 32-bit DMA for JMicron JMB582/JMB585
  HID: roccat: fix use-after-free in roccat_report_event
  HID: quirks: add HID_QUIRK_ALWAYS_POLL for 8BitDo Pro 3
  wifi: wl1251: validate packet IDs before indexing tx_frames
  btrfs: tracepoints: get correct superblock from dentry in event btrfs_sync_file()
  ALSA: asihpi: avoid write overflow check warning
  net: skbuff: preserve shared-frag marker during coalescing

Change-Id: I535ecdb1a77312a12564125fedb9f99b3a5533d1
2026-07-09 22:06:07 +01:00

1101 lines
27 KiB
C

#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>
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;
static 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;
}
DEFINE_SHOW_ATTRIBUTE(bdi_debug_stats);
static int bdi_debug_register(struct backing_dev_info *bdi, const char *name)
{
if (!bdi_debug_root)
return -ENOMEM;
bdi->debug_dir = debugfs_create_dir(name, bdi_debug_root);
if (!bdi->debug_dir)
return -ENOMEM;
bdi->debug_stats = debugfs_create_file("stats", 0444, bdi->debug_dir,
bdi, &bdi_debug_stats_fops);
if (!bdi->debug_stats) {
debugfs_remove(bdi->debug_dir);
bdi->debug_dir = NULL;
return -ENOMEM;
}
return 0;
}
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 int bdi_debug_register(struct backing_dev_info *bdi,
const char *name)
{
return 0;
}
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 bdi_init(struct backing_dev_info *bdi);
static int __init default_bdi_init(void)
{
int err;
bdi_wq = alloc_workqueue("writeback", WQ_MEM_RECLAIM | 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->dirty_sleep = jiffies;
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);
return;
}
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));
}
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);
static struct workqueue_struct *cgwb_release_wq;
/**
* 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 = rb_entry(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;
rb_link_node(&congested->rb_node, parent, node);
rb_insert_color(&congested->rb_node, &bdi->cgwb_congested_tree);
spin_unlock_irqrestore(&cgwb_lock, flags);
return congested;
}
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;
refcount_set(&new_congested->refcnt, 1);
new_congested->__bdi = bdi;
new_congested->blkcg_id = blkcg_id;
goto retry;
found:
refcount_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;
if (!refcount_dec_and_lock_irqsave(&congested->refcnt, &cgwb_lock, &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);
struct blkcg *blkcg = css_to_blkcg(wb->blkcg_css);
mutex_lock(&wb->bdi->cgwb_release_mutex);
wb_shutdown(wb);
css_put(wb->memcg_css);
/* triggers blkg destruction if cgwb_refcnt becomes zero */
blkcg_cgwb_put(blkcg);
css_put(wb->blkcg_css);
mutex_unlock(&wb->bdi->cgwb_release_mutex);
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);
queue_work(cgwb_release_wq, &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 = &memcg->cgwb_list;
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) {
ret = -ENOMEM;
goto out_put;
}
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);
blkcg_cgwb_get(blkcg);
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;
mutex_init(&bdi->cgwb_release_mutex);
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);
spin_unlock_irq(&cgwb_lock);
mutex_lock(&bdi->cgwb_release_mutex);
spin_lock_irq(&cgwb_lock);
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);
mutex_unlock(&bdi->cgwb_release_mutex);
}
/**
* 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)
{
struct list_head *memcg_cgwb_list = &memcg->cgwb_list;
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)
{
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);
}
static int __init cgwb_init(void)
{
/*
* There can be many concurrent release work items overwhelming
* system_wq. Put them in a separate wq and limit concurrency.
* There's no point in executing many of these in parallel.
*/
cgwb_release_wq = alloc_workqueue("cgwb_release", 0, 1);
if (!cgwb_release_wq)
return -ENOMEM;
return 0;
}
subsys_initcall(cgwb_init);
#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;
refcount_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 */
static 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;
}
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;
}
bdi->ra_pages = VM_READAHEAD_PAGES;
bdi->io_pages = VM_READAHEAD_PAGES;
return bdi;
}
EXPORT_SYMBOL(bdi_alloc_node);
int bdi_register_va(struct backing_dev_info *bdi, const char *fmt, va_list args)
{
struct device *dev;
if (bdi->dev) /* The driver needs to use separate queues per device */
return 0;
dev = device_create_vargs(bdi_class, NULL, MKDEV(0, 0), bdi, fmt, 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_va);
int bdi_register(struct backing_dev_info *bdi, const char *fmt, ...)
{
va_list args;
int ret;
va_start(args, fmt);
ret = bdi_register_va(bdi, fmt, args);
va_end(args);
return ret;
}
EXPORT_SYMBOL(bdi_register);
int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner)
{
int rc;
rc = bdi_register(bdi, "%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 release_bdi(struct kref *ref)
{
struct backing_dev_info *bdi =
container_of(ref, struct backing_dev_info, refcnt);
if (test_bit(WB_registered, &bdi->wb.state))
bdi_unregister(bdi);
WARN_ON_ONCE(bdi->dev);
wb_exit(&bdi->wb);
cgwb_bdi_exit(bdi);
kfree(bdi);
}
void bdi_put(struct backing_dev_info *bdi)
{
kref_put(&bdi->refcnt, release_bdi);
}
EXPORT_SYMBOL(bdi_put);
const char *bdi_dev_name(struct backing_dev_info *bdi)
{
if (!bdi || !bdi->dev)
return bdi_unknown_name;
return dev_name(bdi->dev);
}
EXPORT_SYMBOL_GPL(bdi_dev_name);
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
* @sync: SYNC or ASYNC IO
* @timeout: timeout in jiffies
*
* In the event of a congested backing_dev (any backing_dev) this waits
* for up to @timeout jiffies for either a BDI to exit congestion of the
* given @sync queue or a write to complete.
*
* 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(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, yield if necessary instead
* of sleeping on the congestion queue
*/
if (atomic_read(&nr_wb_congested[sync]) == 0) {
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);