msm-4.14/drivers/mtd/mtdpart.c
Srinivasarao P f3f0576c22 Merge android-4.14.158 (84afceb) into msm-4.14
* refs/heads/tmp-84afceb:
  Linux 4.14.158
  net: fec: fix clock count mis-match
  platform/x86: hp-wmi: Fix ACPI errors caused by passing 0 as input size
  platform/x86: hp-wmi: Fix ACPI errors caused by too small buffer
  ASoC: stm32: i2s: fix IRQ clearing
  ASoC: stm32: i2s: fix 16 bit format support
  ASoC: stm32: i2s: fix dma configuration
  pinctrl: stm32: fix memory leak issue
  mailbox: mailbox-test: fix null pointer if no mmio
  hwrng: stm32 - fix unbalanced pm_runtime_enable
  media: stm32-dcmi: fix DMA corruption when stopping streaming
  crypto: stm32/hash - Fix hmac issue more than 256 bytes
  HID: core: check whether Usage Page item is after Usage ID items
  futex: Prevent exit livelock
  futex: Provide distinct return value when owner is exiting
  futex: Add mutex around futex exit
  futex: Provide state handling for exec() as well
  futex: Sanitize exit state handling
  futex: Mark the begin of futex exit explicitly
  futex: Set task::futex_state to DEAD right after handling futex exit
  futex: Split futex_mm_release() for exit/exec
  exit/exec: Seperate mm_release()
  futex: Replace PF_EXITPIDONE with a state
  futex: Move futex exit handling into futex code
  futex: Prevent robust futex exit race
  y2038: futex: Move compat implementation into futex.c
  mtd: spi-nor: cast to u64 to avoid uint overflows
  mtd: rawnand: atmel: fix possible object reference leak
  mtd: rawnand: atmel: Fix spelling mistake in error message
  net: macb driver, check for SKBTX_HW_TSTAMP
  net: macb: Fix SUBNS increment and increase resolution
  watchdog: sama5d4: fix WDD value to be always set to max
  ext4: add more paranoia checking in ext4_expand_extra_isize handling
  net: sched: fix `tc -s class show` no bstats on class with nolock subqueues
  sctp: cache netns in sctp_ep_common
  tipc: fix link name length check
  openvswitch: remove another BUG_ON()
  openvswitch: drop unneeded BUG_ON() in ovs_flow_cmd_build_info()
  slip: Fix use-after-free Read in slip_open
  openvswitch: fix flow command message size
  net: psample: fix skb_over_panic
  macvlan: schedule bc_work even if error
  media: atmel: atmel-isc: fix INIT_WORK misplacement
  media: atmel: atmel-isc: fix asd memory allocation
  pwm: Clear chip_data in pwm_put()
  net: macb: fix error format in dev_err()
  media: v4l2-ctrl: fix flags for DO_WHITE_BALANCE
  xfrm: Fix memleak on xfrm state destroy
  mei: bus: prefix device names on bus with the bus name
  USB: serial: ftdi_sio: add device IDs for U-Blox C099-F9P
  staging: rtl8723bs: Add 024c:0525 to the list of SDIO device-ids
  staging: rtl8723bs: Drop ACPI device ids
  staging: rtl8192e: fix potential use after free
  clk: at91: generated: set audio_pll_allowed in at91_clk_register_generated()
  clk: at91: fix update bit maps on CFG_MOR write
  mm, gup: add missing refcount overflow checks on s390
  mtd: Remove a debug trace in mtdpart.c
  powerpc/pseries/dlpar: Fix a missing check in dlpar_parse_cc_property()
  scsi: libsas: Check SMP PHY control function result
  ACPI / APEI: Switch estatus pool to use vmalloc memory
  ACPI / APEI: Don't wait to serialise with oops messages when panic()ing
  scsi: libsas: Support SATA PHY connection rate unmatch fixing during discovery
  apparmor: delete the dentry in aafs_remove() to avoid a leak
  iommu/amd: Fix NULL dereference bug in match_hid_uid
  net: hns3: Change fw error code NOT_EXEC to NOT_SUPPORTED
  bpf: drop refcount if bpf_map_new_fd() fails in map_create()
  kvm: properly check debugfs dentry before using it
  net: dev: Use unsigned integer as an argument to left-shift
  bpf: decrease usercnt if bpf_map_new_fd() fails in bpf_map_get_fd_by_id()
  sctp: don't compare hb_timer expire date before starting it
  net: fix possible overflow in __sk_mem_raise_allocated()
  sfc: initialise found bitmap in efx_ef10_mtd_probe
  tipc: fix skb may be leaky in tipc_link_input
  blktrace: Show requests without sector
  net/smc: prevent races between smc_lgr_terminate() and smc_conn_free()
  decnet: fix DN_IFREQ_SIZE
  ip_tunnel: Make none-tunnel-dst tunnel port work with lwtunnel
  sfc: suppress duplicate nvmem partition types in efx_ef10_mtd_probe
  gpu: ipu-v3: pre: don't trigger update if buffer address doesn't change
  serial: 8250: Fix serial8250 initialization crash
  net/core/neighbour: fix kmemleak minimal reference count for hash tables
  PCI/MSI: Return -ENOSPC from pci_alloc_irq_vectors_affinity()
  net/core/neighbour: tell kmemleak about hash tables
  tipc: fix memory leak in tipc_nl_compat_publ_dump
  mtd: Check add_mtd_device() ret code
  lib/genalloc.c: include vmalloc.h
  drivers/base/platform.c: kmemleak ignore a known leak
  fork: fix some -Wmissing-prototypes warnings
  lib/genalloc.c: use vzalloc_node() to allocate the bitmap
  lib/genalloc.c: fix allocation of aligned buffer from non-aligned chunk
  vmscan: return NODE_RECLAIM_NOSCAN in node_reclaim() when CONFIG_NUMA is n
  ocfs2: clear journal dirty flag after shutdown journal
  net/wan/fsl_ucc_hdlc: Avoid double free in ucc_hdlc_probe()
  tipc: fix a missing check of genlmsg_put
  atl1e: checking the status of atl1e_write_phy_reg
  net: dsa: bcm_sf2: Propagate error value from mdio_write
  net: stmicro: fix a missing check of clk_prepare
  net: (cpts) fix a missing check of clk_prepare
  um: Make GCOV depend on !KCOV
  f2fs: fix to dirty inode synchronously
  net/net_namespace: Check the return value of register_pernet_subsys()
  net/netlink_compat: Fix a missing check of nla_parse_nested
  pwm: clps711x: Fix period calculation
  crypto: mxc-scc - fix build warnings on ARM64
  powerpc/pseries: Fix node leak in update_lmb_associativity_index()
  powerpc/83xx: handle machine check caused by watchdog timer
  regulator: tps65910: fix a missing check of return value
  IB/rxe: Make counters thread safe
  drbd: fix print_st_err()'s prototype to match the definition
  drbd: do not block when adjusting "disk-options" while IO is frozen
  drbd: reject attach of unsuitable uuids even if connected
  drbd: ignore "all zero" peer volume sizes in handshake
  powerpc/powernv/eeh/npu: Fix uninitialized variables in opal_pci_eeh_freeze_status
  vfio/spapr_tce: Get rid of possible infinite loop
  powerpc/44x/bamboo: Fix PCI range
  powerpc/mm: Make NULL pointer deferences explicit on bad page faults.
  powerpc/prom: fix early DEBUG messages
  powerpc/perf: Fix unit_sel/cache_sel checks
  ath6kl: Fix off by one error in scan completion
  ath6kl: Only use match sets when firmware supports it
  scsi: csiostor: fix incorrect dma device in case of vport
  scsi: qla2xxx: deadlock by configfs_depend_item
  RDMA/srp: Propagate ib_post_send() failures to the SCSI mid-layer
  openrisc: Fix broken paths to arch/or32
  serial: max310x: Fix tx_empty() callback
  Bluetooth: hci_bcm: Handle specific unknown packets after firmware loading
  drivers/regulator: fix a missing check of return value
  powerpc/xmon: fix dump_segments()
  powerpc/book3s/32: fix number of bats in p/v_block_mapped()
  vxlan: Fix error path in __vxlan_dev_create()
  clocksource/drivers/fttmr010: Fix invalid interrupt register access
  IB/qib: Fix an error code in qib_sdma_verbs_send()
  xfs: Fix bulkstat compat ioctls on x32 userspace.
  xfs: Align compat attrlist_by_handle with native implementation.
  gfs2: take jdata unstuff into account in do_grow
  dm flakey: Properly corrupt multi-page bios.
  HID: doc: fix wrong data structure reference for UHID_OUTPUT
  pinctrl: sh-pfc: sh7734: Fix shifted values in IPSR10
  pinctrl: sh-pfc: sh7264: Fix PFCR3 and PFCR0 register configuration
  KVM: s390: unregister debug feature on failing arch init
  bnxt_en: query force speeds before disabling autoneg mode.
  bnxt_en: Return linux standard errors in bnxt_ethtool.c
  exofs_mount(): fix leaks on failure exits
  net/mlx5: Continue driver initialization despite debugfs failure
  pinctrl: xway: fix gpio-hog related boot issues
  vfio-mdev/samples: Use u8 instead of char for handle functions
  xen/pciback: Check dev_data before using it
  kprobes/x86/xen: blacklist non-attachable xen interrupt functions
  serial: 8250: Rate limit serial port rx interrupts during input overruns
  HID: intel-ish-hid: fixes incorrect error handling
  btrfs: only track ref_heads in delayed_ref_updates
  mtd: rawnand: sunxi: Write pageprog related opcodes to WCMD_SET
  mmc: meson-gx: make sure the descriptor is stopped on errors
  VSOCK: bind to random port for VMADDR_PORT_ANY
  kvm: vmx: Set IA32_TSC_AUX for legacy mode guests
  gpiolib: Fix return value of gpio_to_desc() stub if !GPIOLIB
  iwlwifi: move iwl_nvm_check_version() into dvm
  microblaze: move "... is ready" messages to arch/microblaze/Makefile
  microblaze: adjust the help to the real behavior
  ubi: Do not drop UBI device reference before using
  ubi: Put MTD device after it is not used
  xfs: require both realtime inodes to mount
  rtl818x: fix potential use after free
  mwifiex: debugfs: correct histogram spacing, formatting
  mwifiex: fix potential NULL dereference and use after free
  crypto: user - support incremental algorithm dumps
  scsi: lpfc: Enable Management features for IF_TYPE=6
  ACPI / LPSS: Ignore acpi_device_fix_up_power() return value
  ARM: ks8695: fix section mismatch warning
  PM / AVS: SmartReflex: NULL check before some freeing functions is not needed
  RDMA/vmw_pvrdma: Use atomic memory allocation in create AH
  ARM: OMAP1: fix USB configuration for device-only setups
  arm64: smp: Handle errors reported by the firmware
  arm64: mm: Prevent mismatched 52-bit VA support
  parisc: Fix HP SDC hpa address output
  parisc: Fix serio address output
  ARM: dts: imx53-voipac-dmm-668: Fix memory node duplication
  ARM: debug-imx: only define DEBUG_IMX_UART_PORT if needed
  ARM: dts: Fix up SQ201 flash access
  scsi: lpfc: Fix dif and first burst use in write commands
  scsi: lpfc: Fix kernel Oops due to null pring pointers
  pwm: bcm-iproc: Prevent unloading the driver module while in use
  block: drbd: remove a stray unlock in __drbd_send_protocol()
  mac80211: fix station inactive_time shortly after boot
  ceph: return -EINVAL if given fsc mount option on kernel w/o support
  net: bcmgenet: reapply manual settings to the PHY
  scripts/gdb: fix debugging modules compiled with hot/cold partitioning
  watchdog: meson: Fix the wrong value of left time
  can: rx-offload: can_rx_offload_irq_offload_fifo(): continue on error
  can: rx-offload: can_rx_offload_irq_offload_timestamp(): continue on error
  can: rx-offload: can_rx_offload_offload_one(): use ERR_PTR() to propagate error value in case of errors
  can: rx-offload: can_rx_offload_offload_one(): increment rx_fifo_errors on queue overflow or OOM
  can: rx-offload: can_rx_offload_offload_one(): do not increase the skb_queue beyond skb_queue_len_max
  can: rx-offload: can_rx_offload_queue_tail(): fix error handling, avoid skb mem leak
  can: c_can: D_CAN: c_can_chip_config(): perform a sofware reset on open
  can: peak_usb: report bus recovery as well
  bridge: ebtables: don't crash when using dnat target in output chains
  net: fec: add missed clk_disable_unprepare in remove
  clk: ti: dra7-atl-clock: Remove ti_clk_add_alias call
  x86/resctrl: Prevent NULL pointer dereference when reading mondata
  idr: Fix idr_alloc_u32 on 32-bit systems
  clk: sunxi-ng: a80: fix the zero'ing of bits 16 and 18
  clk: at91: avoid sleeping early
  reset: fix reset_control_ops kerneldoc comment
  clk: samsung: exynos5420: Preserve PLL configuration during suspend/resume
  ASoC: kirkwood: fix external clock probe defer
  reset: Fix memory leak in reset_control_array_put()
  ASoC: compress: fix unsigned integer overflow check
  ASoC: msm8916-wcd-analog: Fix RX1 selection in RDAC2 MUX
  clk: meson: gxbb: let sar_adc_clk_div set the parent clock rate
  Revert "KVM: nVMX: reset cache/shadows when switching loaded VMCS"
  UPSTREAM: dt-bindings: arm: coresight: Add support for coresight-loses-context-with-cpu
  BACKPORT: coresight: etm4x: Save/restore state across CPU low power states
  BACKPORT: ARM: 8900/1: UNWINDER_FRAME_POINTER implementation for Clang

Conflicts:
	Documentation/devicetree/bindings/arm/coresight.txt
	arch/arm/Makefile
	drivers/hid/hid-core.c
	kernel/exit.c

Reverted the downstream patch "HID: core: add usage_page_preceding flag for hid_concatenate_usage_page()"
as original issue got fixed with upstream changes.

Change-Id: I3b833825b3d1104fa07378caef144639074d0a0d
Signed-off-by: Srinivasarao P <spathi@codeaurora.org>
2020-04-16 16:59:09 +05:30

1039 lines
28 KiB
C

/*
* Simple MTD partitioning layer
*
* Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
* Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
* Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/err.h>
#include "mtdcore.h"
/* Our partition linked list */
static LIST_HEAD(mtd_partitions);
static DEFINE_MUTEX(mtd_partitions_mutex);
/**
* struct mtd_part - our partition node structure
*
* @mtd: struct holding partition details
* @parent: parent mtd - flash device or another partition
* @offset: partition offset relative to the *flash device*
*/
struct mtd_part {
struct mtd_info mtd;
struct mtd_info *parent;
uint64_t offset;
struct list_head list;
};
/*
* Given a pointer to the MTD object in the mtd_part structure, we can retrieve
* the pointer to that structure with this macro.
*/
#define PART(x) ((struct mtd_part *)(x))
/*
* Given a pointer to the MTD object in the mtd_part structure, we can retrieve
* the pointer to that structure.
*/
static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd)
{
return container_of(mtd, struct mtd_part, mtd);
}
/*
* MTD methods which simply translate the effective address and pass through
* to the _real_ device.
*/
static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
struct mtd_ecc_stats stats;
int res;
stats = part->parent->ecc_stats;
res = part->parent->_read(part->parent, from + part->offset, len,
retlen, buf);
if (unlikely(mtd_is_eccerr(res)))
mtd->ecc_stats.failed +=
part->parent->ecc_stats.failed - stats.failed;
else
mtd->ecc_stats.corrected +=
part->parent->ecc_stats.corrected - stats.corrected;
return res;
}
static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_point(part->parent, from + part->offset, len,
retlen, virt, phys);
}
static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_unpoint(part->parent, from + part->offset, len);
}
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
unsigned long len,
unsigned long offset,
unsigned long flags)
{
struct mtd_part *part = mtd_to_part(mtd);
offset += part->offset;
return part->parent->_get_unmapped_area(part->parent, len, offset,
flags);
}
static int part_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
struct mtd_part *part = mtd_to_part(mtd);
int res;
if (from >= mtd->size)
return -EINVAL;
if (ops->datbuf && from + ops->len > mtd->size)
return -EINVAL;
/*
* If OOB is also requested, make sure that we do not read past the end
* of this partition.
*/
if (ops->oobbuf) {
size_t len, pages;
len = mtd_oobavail(mtd, ops);
pages = mtd_div_by_ws(mtd->size, mtd);
pages -= mtd_div_by_ws(from, mtd);
if (ops->ooboffs + ops->ooblen > pages * len)
return -EINVAL;
}
res = part->parent->_read_oob(part->parent, from + part->offset, ops);
if (unlikely(res)) {
if (mtd_is_bitflip(res))
mtd->ecc_stats.corrected++;
if (mtd_is_eccerr(res))
mtd->ecc_stats.failed++;
}
return res;
}
static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_read_user_prot_reg(part->parent, from, len,
retlen, buf);
}
static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_get_user_prot_info(part->parent, len, retlen,
buf);
}
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_read_fact_prot_reg(part->parent, from, len,
retlen, buf);
}
static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
size_t *retlen, struct otp_info *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_get_fact_prot_info(part->parent, len, retlen,
buf);
}
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_write(part->parent, to + part->offset, len,
retlen, buf);
}
static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_panic_write(part->parent, to + part->offset, len,
retlen, buf);
}
static int part_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
struct mtd_part *part = mtd_to_part(mtd);
if (to >= mtd->size)
return -EINVAL;
if (ops->datbuf && to + ops->len > mtd->size)
return -EINVAL;
return part->parent->_write_oob(part->parent, to + part->offset, ops);
}
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len, size_t *retlen, u_char *buf)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_write_user_prot_reg(part->parent, from, len,
retlen, buf);
}
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_lock_user_prot_reg(part->parent, from, len);
}
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t *retlen)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_writev(part->parent, vecs, count,
to + part->offset, retlen);
}
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct mtd_part *part = mtd_to_part(mtd);
int ret;
instr->addr += part->offset;
ret = part->parent->_erase(part->parent, instr);
if (ret) {
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= part->offset;
instr->addr -= part->offset;
}
return ret;
}
void mtd_erase_callback(struct erase_info *instr)
{
if (instr->mtd->_erase == part_erase) {
struct mtd_part *part = mtd_to_part(instr->mtd);
if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
instr->fail_addr -= part->offset;
instr->addr -= part->offset;
}
if (instr->callback)
instr->callback(instr);
}
EXPORT_SYMBOL_GPL(mtd_erase_callback);
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_lock(part->parent, ofs + part->offset, len);
}
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_unlock(part->parent, ofs + part->offset, len);
}
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_is_locked(part->parent, ofs + part->offset, len);
}
static void part_sync(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
part->parent->_sync(part->parent);
}
static int part_suspend(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_suspend(part->parent);
}
static void part_resume(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
part->parent->_resume(part->parent);
}
static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = mtd_to_part(mtd);
ofs += part->offset;
return part->parent->_block_isreserved(part->parent, ofs);
}
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = mtd_to_part(mtd);
ofs += part->offset;
return part->parent->_block_isbad(part->parent, ofs);
}
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = mtd_to_part(mtd);
int res;
ofs += part->offset;
res = part->parent->_block_markbad(part->parent, ofs);
if (!res)
mtd->ecc_stats.badblocks++;
return res;
}
static int part_get_device(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_get_device(part->parent);
}
static void part_put_device(struct mtd_info *mtd)
{
struct mtd_part *part = mtd_to_part(mtd);
part->parent->_put_device(part->parent);
}
static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct mtd_part *part = mtd_to_part(mtd);
return mtd_ooblayout_ecc(part->parent, section, oobregion);
}
static int part_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *oobregion)
{
struct mtd_part *part = mtd_to_part(mtd);
return mtd_ooblayout_free(part->parent, section, oobregion);
}
static const struct mtd_ooblayout_ops part_ooblayout_ops = {
.ecc = part_ooblayout_ecc,
.free = part_ooblayout_free,
};
static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
{
struct mtd_part *part = mtd_to_part(mtd);
return part->parent->_max_bad_blocks(part->parent,
ofs + part->offset, len);
}
static inline void free_partition(struct mtd_part *p)
{
kfree(p->mtd.name);
kfree(p);
}
void part_fill_badblockstats(struct mtd_info *mtd)
{
uint64_t offs = 0;
struct mtd_info *parent;
struct mtd_part *part;
part = PART(mtd);
parent = part->parent;
if (parent->_block_isbad) {
mtd->ecc_stats.badblocks = 0;
mtd->ecc_stats.bbtblocks = 0;
while (offs < mtd->size) {
if (mtd_block_isreserved(parent, offs + part->offset))
mtd->ecc_stats.bbtblocks++;
else if (mtd_block_isbad(parent, offs + part->offset))
mtd->ecc_stats.badblocks++;
offs += mtd->erasesize;
}
}
}
/**
* mtd_parse_part - parse MTD partition looking for subpartitions
*
* @slave: part that is supposed to be a container and should be parsed
* @types: NULL-terminated array with names of partition parsers to try
*
* Some partitions are kind of containers with extra subpartitions (volumes).
* There can be various formats of such containers. This function tries to use
* specified parsers to analyze given partition and registers found
* subpartitions on success.
*/
static int mtd_parse_part(struct mtd_part *slave, const char *const *types)
{
struct mtd_partitions parsed;
int err;
err = parse_mtd_partitions(&slave->mtd, types, &parsed, NULL);
if (err)
return err;
else if (!parsed.nr_parts)
return -ENOENT;
err = add_mtd_partitions(&slave->mtd, parsed.parts, parsed.nr_parts);
mtd_part_parser_cleanup(&parsed);
return err;
}
static struct mtd_part *allocate_partition(struct mtd_info *parent,
const struct mtd_partition *part, int partno,
uint64_t cur_offset)
{
int wr_alignment = (parent->flags & MTD_NO_ERASE) ? parent->writesize :
parent->erasesize;
struct mtd_part *slave;
u32 remainder;
char *name;
u64 tmp;
/* allocate the partition structure */
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
name = kstrdup(part->name, GFP_KERNEL);
if (!name || !slave) {
printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
parent->name);
kfree(name);
kfree(slave);
return ERR_PTR(-ENOMEM);
}
/* set up the MTD object for this partition */
slave->mtd.type = parent->type;
slave->mtd.flags = parent->flags & ~part->mask_flags;
slave->mtd.size = part->size;
slave->mtd.writesize = parent->writesize;
slave->mtd.writebufsize = parent->writebufsize;
slave->mtd.oobsize = parent->oobsize;
slave->mtd.oobavail = parent->oobavail;
slave->mtd.subpage_sft = parent->subpage_sft;
slave->mtd.pairing = parent->pairing;
slave->mtd.name = name;
slave->mtd.owner = parent->owner;
/* NOTE: Historically, we didn't arrange MTDs as a tree out of
* concern for showing the same data in multiple partitions.
* However, it is very useful to have the master node present,
* so the MTD_PARTITIONED_MASTER option allows that. The master
* will have device nodes etc only if this is set, so make the
* parent conditional on that option. Note, this is a way to
* distinguish between the master and the partition in sysfs.
*/
slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ?
&parent->dev :
parent->dev.parent;
slave->mtd.dev.of_node = part->of_node;
slave->mtd._read = part_read;
slave->mtd._write = part_write;
if (parent->_panic_write)
slave->mtd._panic_write = part_panic_write;
if (parent->_point && parent->_unpoint) {
slave->mtd._point = part_point;
slave->mtd._unpoint = part_unpoint;
}
if (parent->_get_unmapped_area)
slave->mtd._get_unmapped_area = part_get_unmapped_area;
if (parent->_read_oob)
slave->mtd._read_oob = part_read_oob;
if (parent->_write_oob)
slave->mtd._write_oob = part_write_oob;
if (parent->_read_user_prot_reg)
slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
if (parent->_read_fact_prot_reg)
slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
if (parent->_write_user_prot_reg)
slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
if (parent->_lock_user_prot_reg)
slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
if (parent->_get_user_prot_info)
slave->mtd._get_user_prot_info = part_get_user_prot_info;
if (parent->_get_fact_prot_info)
slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
if (parent->_sync)
slave->mtd._sync = part_sync;
if (!partno && !parent->dev.class && parent->_suspend &&
parent->_resume) {
slave->mtd._suspend = part_suspend;
slave->mtd._resume = part_resume;
}
if (parent->_writev)
slave->mtd._writev = part_writev;
if (parent->_lock)
slave->mtd._lock = part_lock;
if (parent->_unlock)
slave->mtd._unlock = part_unlock;
if (parent->_is_locked)
slave->mtd._is_locked = part_is_locked;
if (parent->_block_isreserved)
slave->mtd._block_isreserved = part_block_isreserved;
if (parent->_block_isbad)
slave->mtd._block_isbad = part_block_isbad;
if (parent->_block_markbad)
slave->mtd._block_markbad = part_block_markbad;
if (parent->_max_bad_blocks)
slave->mtd._max_bad_blocks = part_max_bad_blocks;
if (parent->_get_device)
slave->mtd._get_device = part_get_device;
if (parent->_put_device)
slave->mtd._put_device = part_put_device;
slave->mtd._erase = part_erase;
slave->parent = parent;
slave->offset = part->offset;
if (slave->offset == MTDPART_OFS_APPEND)
slave->offset = cur_offset;
if (slave->offset == MTDPART_OFS_NXTBLK) {
tmp = cur_offset;
slave->offset = cur_offset;
remainder = do_div(tmp, wr_alignment);
if (remainder) {
slave->offset += wr_alignment - remainder;
printk(KERN_NOTICE "Moving partition %d: "
"0x%012llx -> 0x%012llx\n", partno,
(unsigned long long)cur_offset, (unsigned long long)slave->offset);
}
}
if (slave->offset == MTDPART_OFS_RETAIN) {
slave->offset = cur_offset;
if (parent->size - slave->offset >= slave->mtd.size) {
slave->mtd.size = parent->size - slave->offset
- slave->mtd.size;
} else {
printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
part->name, parent->size - slave->offset,
slave->mtd.size);
/* register to preserve ordering */
goto out_register;
}
}
if (slave->mtd.size == MTDPART_SIZ_FULL)
slave->mtd.size = parent->size - slave->offset;
printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
/* let's do some sanity checks */
if (slave->offset >= parent->size) {
/* let's register it anyway to preserve ordering */
slave->offset = 0;
slave->mtd.size = 0;
printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
part->name);
goto out_register;
}
if (slave->offset + slave->mtd.size > parent->size) {
slave->mtd.size = parent->size - slave->offset;
printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
part->name, parent->name, (unsigned long long)slave->mtd.size);
}
if (parent->numeraseregions > 1) {
/* Deal with variable erase size stuff */
int i, max = parent->numeraseregions;
u64 end = slave->offset + slave->mtd.size;
struct mtd_erase_region_info *regions = parent->eraseregions;
/* Find the first erase regions which is part of this
* partition. */
for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
;
/* The loop searched for the region _behind_ the first one */
if (i > 0)
i--;
/* Pick biggest erasesize */
for (; i < max && regions[i].offset < end; i++) {
if (slave->mtd.erasesize < regions[i].erasesize) {
slave->mtd.erasesize = regions[i].erasesize;
}
}
BUG_ON(slave->mtd.erasesize == 0);
} else {
/* Single erase size */
slave->mtd.erasesize = parent->erasesize;
}
/*
* Slave erasesize might differ from the master one if the master
* exposes several regions with different erasesize. Adjust
* wr_alignment accordingly.
*/
if (!(slave->mtd.flags & MTD_NO_ERASE))
wr_alignment = slave->mtd.erasesize;
tmp = slave->offset;
remainder = do_div(tmp, wr_alignment);
if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
/* Doesn't start on a boundary of major erase size */
/* FIXME: Let it be writable if it is on a boundary of
* _minor_ erase size though */
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
part->name);
}
tmp = slave->mtd.size;
remainder = do_div(tmp, wr_alignment);
if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
slave->mtd.flags &= ~MTD_WRITEABLE;
printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
part->name);
}
mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
slave->mtd.ecc_step_size = parent->ecc_step_size;
slave->mtd.ecc_strength = parent->ecc_strength;
slave->mtd.bitflip_threshold = parent->bitflip_threshold;
#ifndef CONFIG_MTD_LAZYECCSTATS
part_fill_badblockstats(&(slave->mtd));
#endif
out_register:
return slave;
}
static ssize_t mtd_partition_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mtd_info *mtd = dev_get_drvdata(dev);
struct mtd_part *part = mtd_to_part(mtd);
return snprintf(buf, PAGE_SIZE, "%lld\n", part->offset);
}
static DEVICE_ATTR(offset, S_IRUGO, mtd_partition_offset_show, NULL);
static const struct attribute *mtd_partition_attrs[] = {
&dev_attr_offset.attr,
NULL
};
static int mtd_add_partition_attrs(struct mtd_part *new)
{
int ret = sysfs_create_files(&new->mtd.dev.kobj, mtd_partition_attrs);
if (ret)
printk(KERN_WARNING
"mtd: failed to create partition attrs, err=%d\n", ret);
return ret;
}
int mtd_add_partition(struct mtd_info *parent, const char *name,
long long offset, long long length)
{
struct mtd_partition part;
struct mtd_part *new;
int ret = 0;
/* the direct offset is expected */
if (offset == MTDPART_OFS_APPEND ||
offset == MTDPART_OFS_NXTBLK)
return -EINVAL;
if (length == MTDPART_SIZ_FULL)
length = parent->size - offset;
if (length <= 0)
return -EINVAL;
memset(&part, 0, sizeof(part));
part.name = name;
part.size = length;
part.offset = offset;
new = allocate_partition(parent, &part, -1, offset);
if (IS_ERR(new))
return PTR_ERR(new);
mutex_lock(&mtd_partitions_mutex);
list_add(&new->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
ret = add_mtd_device(&new->mtd);
if (ret)
goto err_remove_part;
mtd_add_partition_attrs(new);
return 0;
err_remove_part:
mutex_lock(&mtd_partitions_mutex);
list_del(&new->list);
mutex_unlock(&mtd_partitions_mutex);
free_partition(new);
return ret;
}
EXPORT_SYMBOL_GPL(mtd_add_partition);
/**
* __mtd_del_partition - delete MTD partition
*
* @priv: internal MTD struct for partition to be deleted
*
* This function must be called with the partitions mutex locked.
*/
static int __mtd_del_partition(struct mtd_part *priv)
{
struct mtd_part *child, *next;
int err;
list_for_each_entry_safe(child, next, &mtd_partitions, list) {
if (child->parent == &priv->mtd) {
err = __mtd_del_partition(child);
if (err)
return err;
}
}
sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs);
err = del_mtd_device(&priv->mtd);
if (err)
return err;
list_del(&priv->list);
free_partition(priv);
return 0;
}
/*
* This function unregisters and destroy all slave MTD objects which are
* attached to the given MTD object.
*/
int del_mtd_partitions(struct mtd_info *mtd)
{
struct mtd_part *slave, *next;
int ret, err = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if (slave->parent == mtd) {
ret = __mtd_del_partition(slave);
if (ret < 0)
err = ret;
}
mutex_unlock(&mtd_partitions_mutex);
return err;
}
int mtd_del_partition(struct mtd_info *mtd, int partno)
{
struct mtd_part *slave, *next;
int ret = -EINVAL;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry_safe(slave, next, &mtd_partitions, list)
if ((slave->parent == mtd) &&
(slave->mtd.index == partno)) {
ret = __mtd_del_partition(slave);
break;
}
mutex_unlock(&mtd_partitions_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(mtd_del_partition);
/*
* This function, given a master MTD object and a partition table, creates
* and registers slave MTD objects which are bound to the master according to
* the partition definitions.
*
* For historical reasons, this function's caller only registers the master
* if the MTD_PARTITIONED_MASTER config option is set.
*/
int add_mtd_partitions(struct mtd_info *master,
const struct mtd_partition *parts,
int nbparts)
{
struct mtd_part *slave;
uint64_t cur_offset = 0;
int i, ret;
printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
for (i = 0; i < nbparts; i++) {
slave = allocate_partition(master, parts + i, i, cur_offset);
if (IS_ERR(slave)) {
ret = PTR_ERR(slave);
goto err_del_partitions;
}
mutex_lock(&mtd_partitions_mutex);
list_add(&slave->list, &mtd_partitions);
mutex_unlock(&mtd_partitions_mutex);
ret = add_mtd_device(&slave->mtd);
if (ret) {
mutex_lock(&mtd_partitions_mutex);
list_del(&slave->list);
mutex_unlock(&mtd_partitions_mutex);
free_partition(slave);
goto err_del_partitions;
}
mtd_add_partition_attrs(slave);
if (parts[i].types)
mtd_parse_part(slave, parts[i].types);
cur_offset = slave->offset + slave->mtd.size;
}
return 0;
err_del_partitions:
del_mtd_partitions(master);
return ret;
}
static DEFINE_SPINLOCK(part_parser_lock);
static LIST_HEAD(part_parsers);
static struct mtd_part_parser *mtd_part_parser_get(const char *name)
{
struct mtd_part_parser *p, *ret = NULL;
spin_lock(&part_parser_lock);
list_for_each_entry(p, &part_parsers, list)
if (!strcmp(p->name, name) && try_module_get(p->owner)) {
ret = p;
break;
}
spin_unlock(&part_parser_lock);
return ret;
}
static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
{
module_put(p->owner);
}
/*
* Many partition parsers just expected the core to kfree() all their data in
* one chunk. Do that by default.
*/
static void mtd_part_parser_cleanup_default(const struct mtd_partition *pparts,
int nr_parts)
{
kfree(pparts);
}
int __register_mtd_parser(struct mtd_part_parser *p, struct module *owner)
{
p->owner = owner;
if (!p->cleanup)
p->cleanup = &mtd_part_parser_cleanup_default;
spin_lock(&part_parser_lock);
list_add(&p->list, &part_parsers);
spin_unlock(&part_parser_lock);
return 0;
}
EXPORT_SYMBOL_GPL(__register_mtd_parser);
void deregister_mtd_parser(struct mtd_part_parser *p)
{
spin_lock(&part_parser_lock);
list_del(&p->list);
spin_unlock(&part_parser_lock);
}
EXPORT_SYMBOL_GPL(deregister_mtd_parser);
/*
* Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
* are changing this array!
*/
static const char * const default_mtd_part_types[] = {
"cmdlinepart",
"ofpart",
NULL
};
static int mtd_part_do_parse(struct mtd_part_parser *parser,
struct mtd_info *master,
struct mtd_partitions *pparts,
struct mtd_part_parser_data *data)
{
int ret;
ret = (*parser->parse_fn)(master, &pparts->parts, data);
pr_debug("%s: parser %s: %i\n", master->name, parser->name, ret);
if (ret <= 0)
return ret;
pr_notice("%d %s partitions found on MTD device %s\n", ret,
parser->name, master->name);
pparts->nr_parts = ret;
pparts->parser = parser;
return ret;
}
/**
* parse_mtd_partitions - parse MTD partitions
* @master: the master partition (describes whole MTD device)
* @types: names of partition parsers to try or %NULL
* @pparts: info about partitions found is returned here
* @data: MTD partition parser-specific data
*
* This function tries to find partition on MTD device @master. It uses MTD
* partition parsers, specified in @types. However, if @types is %NULL, then
* the default list of parsers is used. The default list contains only the
* "cmdlinepart" and "ofpart" parsers ATM.
* Note: If there are more then one parser in @types, the kernel only takes the
* partitions parsed out by the first parser.
*
* This function may return:
* o a negative error code in case of failure
* o zero otherwise, and @pparts will describe the partitions, number of
* partitions, and the parser which parsed them. Caller must release
* resources with mtd_part_parser_cleanup() when finished with the returned
* data.
*/
int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
struct mtd_partitions *pparts,
struct mtd_part_parser_data *data)
{
struct mtd_part_parser *parser;
int ret, err = 0;
if (!types)
types = default_mtd_part_types;
for ( ; *types; types++) {
pr_debug("%s: parsing partitions %s\n", master->name, *types);
parser = mtd_part_parser_get(*types);
if (!parser && !request_module("%s", *types))
parser = mtd_part_parser_get(*types);
pr_debug("%s: got parser %s\n", master->name,
parser ? parser->name : NULL);
if (!parser)
continue;
ret = mtd_part_do_parse(parser, master, pparts, data);
/* Found partitions! */
if (ret > 0)
return 0;
mtd_part_parser_put(parser);
/*
* Stash the first error we see; only report it if no parser
* succeeds
*/
if (ret < 0 && !err)
err = ret;
}
return err;
}
void mtd_part_parser_cleanup(struct mtd_partitions *parts)
{
const struct mtd_part_parser *parser;
if (!parts)
return;
parser = parts->parser;
if (parser) {
if (parser->cleanup)
parser->cleanup(parts->parts, parts->nr_parts);
mtd_part_parser_put(parser);
}
}
int mtd_is_partition(const struct mtd_info *mtd)
{
struct mtd_part *part;
int ispart = 0;
mutex_lock(&mtd_partitions_mutex);
list_for_each_entry(part, &mtd_partitions, list)
if (&part->mtd == mtd) {
ispart = 1;
break;
}
mutex_unlock(&mtd_partitions_mutex);
return ispart;
}
EXPORT_SYMBOL_GPL(mtd_is_partition);
/* Returns the size of the entire flash chip */
uint64_t mtd_get_device_size(const struct mtd_info *mtd)
{
if (!mtd_is_partition(mtd))
return mtd->size;
return mtd_get_device_size(mtd_to_part(mtd)->parent);
}
EXPORT_SYMBOL_GPL(mtd_get_device_size);